blob: 14252c4c438bf5559cb5d33113e387fd901a83ec [file] [log] [blame]
Simon Kelleycdeda282006-03-16 20:16:06 +00001/* dnsmasq is Copyright (c) 2000-2006 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
Simon Kelley9e4abcb2004-01-22 19:47:41 +000013#include "dnsmasq.h"
14
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010015struct iface_param {
16 struct in_addr relay, primary;
17 struct dhcp_context *current;
18 int ind;
19};
20
Simon Kelley5aabfc72007-08-29 11:24:47 +010021static int complete_context(struct in_addr local, int if_index,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010022 struct in_addr netmask, struct in_addr broadcast, void *vparam);
23
Simon Kelley5aabfc72007-08-29 11:24:47 +010024void dhcp_init(void)
Simon Kelley44a2a312004-03-10 20:04:35 +000025{
26 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
27 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010028 int oneopt = 1;
Simon Kelley3be34542004-09-11 19:12:13 +010029 struct dhcp_config *configs, *cp;
Simon Kelleydfa666f2004-08-02 18:27:27 +010030
Simon Kelley44a2a312004-03-10 20:04:35 +000031 if (fd == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010032 die (_("cannot create DHCP socket : %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000033
Simon Kelley4011c4e2006-10-28 16:26:19 +010034 if (!fix_fd(fd) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010035#if defined(HAVE_LINUX_NETWORK)
Simon Kelley3be34542004-09-11 19:12:13 +010036 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000037#elif defined(IP_RECVIF)
Simon Kelley3be34542004-09-11 19:12:13 +010038 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000039#endif
Simon Kelley3be34542004-09-11 19:12:13 +010040 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010041 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000042
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000043 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010044 instance binding port 67. That's OK if they serve different networks.
45 Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD.
46 OpenBSD <= 4.0 screws up IP_RECVIF when SO_REUSEPORT is set, but
47 OpenBSD <= 3.9 doesn't have IP_RECVIF anyway, so we just have to elide
48 this for OpenBSD 4.0, if you want more than one instance on oBSD4.0, tough. */
49
50#ifndef OpenBSD4_0
51 if (daemon->options & OPT_NOWILD)
52 {
53#ifdef SO_REUSEPORT
54 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
55#else
56 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
57#endif
58 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010059 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010060 }
61#endif
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000062
Simon Kelley849a8352006-06-09 21:02:31 +010063 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000064 saddr.sin_family = AF_INET;
65 saddr.sin_port = htons(DHCP_SERVER_PORT);
66 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010067#ifdef HAVE_SOCKADDR_SA_LEN
68 saddr.sin_len = sizeof(struct sockaddr_in);
69#endif
70
Simon Kelley44a2a312004-03-10 20:04:35 +000071 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010072 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000073
Simon Kelley3be34542004-09-11 19:12:13 +010074 daemon->dhcpfd = fd;
75
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010076#ifndef HAVE_LINUX_NETWORK
77 /* When we're not using capabilities, we need to do this here before
78 we drop root. Also, set buffer size small, to avoid wasting
79 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +000080
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010081 if (daemon->options & OPT_NO_PING)
82 daemon->dhcp_icmp_fd = -1;
83 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
84 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +010085 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010086
87 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +010088 init_bpf();
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010089#endif
Simon Kelley3be34542004-09-11 19:12:13 +010090
Simon Kelleydfa666f2004-08-02 18:27:27 +010091 /* If the same IP appears in more than one host config, then DISCOVER
92 for one of the hosts will get the address, but REQUEST will be NAKed,
Simon Kelley5aabfc72007-08-29 11:24:47 +010093 since the address is reserved by the other one -> protocol loop.
94 Also check that FQDNs match the domain we are using. */
Simon Kelley3be34542004-09-11 19:12:13 +010095 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +010096 {
97 char *domain;
98 for (cp = configs->next; cp; cp = cp->next)
99 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
100 die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr), EC_BADCONF);
101
102 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
103 die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
104 }
Simon Kelley3be34542004-09-11 19:12:13 +0100105
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100106 daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
107 daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000108}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100109
Simon Kelley5aabfc72007-08-29 11:24:47 +0100110void dhcp_packet(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000111{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100112 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000113 struct dhcp_context *context;
114 struct iname *tmp;
115 struct ifreq ifr;
116 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100117 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000118 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119 struct iovec iov;
120 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100121 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000122 struct in_addr iface_addr, *addrp = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100123 struct iface_param parm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000124
Simon Kelley44a2a312004-03-10 20:04:35 +0000125 union {
126 struct cmsghdr align; /* this ensures alignment */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100127#ifdef HAVE_LINUX_NETWORK
Simon Kelley44a2a312004-03-10 20:04:35 +0000128 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 Kelley4011c4e2006-10-28 16:26:19 +0100134 msg.msg_control = NULL;
135 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000136 msg.msg_name = NULL;
137 msg.msg_namelen = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100138 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000139 msg.msg_iovlen = 1;
140
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100141 do
142 {
143 msg.msg_flags = 0;
144 while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
145 }
146 while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
147 expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
Simon Kelley44a2a312004-03-10 20:04:35 +0000148
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100149 /* expand_buf may have moved buffer */
150 mess = daemon->dhcp_packet.iov_base;
151 msg.msg_controllen = sizeof(control_u);
Simon Kelley4011c4e2006-10-28 16:26:19 +0100152 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100153 msg.msg_flags = 0;
154 msg.msg_name = &dest;
155 msg.msg_namelen = sizeof(dest);
156
Simon Kelley16972692006-10-16 20:04:18 +0100157 while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100158
Simon Kelley4011c4e2006-10-28 16:26:19 +0100159 if (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000160 return;
161
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100162#if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100163 if (msg.msg_controllen >= sizeof(struct cmsghdr))
164 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
165 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
166 {
167 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
168 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
169 unicast_dest = 1;
170 }
171
Simon Kelley8a911cc2004-03-16 18:35:52 +0000172 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100173 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000174 return;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000175
Simon Kelley44a2a312004-03-10 20:04:35 +0000176#elif defined(IP_RECVIF)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100177 if (msg.msg_controllen >= sizeof(struct cmsghdr))
178 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
179 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
180 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
181
Simon Kelley44a2a312004-03-10 20:04:35 +0000182 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
183 return;
Simon Kelley4011c4e2006-10-28 16:26:19 +0100184
185#ifdef MSG_BCAST
186 /* OpenBSD tells us when a packet was broadcast */
187 if (!(msg.msg_flags & MSG_BCAST))
188 unicast_dest = 1;
189#endif
Simon Kelley832af0b2007-01-21 20:01:28 +0000190
Simon Kelley44a2a312004-03-10 20:04:35 +0000191#else
Simon Kelley4011c4e2006-10-28 16:26:19 +0100192 /* fallback for systems without IP_RECVIF - allow only one interface
193 and assume packets arrive from it - yuk. */
Simon Kelley3be34542004-09-11 19:12:13 +0100194 {
195 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100196 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100197 strcpy(ifr.ifr_name, name->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100198 iface_index = if_nametoindex(name->name);
Simon Kelley3be34542004-09-11 19:12:13 +0100199 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000200#endif
201
Simon Kelley44a2a312004-03-10 20:04:35 +0000202 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000203 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
204 {
205 addrp = &iface_addr;
206 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
207 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000208
Simon Kelley5aabfc72007-08-29 11:24:47 +0100209 if (!iface_check(AF_INET, (struct all_addr *)addrp, &ifr, &iface_index))
Simon Kelley832af0b2007-01-21 20:01:28 +0000210 return;
211
Simon Kelley3d8df262005-08-29 12:19:27 +0100212 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
213 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
214 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000215
Simon Kelley832af0b2007-01-21 20:01:28 +0000216 /* interface may have been changed by alias in iface_check */
217 if (!addrp)
218 {
219 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1)
220 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100221 my_syslog(LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
Simon Kelley832af0b2007-01-21 20:01:28 +0000222 return;
223 }
224 else
225 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
226 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000227
Simon Kelley91dccd02005-03-31 17:48:32 +0100228 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100229 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100230 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000231
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100232 parm.relay = mess->giaddr;
233 parm.primary = iface_addr;
234 parm.current = NULL;
235 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000236
Simon Kelley5aabfc72007-08-29 11:24:47 +0100237 if (!iface_enumerate(&parm, complete_context, NULL))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100238 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000239 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100240 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, (size_t)sz,
241 now, unicast_dest, &is_inform);
242 lease_update_file(now);
243 lease_update_dns();
Simon Kelley16972692006-10-16 20:04:18 +0100244
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100245 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000246 return;
247
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100248 msg.msg_name = &dest;
249 msg.msg_namelen = sizeof(dest);
250 msg.msg_control = NULL;
251 msg.msg_controllen = 0;
252 msg.msg_iov = &iov;
253 iov.iov_base = daemon->dhcp_packet.iov_base;
254
255 /* packet buffer may have moved */
256 mess = daemon->dhcp_packet.iov_base;
257
Simon Kelley3be34542004-09-11 19:12:13 +0100258#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100259 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100260#endif
261
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100262 if (mess->giaddr.s_addr)
263 {
264 /* Send to BOOTP relay */
Simon Kelley208b65c2006-08-05 21:41:37 +0100265 dest.sin_port = htons(DHCP_SERVER_PORT);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100266 dest.sin_addr = mess->giaddr;
267 }
268 else if (mess->ciaddr.s_addr)
269 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100270 /* If the client's idea of its own address tallys with
271 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100272 source port too, and send back to that. If we're replying
273 to a DHCPINFORM, trust the source address always. */
274 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
275 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100276 {
277 dest.sin_port = htons(DHCP_CLIENT_PORT);
278 dest.sin_addr = mess->ciaddr;
279 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100280 }
281#ifdef HAVE_LINUX_NETWORK
Simon Kelley849a8352006-06-09 21:02:31 +0100282 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
283 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100284 {
Simon Kelley849a8352006-06-09 21:02:31 +0100285 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100286 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100287 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100288 msg.msg_controllen = sizeof(control_u);
289 cmptr = CMSG_FIRSTHDR(&msg);
290 dest.sin_addr.s_addr = INADDR_BROADCAST;
291 dest.sin_port = htons(DHCP_CLIENT_PORT);
292 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
293 pkt->ipi_ifindex = iface_index;
294 pkt->ipi_spec_dst.s_addr = 0;
295 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
296 cmptr->cmsg_level = SOL_IP;
297 cmptr->cmsg_type = IP_PKTINFO;
Simon Kelley44a2a312004-03-10 20:04:35 +0000298 }
299 else
300 {
Simon Kelley849a8352006-06-09 21:02:31 +0100301 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
302 struct sockaddr limits size to 14 bytes. */
303 struct arpreq req;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100304 dest.sin_addr = mess->yiaddr;
305 dest.sin_port = htons(DHCP_CLIENT_PORT);
Simon Kelley849a8352006-06-09 21:02:31 +0100306 *((struct sockaddr_in *)&req.arp_pa) = dest;
307 req.arp_ha.sa_family = mess->htype;
308 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
309 strncpy(req.arp_dev, ifr.ifr_name, 16);
310 req.arp_flags = ATF_COM;
311 ioctl(daemon->dhcpfd, SIOCSARP, &req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000312 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100313#else
314 else
315 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100316 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100317 return;
318 }
319#endif
320
321 while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000322}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100323
Simon Kelley0a852542005-03-23 20:28:59 +0000324/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100325 of each interface (and any relay address) and does the following things:
326
327 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
328 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
329 3) Fills in local (this host) and router (this host or relay) addresses.
330 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
331
Simon Kelley0a852542005-03-23 20:28:59 +0000332 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100333
Simon Kelley5aabfc72007-08-29 11:24:47 +0100334static int complete_context(struct in_addr local, int if_index,
335 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000336{
337 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100338 struct iface_param *param = vparam;
339
Simon Kelley0a852542005-03-23 20:28:59 +0000340 for (context = daemon->dhcp; context; context = context->next)
341 {
342 if (!(context->flags & CONTEXT_NETMASK) &&
343 (is_same_net(local, context->start, netmask) ||
344 is_same_net(local, context->end, netmask)))
345 {
346 if (context->netmask.s_addr != netmask.s_addr &&
347 !(is_same_net(local, context->start, netmask) &&
348 is_same_net(local, context->end, netmask)))
349 {
350 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
351 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100352 my_syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
353 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000354 }
355 context->netmask = netmask;
356 }
357
358 if (context->netmask.s_addr)
359 {
360 if (is_same_net(local, context->start, context->netmask) &&
361 is_same_net(local, context->end, context->netmask))
362 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100363 /* link it onto the current chain if we've not seen it before */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100364 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000365 {
366 context->router = local;
367 context->local = local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100368 context->current = param->current;
369 param->current = context;
Simon Kelley0a852542005-03-23 20:28:59 +0000370 }
371
372 if (!(context->flags & CONTEXT_BRDCAST))
373 {
374 if (is_same_net(broadcast, context->start, context->netmask))
375 context->broadcast = broadcast;
376 else
377 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
378 }
379 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100380 else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000381 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100382 context->router = param->relay;
383 context->local = param->primary;
Simon Kelley0a852542005-03-23 20:28:59 +0000384 /* fill in missing broadcast addresses for relayed ranges */
385 if (!(context->flags & CONTEXT_BRDCAST))
386 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
387 }
388
389 }
390 }
391
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100392 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000393}
394
Simon Kelley59353a62004-11-21 19:34:28 +0000395struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000396{
Simon Kelley36717ee2004-09-20 19:20:58 +0100397 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100398 possible ranges. Make sure that the address isn't in use
399 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000400
Simon Kelley36717ee2004-09-20 19:20:58 +0100401 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100402 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100403
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100404 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100405 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100406 return NULL;
407
408 for (tmp = context; tmp; tmp = tmp->current)
409 {
410 start = ntohl(tmp->start.s_addr);
411 end = ntohl(tmp->end.s_addr);
412
413 if (!(tmp->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100414 addr >= start &&
415 addr <= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100416 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100417 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000418
Simon Kelley59353a62004-11-21 19:34:28 +0000419 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000420}
Simon Kelley59353a62004-11-21 19:34:28 +0000421
422struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
423{
Simon Kelleye17fb622006-01-14 20:33:46 +0000424 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000425 These are chained on ->current.
426 Here we have an address, and return the actual context correponding to that
427 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000428 any dhcp-range. In that case we return a static range if possible, or failing that,
429 any context on the correct subnet. (If there's more than one, this is a dodgy
430 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000431
Simon Kelleye17fb622006-01-14 20:33:46 +0000432 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000433
Simon Kelleye17fb622006-01-14 20:33:46 +0000434 if ((tmp = address_available(context, taddr)))
Simon Kelley59353a62004-11-21 19:34:28 +0000435 return tmp;
436
437 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelleye17fb622006-01-14 20:33:46 +0000438 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
439 (tmp->flags & CONTEXT_STATIC))
Simon Kelley59353a62004-11-21 19:34:28 +0000440 return tmp;
441
Simon Kelleye17fb622006-01-14 20:33:46 +0000442 for (tmp = context; tmp; tmp = tmp->current)
443 if (is_same_net(taddr, tmp->start, tmp->netmask))
444 return tmp;
445
446 return NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000447}
448
Simon Kelleydfa666f2004-08-02 18:27:27 +0100449struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
450{
451 struct dhcp_config *config;
452
453 for (config = configs; config; config = config->next)
454 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
455 return config;
456
457 return NULL;
458}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000459
Simon Kelleycdeda282006-03-16 20:16:06 +0000460/* Is every member of check matched by a member of pool?
461 If negonly, match unless there's a negative tag which matches. */
462int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000463{
464 struct dhcp_netid *tmp1;
465
Simon Kelleycdeda282006-03-16 20:16:06 +0000466 if (!check && !negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000467 return 0;
468
469 for (; check; check = check->next)
470 {
471 if (check->net[0] != '#')
472 {
473 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
474 if (strcmp(check->net, tmp1->net) == 0)
475 break;
Simon Kelleycdeda282006-03-16 20:16:06 +0000476 if (!tmp1 || negonly)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000477 return 0;
478 }
479 else
480 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
481 if (strcmp((check->net)+1, tmp1->net) == 0)
482 return 0;
483 }
484 return 1;
485}
486
Simon Kelley5aabfc72007-08-29 11:24:47 +0100487int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000488 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100489 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000490{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100491 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000492 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000493 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000494
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100495 struct in_addr start, addr;
496 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000497 int i, pass;
498 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100499
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100500 /* hash hwaddr */
501 for (j = 0, i = 0; i < hw_len; i++)
502 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
503
Simon Kelleycdeda282006-03-16 20:16:06 +0000504 for (pass = 0; pass <= 1; pass++)
505 for (c = context; c; c = c->current)
506 if (c->flags & CONTEXT_STATIC)
507 continue;
508 else if (!match_netid(c->filter, netids, pass))
509 continue;
510 else
511 {
512 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000513 start.s_addr = addr.s_addr =
514 htonl(ntohl(c->start.s_addr) +
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100515 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelleycdeda282006-03-16 20:16:06 +0000516
517 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100518 /* eliminate addresses in use by the server. */
519 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100520 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100521 break;
522
523 if (!d &&
524 !lease_find_by_addr(addr) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000525 !config_find_by_address(daemon->dhcp_conf, addr))
526 {
527 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100528 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
529 ((float)PING_WAIT)));
530
531 *addrp = addr;
532
533 if (daemon->options & OPT_NO_PING)
534 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000535
536 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100537 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000538 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100539 on us repeatedly. As a final check, if we did more
540 than 60% of the possible ping checks in the last
541 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000542 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100543 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000544 victim = r; /* old record */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100545 else if (++count == max || r->addr.s_addr == addr.s_addr)
546 return 1;
547
Simon Kelley5aabfc72007-08-29 11:24:47 +0100548 if (icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000549 /* address in use: perturb address selection so that we are
550 less likely to try this address again. */
551 c->addr_epoch++;
552 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100553 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000554 /* at this point victim may hold an expired record */
555 if (!victim)
556 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100557 if ((victim = whine_malloc(sizeof(struct ping_result))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000558 {
559 victim->next = daemon->ping_results;
560 daemon->ping_results = victim;
561 }
562 }
563
564 /* record that this address is OK for 30s
565 without more ping checks */
566 if (victim)
567 {
568 victim->addr = addr;
569 victim->time = now;
570 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100571 return 1;
572 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000573 }
Simon Kelley3be34542004-09-11 19:12:13 +0100574
Simon Kelleycdeda282006-03-16 20:16:06 +0000575 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
576
577 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
578 addr = c->start;
579
580 } while (addr.s_addr != start.s_addr);
581 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000582 return 0;
583}
584
585static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
586{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000587 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000588 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100589 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000590 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000591 for (; context; context = context->current)
592 if (is_same_net(config->addr, context->start, context->netmask))
593 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000594
595 return 0;
596}
597
Simon Kelley0a852542005-03-23 20:28:59 +0000598
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000599struct dhcp_config *find_config(struct dhcp_config *configs,
600 struct dhcp_context *context,
601 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000602 unsigned char *hwaddr, int hw_len,
603 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000604{
605 struct dhcp_config *config;
606
Simon Kelley0a852542005-03-23 20:28:59 +0000607 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000608 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100609 if (config->flags & CONFIG_CLID)
610 {
611 if (config->clid_len == clid_len &&
612 memcmp(config->clid, clid, clid_len) == 0 &&
613 is_addr_in_context(context, config))
614 return config;
615
616 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
617 cope with that here */
618 if (*clid == 0 && config->clid_len == clid_len-1 &&
619 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
620 is_addr_in_context(context, config))
621 return config;
622 }
623
Simon Kelley0a852542005-03-23 20:28:59 +0000624
Simon Kelleycdeda282006-03-16 20:16:06 +0000625 for (config = configs; config; config = config->next)
626 if ((config->flags & CONFIG_HWADDR) &&
627 config->wildcard_mask == 0 &&
628 config->hwaddr_len == hw_len &&
629 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
630 memcmp(config->hwaddr, hwaddr, hw_len) == 0 &&
631 is_addr_in_context(context, config))
632 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100633
Simon Kelley0a852542005-03-23 20:28:59 +0000634 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000635 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100636 if ((config->flags & CONFIG_NAME) &&
637 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000638 is_addr_in_context(context, config))
639 return config;
640
Simon Kelleycdeda282006-03-16 20:16:06 +0000641 for (config = configs; config; config = config->next)
642 if ((config->flags & CONFIG_HWADDR) &&
643 config->wildcard_mask != 0 &&
644 config->hwaddr_len == hw_len &&
645 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100646 is_addr_in_context(context, config) &&
647 memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
648 return config;
649
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000650 return NULL;
651}
652
Simon Kelley5aabfc72007-08-29 11:24:47 +0100653void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000654{
Simon Kelley44a2a312004-03-10 20:04:35 +0000655 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000656 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100657 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100658 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000659 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100660 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100661 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100662 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000663 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100664
665 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000666
667 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100668 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100669 my_syslog(LOG_ERR, _("failed to read %s:%s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100670 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100671 }
672
Simon Kelley849a8352006-06-09 21:02:31 +0100673 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100674 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100675 {
676 tmp = config->next;
677 if (config->flags & CONFIG_FROM_ETHERS)
678 {
679 *up = tmp;
680 /* cannot have a clid */
681 if (config->flags & CONFIG_NAME)
682 free(config->hostname);
683 free(config);
684 }
685 else
686 up = &config->next;
687 }
688
Simon Kelley44a2a312004-03-10 20:04:35 +0000689 while (fgets(buff, MAXDNAME, f))
690 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000691 lineno++;
692
Simon Kelley33820b72004-04-03 21:10:00 +0100693 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000694 buff[strlen(buff)-1] = 0;
695
696 if ((*buff == '#') || (*buff == '+'))
697 continue;
698
Simon Kelley33820b72004-04-03 21:10:00 +0100699 for (ip = buff; *ip && !isspace(*ip); ip++);
700 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000701 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000702 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000703 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100704 my_syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000705 continue;
706 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000707
708 /* check for name or dotted-quad */
709 for (cp = ip; *cp; cp++)
710 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
711 break;
712
713 if (!*cp)
714 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000715 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000716 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100717 my_syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000718 continue;
719 }
720
Simon Kelley33820b72004-04-03 21:10:00 +0100721 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000722
Simon Kelley16972692006-10-16 20:04:18 +0100723 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100724 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000725 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000726 }
727 else
728 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100729 if (!canonicalise(ip) || strip_hostname(ip))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000730 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100731 my_syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000732 continue;
733 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100734
Simon Kelley33820b72004-04-03 21:10:00 +0100735 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000736
Simon Kelley16972692006-10-16 20:04:18 +0100737 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100738 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000739 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000740 }
741
Simon Kelley1cff1662004-03-12 08:12:58 +0000742 if (!config)
743 {
Simon Kelley16972692006-10-16 20:04:18 +0100744 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100745 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100746 config->wildcard_mask == 0 &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000747 config->hwaddr_len == ETHER_ADDR_LEN &&
748 (config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100749 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
750 break;
751
752 if (!config)
753 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100754 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100755 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100756 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley91dccd02005-03-31 17:48:32 +0100757 config->wildcard_mask = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100758 config->next = daemon->dhcp_conf;
759 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100760 }
761
762 config->flags |= flags;
763
764 if (flags & CONFIG_NAME)
765 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100766 if ((config->hostname = whine_malloc(strlen(ip)+1)))
Simon Kelley33820b72004-04-03 21:10:00 +0100767 strcpy(config->hostname, ip);
768 else
769 config->flags &= ~CONFIG_NAME;
770 }
771
772 if (flags & CONFIG_ADDR)
773 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000774 }
Simon Kelley33820b72004-04-03 21:10:00 +0100775
Simon Kelleyde379512004-06-22 20:23:33 +0100776 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100777 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelleycdeda282006-03-16 20:16:06 +0000778 config->hwaddr_len = ETHER_ADDR_LEN;
779 config->hwaddr_type = ARPHRD_ETHER;
Simon Kelley33820b72004-04-03 21:10:00 +0100780 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000781 }
782
783 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100784
Simon Kelleyf2621c72007-04-29 19:47:21 +0100785 my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000786}
787
Simon Kelley5aabfc72007-08-29 11:24:47 +0100788void dhcp_read_hosts(void)
789{
790 struct dhcp_config *configs, *cp, **up;
791 int count;
792
793 /* remove existing... */
794 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
795 {
796 cp = configs->next;
797
798 if (configs->flags & CONFIG_BANK)
799 {
800 if (configs->flags & CONFIG_CLID)
801 free(configs->clid);
802 if (configs->flags & CONFIG_NETID)
803 free(configs->netid.net);
804 if (configs->flags & CONFIG_NAME)
805 free(configs->hostname);
806
807 *up = configs->next;
808 free(configs);
809 }
810 else
811 up = &configs->next;
812 }
813
814 one_file(daemon->dhcp_hosts_file, 1, 1);
815
816 for (count = 0, configs = daemon->dhcp_conf; configs; configs = configs->next)
817 {
818 if (configs->flags & CONFIG_BANK)
819 {
820 char *domain;
821 count++;
822
823 for (cp = configs->next; cp; cp = cp->next)
824 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
825 {
826 my_syslog(LOG_ERR, _("duplicate IP address %s in %s."), inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
827 configs->flags &= ~CONFIG_ADDR;
828 }
829
830 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
831 {
832 my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
833 free(configs->hostname);
834 configs->flags &= ~CONFIG_NAME;
835 }
836 }
837 }
838
839 my_syslog(LOG_INFO, _("read %s - %d hosts"), daemon->dhcp_hosts_file, count);
840
841}
842
Simon Kelley44a2a312004-03-10 20:04:35 +0000843void dhcp_update_configs(struct dhcp_config *configs)
844{
845 /* Some people like to keep all static IP addresses in /etc/hosts.
846 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100847 records which don't have an address and whose name matches.
848 We take care to maintain the invariant that any IP address can appear
Simon Kelley849a8352006-06-09 21:02:31 +0100849 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
850 restore the status-quo ante first. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000851
Simon Kelley1ab84e22004-01-29 16:48:35 +0000852 struct dhcp_config *config;
853 struct crec *crec;
Simon Kelley849a8352006-06-09 21:02:31 +0100854
855 for (config = configs; config; config = config->next)
856 if (config->flags & CONFIG_ADDR_HOSTS)
857 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
Simon Kelley44a2a312004-03-10 20:04:35 +0000858
Simon Kelley1ab84e22004-01-29 16:48:35 +0000859 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100860 if (!(config->flags & CONFIG_ADDR) &&
861 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000862 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
863 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100864 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100865 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100866 my_syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
867 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
Simon Kelley3d8df262005-08-29 12:19:27 +0100868 else
869 {
870 config->addr = crec->addr.addr.addr.addr4;
Simon Kelley849a8352006-06-09 21:02:31 +0100871 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
Simon Kelley3d8df262005-08-29 12:19:27 +0100872 }
Simon Kelley33820b72004-04-03 21:10:00 +0100873 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000874}
Simon Kelley44a2a312004-03-10 20:04:35 +0000875
Simon Kelleybb01cb92004-12-13 20:56:23 +0000876/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
877 for this address. If it has a domain part, that must match the set domain and
878 it gets stripped. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100879char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000880{
881 struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
882 char *hostname = NULL;
883
884 if (lookup && (lookup->flags & F_HOSTS))
885 {
886 hostname = daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000887 strncpy(hostname, cache_get_name(lookup), 256);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100888 hostname[255] = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100889 if (strip_hostname(hostname))
890 hostname = NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000891 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100892
Simon Kelleybb01cb92004-12-13 20:56:23 +0000893 return hostname;
894}
895
Simon Kelley5aabfc72007-08-29 11:24:47 +0100896/* return illegal domain or NULL if OK */
897char *strip_hostname(char *hostname)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000898{
899 char *dot = strchr(hostname, '.');
Simon Kelley5aabfc72007-08-29 11:24:47 +0100900
901 if (!dot)
902 return NULL;
903
904 *dot = 0; /* truncate */
905
906 if (*(dot+1) && (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)))
907 return dot+1;
908
909 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000910}