blob: a50d8d01857d7278c69c9c26b08113c5c1231676 [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 Kelleyb8187c82005-11-26 21:46:27 +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 Kelleyb8187c82005-11-26 21:46:27 +000035 die(_("failed to set options on DHCP socket: %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000036
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)
Simon Kelleyb8187c82005-11-26 21:46:27 +000042 die(_("failed to set SO_REUSEADDR on DHCP socket: %s"), NULL);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000043
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)))
Simon Kelleyb8187c82005-11-26 21:46:27 +000052 die(_("failed to bind DHCP server socket: %s"), NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000053
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 Kelley3d8df262005-08-29 12:19:27 +010059 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley3be34542004-09-11 19:12:13 +010060 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
Simon Kelleyb8187c82005-11-26 21:46:27 +000061 die(_("cannot create ICMP raw socket: %s."), NULL);
Simon Kelley3be34542004-09-11 19:12:13 +010062
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)
Simon Kelleyb8187c82005-11-26 21:46:27 +000075 die(_("cannot create DHCP BPF socket: %s"), NULL);
Simon Kelley3be34542004-09-11 19:12:13 +010076 }
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)
Simon Kelleyb8187c82005-11-26 21:46:27 +000086 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 Kelleyb8187c82005-11-26 21:46:27 +000098 die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr));
Simon Kelley3be34542004-09-11 19:12:13 +010099
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 Kelley3d8df262005-08-29 12:19:27 +0100105 daemon->ping_results = NULL;
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 Kelleyb8187c82005-11-26 21:46:27 +0000119 int unicast_dest = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000120 struct in_addr iface_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000121#ifdef HAVE_BPF
122 unsigned char iface_hwaddr[ETHER_ADDR_LEN];
123#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000124
Simon Kelley44a2a312004-03-10 20:04:35 +0000125 union {
126 struct cmsghdr align; /* this ensures alignment */
127#ifdef IP_PKTINFO
128 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
129#else
130 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
131#endif
132 } control_u;
133
Simon Kelley3be34542004-09-11 19:12:13 +0100134 iov[0].iov_base = (char *)mess;
135 iov[0].iov_len = sizeof(struct dhcp_packet);
Simon Kelley44a2a312004-03-10 20:04:35 +0000136
137 msg.msg_control = control_u.control;
138 msg.msg_controllen = sizeof(control_u);
139 msg.msg_flags = 0;
140 msg.msg_name = NULL;
141 msg.msg_namelen = 0;
142 msg.msg_iov = iov;
143 msg.msg_iovlen = 1;
144
Simon Kelley3be34542004-09-11 19:12:13 +0100145 sz = recvmsg(daemon->dhcpfd, &msg, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000146
147 if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
148 return;
149
150#if defined (IP_PKTINFO)
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 == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000155 {
156 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
157 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
158 unicast_dest = 1;
159 }
160
Simon Kelley8a911cc2004-03-16 18:35:52 +0000161 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100162 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000163 return;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000164
Simon Kelley44a2a312004-03-10 20:04:35 +0000165#elif defined(IP_RECVIF)
166 if (msg.msg_controllen < sizeof(struct cmsghdr))
167 return;
168 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
169 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
170 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000171
Simon Kelley44a2a312004-03-10 20:04:35 +0000172 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
173 return;
174
175#else
Simon Kelley3be34542004-09-11 19:12:13 +0100176 {
177 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100178 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100179 strcpy(ifr.ifr_name, name->name);
180 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000181#endif
182
183#ifdef HAVE_BPF
Simon Kelley44a2a312004-03-10 20:04:35 +0000184 ifr.ifr_addr.sa_family = AF_LINK;
Simon Kelley3be34542004-09-11 19:12:13 +0100185 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000186 return;
187 memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
188#endif
189
190 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100191 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
Simon Kelley44a2a312004-03-10 20:04:35 +0000192 return;
193 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
194
195 /* enforce available interface configuration */
Simon Kelley3be34542004-09-11 19:12:13 +0100196 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000197 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
198 return;
Simon Kelley3d8df262005-08-29 12:19:27 +0100199
200 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
201 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
202 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000203
Simon Kelley3be34542004-09-11 19:12:13 +0100204 if (daemon->if_names || daemon->if_addrs)
Simon Kelley44a2a312004-03-10 20:04:35 +0000205 {
Simon Kelley3be34542004-09-11 19:12:13 +0100206 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000207 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
208 break;
209 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100210 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000211 if (tmp->addr.sa.sa_family == AF_INET &&
212 tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
213 break;
214 if (!tmp)
215 return;
216 }
217
Simon Kelley91dccd02005-03-31 17:48:32 +0100218 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100219 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100220 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000221
Simon Kelley0a852542005-03-23 20:28:59 +0000222#ifdef HAVE_RTNETLINK
223 if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
224#endif
225 {
226 struct in_addr iface_netmask, iface_broadcast;
Simon Kelleye17fb622006-01-14 20:33:46 +0000227
228#ifdef HAVE_RTNETLINK
229 static int warned = 0;
230
231 if (!warned)
232 {
233 syslog(LOG_WARNING, _("Cannot use RTnetlink socket, falling back to ioctl API"));
234 warned = 1;
235 }
236#endif
237
Simon Kelley0a852542005-03-23 20:28:59 +0000238 if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) < 0)
239 return;
240 iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
241
242 if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) < 0)
243 return;
244 iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
245
246 context = complete_context(daemon, iface_addr, NULL, iface_netmask,
247 iface_broadcast, mess->giaddr, iface_addr);
248 }
249
Simon Kelley44a2a312004-03-10 20:04:35 +0000250 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000251 newlen = dhcp_reply(daemon, context, ifr.ifr_name, sz, now, unicast_dest);
Simon Kelley44a2a312004-03-10 20:04:35 +0000252 lease_update_file(0, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100253 lease_update_dns(daemon);
Simon Kelley44a2a312004-03-10 20:04:35 +0000254
255 if (newlen == 0)
256 return;
257
258 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
259 {
Simon Kelley3be34542004-09-11 19:12:13 +0100260 /* To send to BOOTP relay or configured client, use the IP packet */
Simon Kelley44a2a312004-03-10 20:04:35 +0000261
262 struct sockaddr_in dest;
263 dest.sin_family = AF_INET;
Simon Kelley3be34542004-09-11 19:12:13 +0100264#ifdef HAVE_SOCKADDR_SA_LEN
265 dest.sin_len = sizeof(struct sockaddr_in);
266#endif
267
Simon Kelley44a2a312004-03-10 20:04:35 +0000268 if (mess->giaddr.s_addr)
269 {
270 dest.sin_port = htons(DHCP_SERVER_PORT);
271 dest.sin_addr = mess->giaddr;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000272 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000273 else
274 {
275 dest.sin_port = htons(DHCP_CLIENT_PORT);
276 dest.sin_addr = mess->ciaddr;
277 }
278
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100279 while(sendto(daemon->dhcpfd, mess, newlen, 0,
280 (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
281 retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000282 }
283 else
284 {
285 /* Hairy stuff, packet either has to go to the
286 net broadcast or the destination can't reply to ARP yet,
287 but we do know the physical address.
288 Build the packet by steam, and send directly, bypassing
289 the kernel IP stack */
290
291 u32 i, sum;
292 unsigned char hwdest[ETHER_ADDR_LEN];
293
294 if (ntohs(mess->flags) & 0x8000)
295 {
296 memset(hwdest, 255, ETHER_ADDR_LEN);
297 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
298 }
299 else
300 {
301 memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
302 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
303 }
304
305 rawpacket->ip.ip_p = IPPROTO_UDP;
306 rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
307 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
308 sizeof(struct udphdr) +
309 newlen) ;
310 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
311 rawpacket->ip.ip_v = IPVERSION;
312 rawpacket->ip.ip_tos = 0;
313 rawpacket->ip.ip_id = htons(0);
314 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
315 rawpacket->ip.ip_ttl = IPDEFTTL;
316 rawpacket->ip.ip_sum = 0;
317 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
318 sum += ((u16 *)&rawpacket->ip)[i];
319 while (sum>>16)
320 sum = (sum & 0xffff) + (sum >> 16);
321 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
322
323 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
324 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
325 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
326 rawpacket->udp.uh_sum = 0;
327 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
328 sum += htons(IPPROTO_UDP);
329 for (i = 0; i < 4; i++)
330 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
331 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
332 sum += ((u16 *)&rawpacket->udp)[i];
333 while (sum>>16)
334 sum = (sum & 0xffff) + (sum >> 16);
335 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
336
337 {
338#ifdef HAVE_BPF
339 struct ether_header header;
340
341 header.ether_type = htons(ETHERTYPE_IP);
342 memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
343 memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
344
Simon Kelley3be34542004-09-11 19:12:13 +0100345 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr);
Simon Kelley44a2a312004-03-10 20:04:35 +0000346
347 iov[0].iov_base = (char *)&header;
348 iov[0].iov_len = sizeof(struct ether_header);
349 iov[1].iov_base = (char *)rawpacket;
350 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
Simon Kelley26128d22004-11-14 16:43:54 +0000351 while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000352#else
353 struct sockaddr_ll dest;
354
Simon Kelley3d8df262005-08-29 12:19:27 +0100355 memset(&dest, 0, sizeof(dest));
Simon Kelley44a2a312004-03-10 20:04:35 +0000356 dest.sll_family = AF_PACKET;
357 dest.sll_halen = ETHER_ADDR_LEN;
358 dest.sll_ifindex = iface_index;
359 dest.sll_protocol = htons(ETHERTYPE_IP);
360 memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100361 while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
362 0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
Simon Kelley26128d22004-11-14 16:43:54 +0000363 retry_send());
Simon Kelley44a2a312004-03-10 20:04:35 +0000364#endif
365 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000366 }
367}
368
Simon Kelley0a852542005-03-23 20:28:59 +0000369/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
370 of the interface on which a DHCP packet arrives (and any relay address) and does the
371 following things:
372 1) Fills in any netmask and broadcast addresses which have not been explicitly configured.
373 2) Fills in local (this host) and router (this host or relay) addresses.
374 3) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
375 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
376struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, struct dhcp_context *current,
377 struct in_addr netmask, struct in_addr broadcast, struct in_addr relay,
378 struct in_addr primary)
379{
380 struct dhcp_context *context;
381
382 for (context = daemon->dhcp; context; context = context->next)
383 {
384 if (!(context->flags & CONTEXT_NETMASK) &&
385 (is_same_net(local, context->start, netmask) ||
386 is_same_net(local, context->end, netmask)))
387 {
388 if (context->netmask.s_addr != netmask.s_addr &&
389 !(is_same_net(local, context->start, netmask) &&
390 is_same_net(local, context->end, netmask)))
391 {
392 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
393 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelleyb8187c82005-11-26 21:46:27 +0000394 syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelley0a852542005-03-23 20:28:59 +0000395 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
396 }
397 context->netmask = netmask;
398 }
399
400 if (context->netmask.s_addr)
401 {
402 if (is_same_net(local, context->start, context->netmask) &&
403 is_same_net(local, context->end, context->netmask))
404 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100405 /* link it onto the current chain if we've not seen it before */
406 if (context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000407 {
408 context->router = local;
409 context->local = local;
410 context->current = current;
411 current = context;
412 }
413
414 if (!(context->flags & CONTEXT_BRDCAST))
415 {
416 if (is_same_net(broadcast, context->start, context->netmask))
417 context->broadcast = broadcast;
418 else
419 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
420 }
421 }
422 else if (relay.s_addr && is_same_net(relay, context->start, context->netmask))
423 {
424 context->router = relay;
425 context->local = primary;
426 /* fill in missing broadcast addresses for relayed ranges */
427 if (!(context->flags & CONTEXT_BRDCAST))
428 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
429 }
430
431 }
432 }
433
434 return current;
435}
436
Simon Kelley59353a62004-11-21 19:34:28 +0000437struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000438{
Simon Kelley36717ee2004-09-20 19:20:58 +0100439 /* Check is an address is OK for this network, check all
440 possible ranges. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000441
Simon Kelley36717ee2004-09-20 19:20:58 +0100442 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000443
Simon Kelley36717ee2004-09-20 19:20:58 +0100444 for (; context; context = context->current)
445 {
446 start = ntohl(context->start.s_addr);
447 end = ntohl(context->end.s_addr);
Simon Kelley3be34542004-09-11 19:12:13 +0100448
Simon Kelley0a852542005-03-23 20:28:59 +0000449 if (!(context->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100450 addr >= start &&
451 addr <= end)
Simon Kelley59353a62004-11-21 19:34:28 +0000452 return context;
Simon Kelley36717ee2004-09-20 19:20:58 +0100453 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000454
Simon Kelley59353a62004-11-21 19:34:28 +0000455 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000456}
Simon Kelley59353a62004-11-21 19:34:28 +0000457
458struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
459{
Simon Kelleye17fb622006-01-14 20:33:46 +0000460 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000461 These are chained on ->current.
462 Here we have an address, and return the actual context correponding to that
463 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000464 any dhcp-range. In that case we return a static range if possible, or failing that,
465 any context on the correct subnet. (If there's more than one, this is a dodgy
466 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000467
Simon Kelleye17fb622006-01-14 20:33:46 +0000468 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000469
Simon Kelleye17fb622006-01-14 20:33:46 +0000470 if ((tmp = address_available(context, taddr)))
Simon Kelley59353a62004-11-21 19:34:28 +0000471 return tmp;
472
473 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleye17fb622006-01-14 20:33:46 +0000474 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
475 (tmp->flags & CONTEXT_STATIC))
Simon Kelley59353a62004-11-21 19:34:28 +0000476 return tmp;
477
Simon Kelleye17fb622006-01-14 20:33:46 +0000478 for (tmp = context; tmp; tmp = tmp->current)
479 if (is_same_net(taddr, tmp->start, tmp->netmask))
480 return tmp;
481
482 return NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000483}
484
Simon Kelleydfa666f2004-08-02 18:27:27 +0100485struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
486{
487 struct dhcp_config *config;
488
489 for (config = configs; config; config = config->next)
490 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
491 return config;
492
493 return NULL;
494}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000495
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000496/* Is every member of check matched by a member of pool? */
497int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
498{
499 struct dhcp_netid *tmp1;
500
501 if (!check)
502 return 0;
503
504 for (; check; check = check->next)
505 {
506 if (check->net[0] != '#')
507 {
508 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
509 if (strcmp(check->net, tmp1->net) == 0)
510 break;
511 if (!tmp1)
512 return 0;
513 }
514 else
515 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
516 if (strcmp((check->net)+1, tmp1->net) == 0)
517 return 0;
518 }
519 return 1;
520}
521
Simon Kelley3be34542004-09-11 19:12:13 +0100522int address_allocate(struct dhcp_context *context, struct daemon *daemon,
Simon Kelley3d8df262005-08-29 12:19:27 +0100523 struct in_addr *addrp, unsigned char *hwaddr,
524 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000525{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100526 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000527 a particular hwaddr/clientid/hostname in our configuration.
528 Try to return from contexts which mathc netis first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000529
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100530 struct in_addr start, addr ;
Simon Kelley0a852542005-03-23 20:28:59 +0000531 struct dhcp_context *c;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100532 unsigned int i, j;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100533
Simon Kelley0a852542005-03-23 20:28:59 +0000534 for (c = context; c; c = c->current)
535 if (c->flags & CONTEXT_STATIC)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000536 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000537 else if (netids && !(c->flags & CONTEXT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000538 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000539 else if (!netids && (c->flags & CONTEXT_FILTER))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000540 continue;
Simon Kelley0a852542005-03-23 20:28:59 +0000541 else if (netids && (c->flags & CONTEXT_FILTER) && !match_netid(&c->netid, netids))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000542 continue;
543 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000544 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100545 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelley0a852542005-03-23 20:28:59 +0000546 for (j = c->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
Simon Kelley36717ee2004-09-20 19:20:58 +0100547 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
548
549 start.s_addr = addr.s_addr =
Simon Kelley0a852542005-03-23 20:28:59 +0000550 htonl(ntohl(c->start.s_addr) +
551 (j % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelley36717ee2004-09-20 19:20:58 +0100552
553 do {
554 if (!lease_find_by_addr(addr) &&
555 !config_find_by_address(daemon->dhcp_conf, addr))
556 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100557 struct ping_result *r, *victim = NULL;
558 int count;
559
560 /* check if we failed to ping addr sometime in the last
561 30s. If so, assume the same situation still exists.
562 This avoids problems when a stupid client bangs
563 on us repeatedly. As a final check, is we did more
564 than six ping checks in the last 30s, we are in
565 high-load mode, so don't do any more. */
566 for (count = 0, r = daemon->ping_results; r; r = r->next)
567 if (difftime(now, r->time) > 30.0)
568 victim = r; /* old record */
569 else if (++count == 6 || r->addr.s_addr == addr.s_addr)
570 {
571 *addrp = addr;
572 return 1;
573 }
574
Simon Kelley36717ee2004-09-20 19:20:58 +0100575 if (icmp_ping(daemon, addr))
Simon Kelley3d8df262005-08-29 12:19:27 +0100576 /* address in use: perturb address selection so that we are
Simon Kelley36717ee2004-09-20 19:20:58 +0100577 less likely to try this address again. */
Simon Kelley0a852542005-03-23 20:28:59 +0000578 c->addr_epoch++;
Simon Kelley36717ee2004-09-20 19:20:58 +0100579 else
580 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100581 /* at this point victim may hold an expired record */
582 if (!victim)
583 {
584 if ((victim = malloc(sizeof(struct ping_result))))
585 {
586 victim->next = daemon->ping_results;
587 daemon->ping_results = victim;
588 }
589 }
590
591 /* record that this address is OK for 30s
592 without more ping checks */
593 if (victim)
594 {
595 victim->addr = addr;
596 victim->time = now;
597 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100598 *addrp = addr;
599 return 1;
600 }
601 }
Simon Kelley3be34542004-09-11 19:12:13 +0100602
Simon Kelley36717ee2004-09-20 19:20:58 +0100603 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
604
Simon Kelley0a852542005-03-23 20:28:59 +0000605 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
606 addr = c->start;
Simon Kelley36717ee2004-09-20 19:20:58 +0100607
608 } while (addr.s_addr != start.s_addr);
609 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000610
611 if (netids)
Simon Kelley3d8df262005-08-29 12:19:27 +0100612 return address_allocate(context, daemon, addrp, hwaddr, NULL, now);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000613
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000614 return 0;
615}
616
617static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
618{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000619 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000620 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100621 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000622 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000623 for (; context; context = context->current)
624 if (is_same_net(config->addr, context->start, context->netmask))
625 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000626
627 return 0;
628}
629
Simon Kelley0a852542005-03-23 20:28:59 +0000630
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000631struct dhcp_config *find_config(struct dhcp_config *configs,
632 struct dhcp_context *context,
633 unsigned char *clid, int clid_len,
634 unsigned char *hwaddr, char *hostname)
635{
636 struct dhcp_config *config;
637
Simon Kelley0a852542005-03-23 20:28:59 +0000638 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000639 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100640 if (config->flags & CONFIG_CLID)
641 {
642 if (config->clid_len == clid_len &&
643 memcmp(config->clid, clid, clid_len) == 0 &&
644 is_addr_in_context(context, config))
645 return config;
646
647 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
648 cope with that here */
649 if (*clid == 0 && config->clid_len == clid_len-1 &&
650 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
651 is_addr_in_context(context, config))
652 return config;
653 }
654
Simon Kelley0a852542005-03-23 20:28:59 +0000655
Simon Kelley3d8df262005-08-29 12:19:27 +0100656 if (hwaddr)
657 for (config = configs; config; config = config->next)
658 if ((config->flags & CONFIG_HWADDR) &&
659 config->wildcard_mask == 0 &&
660 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
661 is_addr_in_context(context, config))
662 return config;
663
664
Simon Kelley0a852542005-03-23 20:28:59 +0000665 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000666 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100667 if ((config->flags & CONFIG_NAME) &&
668 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000669 is_addr_in_context(context, config))
670 return config;
671
Simon Kelley3d8df262005-08-29 12:19:27 +0100672 if (hwaddr)
673 for (config = configs; config; config = config->next)
674 if ((config->flags & CONFIG_HWADDR) &&
675 config->wildcard_mask != 0 &&
676 is_addr_in_context(context, config))
677 {
678 int i;
679 unsigned int mask = config->wildcard_mask;
680 for (i = ETHER_ADDR_LEN - 1; i >= 0; i--, mask = mask >> 1)
681 if (mask & 1)
682 config->hwaddr[i] = hwaddr[i];
683 if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
684 return config;
685 }
686
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000687 return NULL;
688}
689
Simon Kelley3be34542004-09-11 19:12:13 +0100690void dhcp_read_ethers(struct daemon *daemon)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000691{
Simon Kelley44a2a312004-03-10 20:04:35 +0000692 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000693 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100694 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100695 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000696 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100697 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley3be34542004-09-11 19:12:13 +0100698 struct dhcp_config *config, *configs = daemon->dhcp_conf;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000699 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100700
701 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000702
703 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100704 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000705 syslog(LOG_ERR, _("failed to read %s:%m"), ETHERSFILE);
Simon Kelley3be34542004-09-11 19:12:13 +0100706 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100707 }
708
Simon Kelley44a2a312004-03-10 20:04:35 +0000709 while (fgets(buff, MAXDNAME, f))
710 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000711 lineno++;
712
Simon Kelley33820b72004-04-03 21:10:00 +0100713 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000714 buff[strlen(buff)-1] = 0;
715
716 if ((*buff == '#') || (*buff == '+'))
717 continue;
718
Simon Kelley33820b72004-04-03 21:10:00 +0100719 for (ip = buff; *ip && !isspace(*ip); ip++);
720 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000721 *ip = 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000722 if (!*ip || parse_hex(buff, hwaddr, 6, NULL) != 6)
723 {
724 syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
725 continue;
726 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000727
728 /* check for name or dotted-quad */
729 for (cp = ip; *cp; cp++)
730 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
731 break;
732
733 if (!*cp)
734 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000735 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000736 {
737 syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
738 continue;
739 }
740
Simon Kelley33820b72004-04-03 21:10:00 +0100741 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000742
743 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100744 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000745 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000746 }
747 else
748 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000749 if (!canonicalise(ip))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000750 {
751 syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
752 continue;
753 }
754
Simon Kelley33820b72004-04-03 21:10:00 +0100755 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000756
757 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100758 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000759 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000760 }
761
Simon Kelley1cff1662004-03-12 08:12:58 +0000762 if (!config)
763 {
Simon Kelley33820b72004-04-03 21:10:00 +0100764 for (config = configs; config; config = config->next)
765 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100766 config->wildcard_mask == 0 &&
Simon Kelley33820b72004-04-03 21:10:00 +0100767 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
768 break;
769
770 if (!config)
771 {
772 if (!(config = malloc(sizeof(struct dhcp_config))))
773 continue;
774 config->flags = 0;
Simon Kelley91dccd02005-03-31 17:48:32 +0100775 config->wildcard_mask = 0;
Simon Kelley33820b72004-04-03 21:10:00 +0100776 config->next = configs;
777 configs = config;
778 }
779
780 config->flags |= flags;
781
782 if (flags & CONFIG_NAME)
783 {
784 if ((config->hostname = malloc(strlen(ip)+1)))
785 strcpy(config->hostname, ip);
786 else
787 config->flags &= ~CONFIG_NAME;
788 }
789
790 if (flags & CONFIG_ADDR)
791 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000792 }
Simon Kelley33820b72004-04-03 21:10:00 +0100793
Simon Kelleyde379512004-06-22 20:23:33 +0100794 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100795 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelley1cff1662004-03-12 08:12:58 +0000796
Simon Kelley33820b72004-04-03 21:10:00 +0100797 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000798 }
799
800 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100801
Simon Kelleyb8187c82005-11-26 21:46:27 +0000802 syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley3be34542004-09-11 19:12:13 +0100803
804 daemon->dhcp_conf = configs;
Simon Kelley44a2a312004-03-10 20:04:35 +0000805}
806
807void dhcp_update_configs(struct dhcp_config *configs)
808{
809 /* Some people like to keep all static IP addresses in /etc/hosts.
810 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100811 records which don't have an address and whose name matches.
812 We take care to maintain the invariant that any IP address can appear
813 in at most one dhcp-host. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000814
Simon Kelley1ab84e22004-01-29 16:48:35 +0000815 struct dhcp_config *config;
816 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000817
Simon Kelley1ab84e22004-01-29 16:48:35 +0000818 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100819 if (!(config->flags & CONFIG_ADDR) &&
820 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000821 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
822 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100823 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100824 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000825 syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
Simon Kelley3d8df262005-08-29 12:19:27 +0100826 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
827 else
828 {
829 config->addr = crec->addr.addr.addr.addr4;
830 config->flags |= CONFIG_ADDR;
831 }
Simon Kelley33820b72004-04-03 21:10:00 +0100832 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000833}
Simon Kelley44a2a312004-03-10 20:04:35 +0000834
Simon Kelleybb01cb92004-12-13 20:56:23 +0000835/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
836 for this address. If it has a domain part, that must match the set domain and
837 it gets stripped. */
838char *host_from_dns(struct daemon *daemon, struct in_addr addr)
839{
840 struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
841 char *hostname = NULL;
842
843 if (lookup && (lookup->flags & F_HOSTS))
844 {
845 hostname = daemon->dhcp_buff;
846 hostname[256] = 0;
847 strncpy(hostname, cache_get_name(lookup), 256);
848 hostname = strip_hostname(daemon, hostname);
849 }
850
851 return hostname;
852}
853
854char *strip_hostname(struct daemon *daemon, char *hostname)
855{
856 char *dot = strchr(hostname, '.');
857 if (dot)
858 {
859 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
860 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000861 syslog(LOG_WARNING, _("Ignoring DHCP host name %s because it has an illegal domain part"), hostname);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000862 hostname = NULL;
863 }
864 else
865 {
866 *dot = 0; /* truncate */
867 if (strlen(hostname) == 0)
868 hostname = NULL; /* nothing left */
869 }
870 }
871 return hostname;
872}