blob: 63392cbe315d91cdf819c6fe3f862a8c6065fce6 [file] [log] [blame]
Simon Kelley0a852542005-03-23 20:28:59 +00001/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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 Kelleyfd9fa482004-10-21 20:24:00 +010021 int flags, oneopt = 1, zeroopt = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010022 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 Kelleyfd9fa482004-10-21 20:24:00 +010027 if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
28 fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000029#if defined(IP_PKTINFO)
Simon Kelley3be34542004-09-11 19:12:13 +010030 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000031#elif defined(IP_RECVIF)
Simon Kelley3be34542004-09-11 19:12:13 +010032 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000033#endif
Simon Kelley3be34542004-09-11 19:12:13 +010034 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +000035 die("failed to set options on DHCP socket: %s", NULL);
36
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000037 /* When bind-interfaces is set, there might be more than one dnmsasq
38 instance binding port 67. That's Ok if they serve different networks.
39 Need to set REUSEADDR to make this posible. */
40 if ((daemon->options & OPT_NOWILD) &&
41 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1)
42 die("failed to set SO_REUSEADDR on DHCP socket: %s", NULL);
43
Simon Kelley44a2a312004-03-10 20:04:35 +000044 saddr.sin_family = AF_INET;
45 saddr.sin_port = htons(DHCP_SERVER_PORT);
46 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010047#ifdef HAVE_SOCKADDR_SA_LEN
48 saddr.sin_len = sizeof(struct sockaddr_in);
49#endif
50
Simon Kelley44a2a312004-03-10 20:04:35 +000051 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
52 die("failed to bind DHCP server socket: %s", NULL);
53
Simon Kelley3be34542004-09-11 19:12:13 +010054 daemon->dhcpfd = fd;
55
56 if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
Simon Kelley26128d22004-11-14 16:43:54 +000057 (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
58 fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
Simon Kelley3be34542004-09-11 19:12:13 +010059 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
60 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
61 die("cannot create ICMP raw socket: %s.", NULL);
62
63 daemon->dhcp_icmp_fd = fd;
Simon Kelley44a2a312004-03-10 20:04:35 +000064
65#ifdef HAVE_BPF
Simon Kelley3be34542004-09-11 19:12:13 +010066 {
67 int i = 0;
68 while (1)
69 {
70 char filename[50];
71 sprintf(filename, "/dev/bpf%d", i++);
72 if ((fd = open(filename, O_RDWR, 0)) != -1)
73 break;
74 if (errno != EBUSY)
75 die("cannot create DHCP BPF socket: %s", NULL);
76 }
77 }
Simon Kelley44a2a312004-03-10 20:04:35 +000078#else
Simon Kelley3be34542004-09-11 19:12:13 +010079 /* since we don't ever use the packet socket for reception,
80 and it receives copies of _all_ IP packets, then that data
81 will build up in kernel buffers, wasting memory. Set the
82 socket receive buffer size to one to avoid that. (zero is
83 rejected as non-sensical by some BSD kernels) */
84 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
85 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
86 die("cannot create DHCP packet socket: %s. "
87 "Is CONFIG_PACKET enabled in your kernel?", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000088#endif
89
Simon Kelley3be34542004-09-11 19:12:13 +010090 daemon->dhcp_raw_fd = fd;
91
Simon Kelleydfa666f2004-08-02 18:27:27 +010092 /* If the same IP appears in more than one host config, then DISCOVER
93 for one of the hosts will get the address, but REQUEST will be NAKed,
94 since the address is reserved by the other one -> protocol loop. */
Simon Kelley3be34542004-09-11 19:12:13 +010095 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelleydfa666f2004-08-02 18:27:27 +010096 for (cp = configs->next; cp; cp = cp->next)
97 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
Simon Kelley3be34542004-09-11 19:12:13 +010098 die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
99
100 daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
Simon Kelley0a852542005-03-23 20:28:59 +0000101 /* These two each hold a DHCP option max size 255
Simon Kelley3be34542004-09-11 19:12:13 +0100102 and get a terminating zero added */
Simon Kelley0a852542005-03-23 20:28:59 +0000103 daemon->dhcp_buff = safe_malloc(256);
104 daemon->dhcp_buff2 = safe_malloc(256);
Simon Kelley3be34542004-09-11 19:12:13 +0100105
Simon Kelley44a2a312004-03-10 20:04:35 +0000106}
107
Simon Kelley3be34542004-09-11 19:12:13 +0100108void dhcp_packet(struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000109{
Simon Kelley3be34542004-09-11 19:12:13 +0100110 struct udp_dhcp_packet *rawpacket = daemon->dhcp_packet;
111 struct dhcp_packet *mess = &rawpacket->data;
Simon Kelley44a2a312004-03-10 20:04:35 +0000112 struct dhcp_context *context;
113 struct iname *tmp;
114 struct ifreq ifr;
115 struct msghdr msg;
116 struct iovec iov[2];
117 struct cmsghdr *cmptr;
118 int sz, newlen, iface_index = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000119 struct in_addr iface_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000120#ifdef HAVE_BPF
121 unsigned char iface_hwaddr[ETHER_ADDR_LEN];
122#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000123
Simon Kelley44a2a312004-03-10 20:04:35 +0000124 union {
125 struct cmsghdr align; /* this ensures alignment */
126#ifdef IP_PKTINFO
127 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
128#else
129 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
130#endif
131 } control_u;
132
Simon Kelley3be34542004-09-11 19:12:13 +0100133 iov[0].iov_base = (char *)mess;
134 iov[0].iov_len = sizeof(struct dhcp_packet);
Simon Kelley44a2a312004-03-10 20:04:35 +0000135
136 msg.msg_control = control_u.control;
137 msg.msg_controllen = sizeof(control_u);
138 msg.msg_flags = 0;
139 msg.msg_name = NULL;
140 msg.msg_namelen = 0;
141 msg.msg_iov = iov;
142 msg.msg_iovlen = 1;
143
Simon Kelley3be34542004-09-11 19:12:13 +0100144 sz = recvmsg(daemon->dhcpfd, &msg, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000145
146 if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
147 return;
148
149#if defined (IP_PKTINFO)
150 if (msg.msg_controllen < sizeof(struct cmsghdr))
151 return;
152 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
153 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
154 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
155
Simon Kelley8a911cc2004-03-16 18:35:52 +0000156 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100157 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000158 return;
159
160#elif defined(IP_RECVIF)
161 if (msg.msg_controllen < sizeof(struct cmsghdr))
162 return;
163 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
164 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
165 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
166
167 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
168 return;
169
170#else
Simon Kelley3be34542004-09-11 19:12:13 +0100171 {
172 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100173 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100174 strcpy(ifr.ifr_name, name->name);
175 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000176#endif
177
178#ifdef HAVE_BPF
Simon Kelley44a2a312004-03-10 20:04:35 +0000179 ifr.ifr_addr.sa_family = AF_LINK;
Simon Kelley3be34542004-09-11 19:12:13 +0100180 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000181 return;
182 memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
183#endif
184
185 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100186 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
Simon Kelley44a2a312004-03-10 20:04:35 +0000187 return;
188 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
189
190 /* enforce available interface configuration */
Simon Kelley3be34542004-09-11 19:12:13 +0100191 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000192 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
193 return;
194
Simon Kelley3be34542004-09-11 19:12:13 +0100195 if (daemon->if_names || daemon->if_addrs)
Simon Kelley44a2a312004-03-10 20:04:35 +0000196 {
Simon Kelley3be34542004-09-11 19:12:13 +0100197 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000198 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
199 break;
200 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100201 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000202 if (tmp->addr.sa.sa_family == AF_INET &&
203 tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
204 break;
205 if (!tmp)
206 return;
207 }
208
Simon Kelley91dccd02005-03-31 17:48:32 +0100209 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100210 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100211 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000212
Simon Kelley0a852542005-03-23 20:28:59 +0000213#ifdef HAVE_RTNETLINK
214 if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
215#endif
216 {
217 struct in_addr iface_netmask, iface_broadcast;
218
219 if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) < 0)
220 return;
221 iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
222
223 if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) < 0)
224 return;
225 iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
226
227 context = complete_context(daemon, iface_addr, NULL, iface_netmask,
228 iface_broadcast, mess->giaddr, iface_addr);
229 }
230
Simon Kelley44a2a312004-03-10 20:04:35 +0000231 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley0a852542005-03-23 20:28:59 +0000232 newlen = dhcp_reply(daemon, context, ifr.ifr_name, sz, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000233 lease_update_file(0, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100234 lease_update_dns(daemon);
Simon Kelley44a2a312004-03-10 20:04:35 +0000235
236 if (newlen == 0)
237 return;
238
239 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
240 {
Simon Kelley3be34542004-09-11 19:12:13 +0100241 /* To send to BOOTP relay or configured client, use the IP packet */
Simon Kelley44a2a312004-03-10 20:04:35 +0000242
243 struct sockaddr_in dest;
244 dest.sin_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100245#ifdef HAVE_SOCKADDR_SA_LEN
246 dest.sin_len = sizeof(struct sockaddr_in);
247#endif
248
Simon Kelley44a2a312004-03-10 20:04:35 +0000249 if (mess->giaddr.s_addr)
250 {
251 dest.sin_port = htons(DHCP_SERVER_PORT);
252 dest.sin_addr = mess->giaddr;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000253 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000254 else
255 {
256 dest.sin_port = htons(DHCP_CLIENT_PORT);
257 dest.sin_addr = mess->ciaddr;
258 }
259
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100260 while(sendto(daemon->dhcpfd, mess, newlen, 0,
261 (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
262 retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000263 }
264 else
265 {
266 /* Hairy stuff, packet either has to go to the
267 net broadcast or the destination can't reply to ARP yet,
268 but we do know the physical address.
269 Build the packet by steam, and send directly, bypassing
270 the kernel IP stack */
271
272 u32 i, sum;
273 unsigned char hwdest[ETHER_ADDR_LEN];
274
275 if (ntohs(mess->flags) & 0x8000)
276 {
277 memset(hwdest, 255, ETHER_ADDR_LEN);
278 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
279 }
280 else
281 {
282 memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
283 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
284 }
285
286 rawpacket->ip.ip_p = IPPROTO_UDP;
287 rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
288 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
289 sizeof(struct udphdr) +
290 newlen) ;
291 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
292 rawpacket->ip.ip_v = IPVERSION;
293 rawpacket->ip.ip_tos = 0;
294 rawpacket->ip.ip_id = htons(0);
295 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
296 rawpacket->ip.ip_ttl = IPDEFTTL;
297 rawpacket->ip.ip_sum = 0;
298 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
299 sum += ((u16 *)&rawpacket->ip)[i];
300 while (sum>>16)
301 sum = (sum & 0xffff) + (sum >> 16);
302 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
303
304 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
305 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
306 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
307 rawpacket->udp.uh_sum = 0;
308 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
309 sum += htons(IPPROTO_UDP);
310 for (i = 0; i < 4; i++)
311 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
312 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
313 sum += ((u16 *)&rawpacket->udp)[i];
314 while (sum>>16)
315 sum = (sum & 0xffff) + (sum >> 16);
316 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
317
318 {
319#ifdef HAVE_BPF
320 struct ether_header header;
321
322 header.ether_type = htons(ETHERTYPE_IP);
323 memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
324 memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
325
Simon Kelley3be34542004-09-11 19:12:13 +0100326 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr);
Simon Kelley44a2a312004-03-10 20:04:35 +0000327
328 iov[0].iov_base = (char *)&header;
329 iov[0].iov_len = sizeof(struct ether_header);
330 iov[1].iov_base = (char *)rawpacket;
331 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
Simon Kelley26128d22004-11-14 16:43:54 +0000332 while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000333#else
334 struct sockaddr_ll dest;
335
336 dest.sll_family = AF_PACKET;
337 dest.sll_halen = ETHER_ADDR_LEN;
338 dest.sll_ifindex = iface_index;
339 dest.sll_protocol = htons(ETHERTYPE_IP);
340 memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100341 while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
342 0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
Simon Kelley26128d22004-11-14 16:43:54 +0000343 retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000344#endif
345 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000346 }
347}
348
Simon Kelley0a852542005-03-23 20:28:59 +0000349/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
350 of the interface on which a DHCP packet arrives (and any relay address) and does the
351 following things:
352 1) Fills in any netmask and broadcast addresses which have not been explicitly configured.
353 2) Fills in local (this host) and router (this host or relay) addresses.
354 3) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
355 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
356struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, struct dhcp_context *current,
357 struct in_addr netmask, struct in_addr broadcast, struct in_addr relay,
358 struct in_addr primary)
359{
360 struct dhcp_context *context;
361
362 for (context = daemon->dhcp; context; context = context->next)
363 {
364 if (!(context->flags & CONTEXT_NETMASK) &&
365 (is_same_net(local, context->start, netmask) ||
366 is_same_net(local, context->end, netmask)))
367 {
368 if (context->netmask.s_addr != netmask.s_addr &&
369 !(is_same_net(local, context->start, netmask) &&
370 is_same_net(local, context->end, netmask)))
371 {
372 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
373 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
374 syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
375 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
376 }
377 context->netmask = netmask;
378 }
379
380 if (context->netmask.s_addr)
381 {
382 if (is_same_net(local, context->start, context->netmask) &&
383 is_same_net(local, context->end, context->netmask))
384 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100385 /* link it onto the current chain if we've not seen it before */
386 if (context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000387 {
388 context->router = local;
389 context->local = local;
390 context->current = current;
391 current = context;
392 }
393
394 if (!(context->flags & CONTEXT_BRDCAST))
395 {
396 if (is_same_net(broadcast, context->start, context->netmask))
397 context->broadcast = broadcast;
398 else
399 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
400 }
401 }
402 else if (relay.s_addr && is_same_net(relay, context->start, context->netmask))
403 {
404 context->router = relay;
405 context->local = primary;
406 /* fill in missing broadcast addresses for relayed ranges */
407 if (!(context->flags & CONTEXT_BRDCAST))
408 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
409 }
410
411 }
412 }
413
414 return current;
415}
416
Simon Kelley59353a62004-11-21 19:34:28 +0000417struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000418{
Simon Kelley36717ee2004-09-20 19:20:58 +0100419 /* Check is an address is OK for this network, check all
420 possible ranges. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000421
Simon Kelley36717ee2004-09-20 19:20:58 +0100422 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000423
Simon Kelley36717ee2004-09-20 19:20:58 +0100424 for (; context; context = context->current)
425 {
426 start = ntohl(context->start.s_addr);
427 end = ntohl(context->end.s_addr);
Simon Kelley3be34542004-09-11 19:12:13 +0100428
Simon Kelley0a852542005-03-23 20:28:59 +0000429 if (!(context->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100430 addr >= start &&
431 addr <= end)
Simon Kelley59353a62004-11-21 19:34:28 +0000432 return context;
Simon Kelley36717ee2004-09-20 19:20:58 +0100433 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000434
Simon Kelley59353a62004-11-21 19:34:28 +0000435 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000436}
Simon Kelley59353a62004-11-21 19:34:28 +0000437
438struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
439{
440 /* We start of with a set of possible contexts, all on the current subnet.
441 These are chained on ->current.
442 Here we have an address, and return the actual context correponding to that
443 address. Note that none may fit, if the address came a dhcp-host and is outside
444 any dhcp-range. In that case we return a static range is possible, or failing that,
445 any context on the subnet. (If there's more than one, this is a dodgy configuration:
446 maybe there should be a warning.) */
447
448 struct dhcp_context *tmp = address_available(context, taddr);
449
450 if (tmp)
451 return tmp;
452
453 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley0a852542005-03-23 20:28:59 +0000454 if (tmp->flags & CONTEXT_STATIC)
Simon Kelley59353a62004-11-21 19:34:28 +0000455 return tmp;
456
457 return context;
458}
459
Simon Kelleydfa666f2004-08-02 18:27:27 +0100460struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
461{
462 struct dhcp_config *config;
463
464 for (config = configs; config; config = config->next)
465 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
466 return config;
467
468 return NULL;
469}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000470
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000471/* Is every member of check matched by a member of pool? */
472int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
473{
474 struct dhcp_netid *tmp1;
475
476 if (!check)
477 return 0;
478
479 for (; check; check = check->next)
480 {
481 if (check->net[0] != '#')
482 {
483 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
484 if (strcmp(check->net, tmp1->net) == 0)
485 break;
486 if (!tmp1)
487 return 0;
488 }
489 else
490 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
491 if (strcmp((check->net)+1, tmp1->net) == 0)
492 return 0;
493 }
494 return 1;
495}
496
Simon Kelley3be34542004-09-11 19:12:13 +0100497int address_allocate(struct dhcp_context *context, struct daemon *daemon,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000498 struct in_addr *addrp, unsigned char *hwaddr, struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000499{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100500 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000501 a particular hwaddr/clientid/hostname in our configuration.
502 Try to return from contexts which mathc netis first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000503
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100504 struct in_addr start, addr ;
Simon Kelley0a852542005-03-23 20:28:59 +0000505 struct dhcp_context *c;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100506 unsigned int i, j;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100507
Simon Kelley0a852542005-03-23 20:28:59 +0000508 for (c = context; c; c = c->current)
509 if (c->flags & CONTEXT_STATIC)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000510 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000511 else if (netids && !(c->flags & CONTEXT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000512 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000513 else if (!netids && (c->flags & CONTEXT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000514 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000515 else if (netids && (c->flags & CONTEXT_FILTER) && !match_netid(&c->netid, netids))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000516 continue;
517 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000518 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100519 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelley0a852542005-03-23 20:28:59 +0000520 for (j = c->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100521 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
522
523 start.s_addr = addr.s_addr =
Simon Kelley0a852542005-03-23 20:28:59 +0000524 htonl(ntohl(c->start.s_addr) +
525 (j % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelley36717ee2004-09-20 19:20:58 +0100526
527 do {
528 if (!lease_find_by_addr(addr) &&
529 !config_find_by_address(daemon->dhcp_conf, addr))
530 {
531 if (icmp_ping(daemon, addr))
532 /* perturb address selection so that we are
533 less likely to try this address again. */
Simon Kelley0a852542005-03-23 20:28:59 +0000534 c->addr_epoch++;
Simon Kelley36717ee2004-09-20 19:20:58 +0100535 else
536 {
537 *addrp = addr;
538 return 1;
539 }
540 }
Simon Kelley3be34542004-09-11 19:12:13 +0100541
Simon Kelley36717ee2004-09-20 19:20:58 +0100542 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
543
Simon Kelley0a852542005-03-23 20:28:59 +0000544 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
545 addr = c->start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100546
547 } while (addr.s_addr != start.s_addr);
548 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000549
550 if (netids)
551 return address_allocate(context, daemon, addrp, hwaddr, NULL);
552
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000553 return 0;
554}
555
556static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
557{
558 if (!context)
Simon Kelley0a852542005-03-23 20:28:59 +0000559 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100560 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561 return 1;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100562 if (is_same_net(config->addr, context->start, context->netmask))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000563 return 1;
564
565 return 0;
566}
567
Simon Kelley0a852542005-03-23 20:28:59 +0000568
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000569struct dhcp_config *find_config(struct dhcp_config *configs,
570 struct dhcp_context *context,
571 unsigned char *clid, int clid_len,
572 unsigned char *hwaddr, char *hostname)
573{
574 struct dhcp_config *config;
575
Simon Kelley0a852542005-03-23 20:28:59 +0000576 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000577 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100578 if (config->flags & CONFIG_CLID)
579 {
580 if (config->clid_len == clid_len &&
581 memcmp(config->clid, clid, clid_len) == 0 &&
582 is_addr_in_context(context, config))
583 return config;
584
585 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
586 cope with that here */
587 if (*clid == 0 && config->clid_len == clid_len-1 &&
588 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
589 is_addr_in_context(context, config))
590 return config;
591 }
592
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000593 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100594 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley0a852542005-03-23 20:28:59 +0000595 config->wildcard_mask == 0 &&
Simon Kelley33820b72004-04-03 21:10:00 +0100596 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000597 is_addr_in_context(context, config))
598 return config;
599
Simon Kelley0a852542005-03-23 20:28:59 +0000600
601 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000602 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100603 if ((config->flags & CONFIG_NAME) &&
604 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000605 is_addr_in_context(context, config))
606 return config;
607
Simon Kelley0a852542005-03-23 20:28:59 +0000608 for (config = configs; config; config = config->next)
609 if ((config->flags & CONFIG_HWADDR) &&
610 config->wildcard_mask != 0 &&
611 is_addr_in_context(context, config))
612 {
613 int i;
614 unsigned int mask = config->wildcard_mask;
615 for (i = ETHER_ADDR_LEN - 1; i >= 0; i--, mask = mask >> 1)
616 if (mask & 1)
617 config->hwaddr[i] = hwaddr[i];
618 if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
619 return config;
620 }
621
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000622 return NULL;
623}
624
Simon Kelley3be34542004-09-11 19:12:13 +0100625void dhcp_read_ethers(struct daemon *daemon)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000626{
Simon Kelley44a2a312004-03-10 20:04:35 +0000627 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000628 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100629 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100630 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000631 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100632 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley3be34542004-09-11 19:12:13 +0100633 struct dhcp_config *config, *configs = daemon->dhcp_conf;
Simon Kelley33820b72004-04-03 21:10:00 +0100634 int count = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000635
636 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100637 {
638 syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
Simon Kelley3be34542004-09-11 19:12:13 +0100639 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100640 }
641
Simon Kelley44a2a312004-03-10 20:04:35 +0000642 while (fgets(buff, MAXDNAME, f))
643 {
Simon Kelley33820b72004-04-03 21:10:00 +0100644 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000645 buff[strlen(buff)-1] = 0;
646
647 if ((*buff == '#') || (*buff == '+'))
648 continue;
649
Simon Kelley33820b72004-04-03 21:10:00 +0100650 for (ip = buff; *ip && !isspace(*ip); ip++);
651 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000652 *ip = 0;
653 if (!*ip)
654 continue;
655
Simon Kelley0a852542005-03-23 20:28:59 +0000656 if (parse_hex(buff, hwaddr, 6, NULL) != 6)
Simon Kelley44a2a312004-03-10 20:04:35 +0000657 continue;
658
659 /* check for name or dotted-quad */
660 for (cp = ip; *cp; cp++)
661 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
662 break;
663
664 if (!*cp)
665 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000666 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelley1cff1662004-03-12 08:12:58 +0000667 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100668 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000669
670 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100671 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000672 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000673 }
674 else
675 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000676 if (!canonicalise(ip))
677 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100678 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000679
680 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100681 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000682 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000683 }
684
Simon Kelley1cff1662004-03-12 08:12:58 +0000685 if (!config)
686 {
Simon Kelley33820b72004-04-03 21:10:00 +0100687 for (config = configs; config; config = config->next)
688 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100689 config->wildcard_mask == 0 &&
Simon Kelley33820b72004-04-03 21:10:00 +0100690 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
691 break;
692
693 if (!config)
694 {
695 if (!(config = malloc(sizeof(struct dhcp_config))))
696 continue;
697 config->flags = 0;
Simon Kelley91dccd02005-03-31 17:48:32 +0100698 config->wildcard_mask = 0;
Simon Kelley33820b72004-04-03 21:10:00 +0100699 config->next = configs;
700 configs = config;
701 }
702
703 config->flags |= flags;
704
705 if (flags & CONFIG_NAME)
706 {
707 if ((config->hostname = malloc(strlen(ip)+1)))
708 strcpy(config->hostname, ip);
709 else
710 config->flags &= ~CONFIG_NAME;
711 }
712
713 if (flags & CONFIG_ADDR)
714 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000715 }
Simon Kelley33820b72004-04-03 21:10:00 +0100716
Simon Kelleyde379512004-06-22 20:23:33 +0100717 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100718 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelley1cff1662004-03-12 08:12:58 +0000719
Simon Kelley33820b72004-04-03 21:10:00 +0100720 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000721 }
722
723 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100724
725 syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
Simon Kelley3be34542004-09-11 19:12:13 +0100726
727 daemon->dhcp_conf = configs;
Simon Kelley44a2a312004-03-10 20:04:35 +0000728}
729
730void dhcp_update_configs(struct dhcp_config *configs)
731{
732 /* Some people like to keep all static IP addresses in /etc/hosts.
733 This goes through /etc/hosts and sets static addresses for any DHCP config
734 records which don't have an address and whose name matches. */
735
Simon Kelley1ab84e22004-01-29 16:48:35 +0000736 struct dhcp_config *config;
737 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000738
Simon Kelley1ab84e22004-01-29 16:48:35 +0000739 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100740 if (!(config->flags & CONFIG_ADDR) &&
741 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000742 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
743 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100744 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100745 config->addr = crec->addr.addr.addr.addr4;
Simon Kelley33820b72004-04-03 21:10:00 +0100746 config->flags |= CONFIG_ADDR;
747 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000748}
Simon Kelley44a2a312004-03-10 20:04:35 +0000749
Simon Kelleybb01cb92004-12-13 20:56:23 +0000750/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
751 for this address. If it has a domain part, that must match the set domain and
752 it gets stripped. */
753char *host_from_dns(struct daemon *daemon, struct in_addr addr)
754{
755 struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
756 char *hostname = NULL;
757
758 if (lookup && (lookup->flags & F_HOSTS))
759 {
760 hostname = daemon->dhcp_buff;
761 hostname[256] = 0;
762 strncpy(hostname, cache_get_name(lookup), 256);
763 hostname = strip_hostname(daemon, hostname);
764 }
765
766 return hostname;
767}
768
769char *strip_hostname(struct daemon *daemon, char *hostname)
770{
771 char *dot = strchr(hostname, '.');
772 if (dot)
773 {
774 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
775 {
776 syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
777 hostname = NULL;
778 }
779 else
780 {
781 *dot = 0; /* truncate */
782 if (strlen(hostname) == 0)
783 hostname = NULL; /* nothing left */
784 }
785 }
786 return hostname;
787}