blob: 335fc040adceaa9ed4e8d1ed63c2aa67220b4b11 [file] [log] [blame]
Simon Kelley73a08a22009-02-05 20:28:08 +00001/* dnsmasq is Copyright (c) 2000-2009 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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021struct iface_param {
22 struct in_addr relay, primary;
23 struct dhcp_context *current;
24 int ind;
25};
26
Simon Kelley5aabfc72007-08-29 11:24:47 +010027static int complete_context(struct in_addr local, int if_index,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010028 struct in_addr netmask, struct in_addr broadcast, void *vparam);
29
Simon Kelley5aabfc72007-08-29 11:24:47 +010030void dhcp_init(void)
Simon Kelley44a2a312004-03-10 20:04:35 +000031{
32 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
33 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010034 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000035#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
36 int mtu = IP_PMTUDISC_DONT;
37#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010038
Simon Kelley44a2a312004-03-10 20:04:35 +000039 if (fd == -1)
Simon Kelley7622fc02009-06-04 20:32:05 +010040 die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000041
Simon Kelley824af852008-02-12 20:43:05 +000042 if (!fix_fd(fd) ||
43#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
44 setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
45#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010046#if defined(HAVE_LINUX_NETWORK)
Simon Kelley3be34542004-09-11 19:12:13 +010047 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley7622fc02009-06-04 20:32:05 +010048#else
Simon Kelley3be34542004-09-11 19:12:13 +010049 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000050#endif
Simon Kelley3be34542004-09-11 19:12:13 +010051 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010052 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000053
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000054 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010055 instance binding port 67. That's OK if they serve different networks.
Simon Kelley7622fc02009-06-04 20:32:05 +010056 Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
Simon Kelley4011c4e2006-10-28 16:26:19 +010057 if (daemon->options & OPT_NOWILD)
58 {
59#ifdef SO_REUSEPORT
60 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
61#else
62 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
63#endif
64 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010065 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010066 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000067
Simon Kelley849a8352006-06-09 21:02:31 +010068 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000069 saddr.sin_family = AF_INET;
Simon Kelley9e038942008-05-30 20:06:34 +010070 saddr.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley44a2a312004-03-10 20:04:35 +000071 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010072#ifdef HAVE_SOCKADDR_SA_LEN
73 saddr.sin_len = sizeof(struct sockaddr_in);
74#endif
75
Simon Kelley44a2a312004-03-10 20:04:35 +000076 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010077 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000078
Simon Kelley3be34542004-09-11 19:12:13 +010079 daemon->dhcpfd = fd;
80
Simon Kelley824af852008-02-12 20:43:05 +000081#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010082 /* When we're not using capabilities, we need to do this here before
83 we drop root. Also, set buffer size small, to avoid wasting
84 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +000085
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010086 if (daemon->options & OPT_NO_PING)
87 daemon->dhcp_icmp_fd = -1;
88 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
89 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +010090 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010091
92 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +010093 init_bpf();
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010094#endif
Simon Kelley3be34542004-09-11 19:12:13 +010095
Simon Kelley824af852008-02-12 20:43:05 +000096 check_dhcp_hosts(1);
97
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010098 daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
99 daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000100}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100101
Simon Kelley5aabfc72007-08-29 11:24:47 +0100102void dhcp_packet(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000103{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100104 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000105 struct dhcp_context *context;
106 struct iname *tmp;
107 struct ifreq ifr;
108 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100109 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000110 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111 struct iovec iov;
112 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100113 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000114 struct in_addr iface_addr, *addrp = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 struct iface_param parm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000116
Simon Kelley44a2a312004-03-10 20:04:35 +0000117 union {
118 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000119#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000120 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000121#elif defined(HAVE_SOLARIS_NETWORK)
122 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100123#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000124 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
125#endif
126 } control_u;
127
Simon Kelley4011c4e2006-10-28 16:26:19 +0100128 msg.msg_control = NULL;
129 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000130 msg.msg_name = NULL;
131 msg.msg_namelen = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100132 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000133 msg.msg_iovlen = 1;
134
Simon Kelley7622fc02009-06-04 20:32:05 +0100135 while (1)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100136 {
137 msg.msg_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100138 while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
139
140 if (sz == -1)
141 return;
142
143 if (!(msg.msg_flags & MSG_TRUNC))
144 break;
145
146 /* Very new Linux kernels return the actual size needed,
147 older ones always return truncated size */
148 if ((size_t)sz == daemon->dhcp_packet.iov_len)
149 {
150 if (!expand_buf(&daemon->dhcp_packet, sz + 100))
151 return;
152 }
153 else
154 {
155 expand_buf(&daemon->dhcp_packet, sz);
156 break;
157 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100158 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000159
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100160 /* expand_buf may have moved buffer */
Simon Kelley824af852008-02-12 20:43:05 +0000161 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100162 msg.msg_controllen = sizeof(control_u);
Simon Kelley4011c4e2006-10-28 16:26:19 +0100163 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100164 msg.msg_flags = 0;
165 msg.msg_name = &dest;
166 msg.msg_namelen = sizeof(dest);
167
Simon Kelley16972692006-10-16 20:04:18 +0100168 while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100169
Simon Kelley7622fc02009-06-04 20:32:05 +0100170 if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000171 return;
172
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100173#if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100174 if (msg.msg_controllen >= sizeof(struct cmsghdr))
175 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
176 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
177 {
178 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
179 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
180 unicast_dest = 1;
181 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100182
183#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100184 if (msg.msg_controllen >= sizeof(struct cmsghdr))
185 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
186 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley824af852008-02-12 20:43:05 +0000187 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelley7622fc02009-06-04 20:32:05 +0100188
Simon Kelley4011c4e2006-10-28 16:26:19 +0100189
Simon Kelley7622fc02009-06-04 20:32:05 +0100190#elif defined(HAVE_SOLARIS_NETWORK)
191 if (msg.msg_controllen >= sizeof(struct cmsghdr))
192 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
193 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
194 iface_index = *((unsigned int *)CMSG_DATA(cmptr));
195
196#endif
197
198 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
199 return;
200
Simon Kelley4011c4e2006-10-28 16:26:19 +0100201#ifdef MSG_BCAST
202 /* OpenBSD tells us when a packet was broadcast */
203 if (!(msg.msg_flags & MSG_BCAST))
204 unicast_dest = 1;
205#endif
Simon Kelley832af0b2007-01-21 20:01:28 +0000206
Simon Kelley44a2a312004-03-10 20:04:35 +0000207 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000208 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
209 {
210 addrp = &iface_addr;
211 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
212 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000213
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
Simon Kelley832af0b2007-01-21 20:01:28 +0000215 return;
216
Simon Kelley3d8df262005-08-29 12:19:27 +0100217 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
218 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
219 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000220
Simon Kelley832af0b2007-01-21 20:01:28 +0000221 /* interface may have been changed by alias in iface_check */
222 if (!addrp)
223 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100224 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000225 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100226 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
Simon Kelley832af0b2007-01-21 20:01:28 +0000227 return;
228 }
229 else
230 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
231 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000232
Simon Kelley91dccd02005-03-31 17:48:32 +0100233 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100234 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100235 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000236
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100237 parm.relay = mess->giaddr;
238 parm.primary = iface_addr;
239 parm.current = NULL;
240 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000241
Simon Kelley5aabfc72007-08-29 11:24:47 +0100242 if (!iface_enumerate(&parm, complete_context, NULL))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100243 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000244 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000245 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100246 now, unicast_dest, &is_inform);
247 lease_update_file(now);
248 lease_update_dns();
Simon Kelley16972692006-10-16 20:04:18 +0100249
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100250 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000251 return;
252
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100253 msg.msg_name = &dest;
254 msg.msg_namelen = sizeof(dest);
255 msg.msg_control = NULL;
256 msg.msg_controllen = 0;
257 msg.msg_iov = &iov;
258 iov.iov_base = daemon->dhcp_packet.iov_base;
259
260 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000261 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100262
Simon Kelley3be34542004-09-11 19:12:13 +0100263#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100264 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100265#endif
266
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100267 if (mess->giaddr.s_addr)
268 {
269 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100270 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100271 dest.sin_addr = mess->giaddr;
272 }
273 else if (mess->ciaddr.s_addr)
274 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100275 /* If the client's idea of its own address tallys with
276 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100277 source port too, and send back to that. If we're replying
278 to a DHCPINFORM, trust the source address always. */
279 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
280 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100281 {
Simon Kelley9e038942008-05-30 20:06:34 +0100282 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100283 dest.sin_addr = mess->ciaddr;
284 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100285 }
Simon Kelley824af852008-02-12 20:43:05 +0000286#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100287 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
288 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100289 {
Simon Kelley849a8352006-06-09 21:02:31 +0100290 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100291 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100292 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100293 msg.msg_controllen = sizeof(control_u);
294 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100295 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
296 pkt->ipi_ifindex = iface_index;
297 pkt->ipi_spec_dst.s_addr = 0;
298 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
299 cmptr->cmsg_level = SOL_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000300 cmptr->cmsg_type = IP_PKTINFO;
301 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100302 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000303 }
304 else
305 {
Simon Kelley849a8352006-06-09 21:02:31 +0100306 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
307 struct sockaddr limits size to 14 bytes. */
308 struct arpreq req;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100309 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100310 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley849a8352006-06-09 21:02:31 +0100311 *((struct sockaddr_in *)&req.arp_pa) = dest;
312 req.arp_ha.sa_family = mess->htype;
313 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
314 strncpy(req.arp_dev, ifr.ifr_name, 16);
315 req.arp_flags = ATF_COM;
316 ioctl(daemon->dhcpfd, SIOCSARP, &req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000317 }
Simon Kelley824af852008-02-12 20:43:05 +0000318#elif defined(HAVE_SOLARIS_NETWORK)
319 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
320 {
321 /* broadcast to 255.255.255.255 (or mac address invalid) */
322 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100323 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000324 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100325 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000326 }
327 else
328 {
329 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
330 Note that this only works for ethernet on solaris, because we use SIOCSARP
331 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
332 mysteriously. Bah. Fall back to broadcast for other net types. */
333 struct arpreq req;
334 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100335 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000336 *((struct sockaddr_in *)&req.arp_pa) = dest;
337 req.arp_ha.sa_family = AF_UNSPEC;
338 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
339 req.arp_flags = ATF_COM;
340 ioctl(daemon->dhcpfd, SIOCSARP, &req);
341 }
342#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100343 else
344 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100345 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100346 return;
347 }
348#endif
349
Simon Kelley824af852008-02-12 20:43:05 +0000350#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley73a08a22009-02-05 20:28:08 +0000351 setsockopt(daemon->dhcpfd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000352#endif
353
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100354 while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000355}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100356
Simon Kelley0a852542005-03-23 20:28:59 +0000357/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100358 of each interface (and any relay address) and does the following things:
359
360 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
361 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
362 3) Fills in local (this host) and router (this host or relay) addresses.
363 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
364
Simon Kelley0a852542005-03-23 20:28:59 +0000365 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366
Simon Kelley5aabfc72007-08-29 11:24:47 +0100367static int complete_context(struct in_addr local, int if_index,
368 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000369{
370 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100371 struct iface_param *param = vparam;
372
Simon Kelley0a852542005-03-23 20:28:59 +0000373 for (context = daemon->dhcp; context; context = context->next)
374 {
375 if (!(context->flags & CONTEXT_NETMASK) &&
376 (is_same_net(local, context->start, netmask) ||
377 is_same_net(local, context->end, netmask)))
378 {
379 if (context->netmask.s_addr != netmask.s_addr &&
380 !(is_same_net(local, context->start, netmask) &&
381 is_same_net(local, context->end, netmask)))
382 {
383 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
384 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100385 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100386 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000387 }
388 context->netmask = netmask;
389 }
390
391 if (context->netmask.s_addr)
392 {
393 if (is_same_net(local, context->start, context->netmask) &&
394 is_same_net(local, context->end, context->netmask))
395 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100396 /* link it onto the current chain if we've not seen it before */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100397 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000398 {
399 context->router = local;
400 context->local = local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100401 context->current = param->current;
402 param->current = context;
Simon Kelley0a852542005-03-23 20:28:59 +0000403 }
404
405 if (!(context->flags & CONTEXT_BRDCAST))
406 {
407 if (is_same_net(broadcast, context->start, context->netmask))
408 context->broadcast = broadcast;
409 else
410 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
411 }
412 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100413 else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000414 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100415 context->router = param->relay;
416 context->local = param->primary;
Simon Kelley0a852542005-03-23 20:28:59 +0000417 /* fill in missing broadcast addresses for relayed ranges */
418 if (!(context->flags & CONTEXT_BRDCAST))
419 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
420 }
421
422 }
423 }
424
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100425 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000426}
427
Simon Kelley824af852008-02-12 20:43:05 +0000428struct dhcp_context *address_available(struct dhcp_context *context,
429 struct in_addr taddr,
430 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000431{
Simon Kelley36717ee2004-09-20 19:20:58 +0100432 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100433 possible ranges. Make sure that the address isn't in use
434 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000435
Simon Kelley36717ee2004-09-20 19:20:58 +0100436 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100437 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100438
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100439 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100440 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100441 return NULL;
442
443 for (tmp = context; tmp; tmp = tmp->current)
444 {
445 start = ntohl(tmp->start.s_addr);
446 end = ntohl(tmp->end.s_addr);
447
448 if (!(tmp->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100449 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000450 addr <= end &&
451 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100452 return tmp;
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
Simon Kelley824af852008-02-12 20:43:05 +0000458struct dhcp_context *narrow_context(struct dhcp_context *context,
459 struct in_addr taddr,
460 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000461{
Simon Kelleye17fb622006-01-14 20:33:46 +0000462 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000463 These are chained on ->current.
464 Here we have an address, and return the actual context correponding to that
465 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000466 any dhcp-range. In that case we return a static range if possible, or failing that,
467 any context on the correct subnet. (If there's more than one, this is a dodgy
468 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000469
Simon Kelleye17fb622006-01-14 20:33:46 +0000470 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000471
Simon Kelley824af852008-02-12 20:43:05 +0000472 if (!(tmp = address_available(context, taddr, netids)))
473 {
474 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley7622fc02009-06-04 20:32:05 +0100475 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
476 (tmp->flags & CONTEXT_STATIC))
477 break;
Simon Kelley824af852008-02-12 20:43:05 +0000478
479 if (!tmp)
480 for (tmp = context; tmp; tmp = tmp->current)
481 if (is_same_net(taddr, tmp->start, tmp->netmask))
482 break;
483 }
Simon Kelley59353a62004-11-21 19:34:28 +0000484
Simon Kelley824af852008-02-12 20:43:05 +0000485 /* Only one context allowed now */
486 if (tmp)
487 tmp->current = NULL;
488
489 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000490}
491
Simon Kelleydfa666f2004-08-02 18:27:27 +0100492struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
493{
494 struct dhcp_config *config;
495
496 for (config = configs; config; config = config->next)
497 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
498 return config;
499
500 return NULL;
501}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000502
Simon Kelleycdeda282006-03-16 20:16:06 +0000503/* Is every member of check matched by a member of pool?
Simon Kelley824af852008-02-12 20:43:05 +0000504 If tagnotneeded, untagged is OK */
505int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000506{
507 struct dhcp_netid *tmp1;
508
Simon Kelley824af852008-02-12 20:43:05 +0000509 if (!check && !tagnotneeded)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000510 return 0;
511
512 for (; check; check = check->next)
513 {
514 if (check->net[0] != '#')
515 {
516 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
517 if (strcmp(check->net, tmp1->net) == 0)
518 break;
Simon Kelley824af852008-02-12 20:43:05 +0000519 if (!tmp1)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000520 return 0;
521 }
522 else
523 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
524 if (strcmp((check->net)+1, tmp1->net) == 0)
525 return 0;
526 }
527 return 1;
528}
529
Simon Kelley5aabfc72007-08-29 11:24:47 +0100530int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000531 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100532 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000533{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100534 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000535 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000536 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000537
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100538 struct in_addr start, addr;
539 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000540 int i, pass;
541 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100542
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100543 /* hash hwaddr */
544 for (j = 0, i = 0; i < hw_len; i++)
545 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
546
Simon Kelleycdeda282006-03-16 20:16:06 +0000547 for (pass = 0; pass <= 1; pass++)
548 for (c = context; c; c = c->current)
549 if (c->flags & CONTEXT_STATIC)
550 continue;
551 else if (!match_netid(c->filter, netids, pass))
552 continue;
553 else
554 {
555 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000556 start.s_addr = addr.s_addr =
557 htonl(ntohl(c->start.s_addr) +
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100558 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelleycdeda282006-03-16 20:16:06 +0000559
560 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100561 /* eliminate addresses in use by the server. */
562 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100563 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100564 break;
565
Simon Kelley73a08a22009-02-05 20:28:08 +0000566 /* Addresses which end in .255 and .0 are broken in Windows even when using
567 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
568 then 192.168.0.255 is a valid IP address, but not for Windows as it's
569 in the class C range. See KB281579. We therefore don't allocate these
570 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100571 if (!d &&
572 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000573 !config_find_by_address(daemon->dhcp_conf, addr) &&
574 (!IN_CLASSC(ntohl(addr.s_addr)) ||
575 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000576 {
577 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100578 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
579 ((float)PING_WAIT)));
580
581 *addrp = addr;
582
583 if (daemon->options & OPT_NO_PING)
584 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000585
586 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100587 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000588 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100589 on us repeatedly. As a final check, if we did more
590 than 60% of the possible ping checks in the last
591 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000592 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100593 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000594 victim = r; /* old record */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100595 else if (++count == max || r->addr.s_addr == addr.s_addr)
596 return 1;
597
Simon Kelley5aabfc72007-08-29 11:24:47 +0100598 if (icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000599 /* address in use: perturb address selection so that we are
600 less likely to try this address again. */
601 c->addr_epoch++;
602 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100603 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000604 /* at this point victim may hold an expired record */
605 if (!victim)
606 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100607 if ((victim = whine_malloc(sizeof(struct ping_result))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000608 {
609 victim->next = daemon->ping_results;
610 daemon->ping_results = victim;
611 }
612 }
613
614 /* record that this address is OK for 30s
615 without more ping checks */
616 if (victim)
617 {
618 victim->addr = addr;
619 victim->time = now;
620 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100621 return 1;
622 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000623 }
Simon Kelley3be34542004-09-11 19:12:13 +0100624
Simon Kelleycdeda282006-03-16 20:16:06 +0000625 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
626
627 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
628 addr = c->start;
629
630 } while (addr.s_addr != start.s_addr);
631 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000632 return 0;
633}
634
635static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
636{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000637 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000638 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100639 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000640 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000641 for (; context; context = context->current)
642 if (is_same_net(config->addr, context->start, context->netmask))
643 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644
645 return 0;
646}
647
Simon Kelley9009d742008-11-14 20:04:27 +0000648int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
649{
650 struct hwaddr_config *conf_addr;
651
652 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
653 if (conf_addr->wildcard_mask == 0 &&
654 conf_addr->hwaddr_len == len &&
655 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
656 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
657 return 1;
658
659 return 0;
660}
Simon Kelley0a852542005-03-23 20:28:59 +0000661
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000662struct dhcp_config *find_config(struct dhcp_config *configs,
663 struct dhcp_context *context,
664 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000665 unsigned char *hwaddr, int hw_len,
666 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000667{
Simon Kelley7622fc02009-06-04 20:32:05 +0100668 int count, new;
669 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000670 struct hwaddr_config *conf_addr;
671
Simon Kelley0a852542005-03-23 20:28:59 +0000672 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000673 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100674 if (config->flags & CONFIG_CLID)
675 {
676 if (config->clid_len == clid_len &&
677 memcmp(config->clid, clid, clid_len) == 0 &&
678 is_addr_in_context(context, config))
679 return config;
680
681 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
682 cope with that here */
683 if (*clid == 0 && config->clid_len == clid_len-1 &&
684 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
685 is_addr_in_context(context, config))
686 return config;
687 }
688
Simon Kelley0a852542005-03-23 20:28:59 +0000689
Simon Kelleycdeda282006-03-16 20:16:06 +0000690 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000691 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 is_addr_in_context(context, config))
693 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100694
Simon Kelley0a852542005-03-23 20:28:59 +0000695 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000696 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100697 if ((config->flags & CONFIG_NAME) &&
698 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000699 is_addr_in_context(context, config))
700 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100701
702 /* use match with fewest wildcast octets */
703 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
704 if (is_addr_in_context(context, config))
705 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
706 if (conf_addr->wildcard_mask != 0 &&
707 conf_addr->hwaddr_len == hw_len &&
708 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
709 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
710 {
711 count = new;
712 candidate = config;
713 }
714
715 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716}
717
Simon Kelley5aabfc72007-08-29 11:24:47 +0100718void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000719{
Simon Kelley44a2a312004-03-10 20:04:35 +0000720 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000721 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100722 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100723 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000724 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100725 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100726 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100727 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000728 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100729
730 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000731
732 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100733 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100734 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100735 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100736 }
737
Simon Kelley849a8352006-06-09 21:02:31 +0100738 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100739 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100740 {
741 tmp = config->next;
742 if (config->flags & CONFIG_FROM_ETHERS)
743 {
744 *up = tmp;
745 /* cannot have a clid */
746 if (config->flags & CONFIG_NAME)
747 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000748 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100749 free(config);
750 }
751 else
752 up = &config->next;
753 }
754
Simon Kelley44a2a312004-03-10 20:04:35 +0000755 while (fgets(buff, MAXDNAME, f))
756 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100757 char *host = NULL;
758
Simon Kelleyb8187c82005-11-26 21:46:27 +0000759 lineno++;
760
Simon Kelley824af852008-02-12 20:43:05 +0000761 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000762 buff[strlen(buff)-1] = 0;
763
Simon Kelley73a08a22009-02-05 20:28:08 +0000764 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000765 continue;
766
Simon Kelley824af852008-02-12 20:43:05 +0000767 for (ip = buff; *ip && !isspace((int)*ip); ip++);
768 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000769 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000770 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000771 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100772 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000773 continue;
774 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000775
776 /* check for name or dotted-quad */
777 for (cp = ip; *cp; cp++)
778 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
779 break;
780
781 if (!*cp)
782 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000783 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000784 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100785 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000786 continue;
787 }
788
Simon Kelley33820b72004-04-03 21:10:00 +0100789 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000790
Simon Kelley16972692006-10-16 20:04:18 +0100791 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100792 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000793 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000794 }
795 else
796 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100797 int nomem;
798 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000799 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100800 if (!nomem)
801 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
802 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000803 continue;
804 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100805
Simon Kelley33820b72004-04-03 21:10:00 +0100806 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000807
Simon Kelley16972692006-10-16 20:04:18 +0100808 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100809 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000810 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000811 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100812
813 if (config && (config->flags & CONFIG_FROM_ETHERS))
814 {
815 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
816 continue;
817 }
818
Simon Kelley1cff1662004-03-12 08:12:58 +0000819 if (!config)
820 {
Simon Kelley16972692006-10-16 20:04:18 +0100821 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000822 {
823 struct hwaddr_config *conf_addr = config->hwaddr;
824 if (conf_addr &&
825 conf_addr->next == NULL &&
826 conf_addr->wildcard_mask == 0 &&
827 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
828 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
829 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
830 break;
831 }
Simon Kelley33820b72004-04-03 21:10:00 +0100832
833 if (!config)
834 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100835 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100836 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100837 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000838 config->hwaddr = NULL;
839 config->domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100840 config->next = daemon->dhcp_conf;
841 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100842 }
843
844 config->flags |= flags;
845
846 if (flags & CONFIG_NAME)
847 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100848 config->hostname = host;
849 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100850 }
851
852 if (flags & CONFIG_ADDR)
853 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000854 }
Simon Kelley33820b72004-04-03 21:10:00 +0100855
Simon Kelley9009d742008-11-14 20:04:27 +0000856 config->flags |= CONFIG_NOCLID;
857 if (!config->hwaddr)
858 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
859 if (config->hwaddr)
860 {
861 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
862 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
863 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
864 config->hwaddr->wildcard_mask = 0;
865 config->hwaddr->next = NULL;
866 }
Simon Kelley33820b72004-04-03 21:10:00 +0100867 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100868
869 free(host);
870
Simon Kelley44a2a312004-03-10 20:04:35 +0000871 }
872
873 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100874
Simon Kelley7622fc02009-06-04 20:32:05 +0100875 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000876}
877
Simon Kelley824af852008-02-12 20:43:05 +0000878void check_dhcp_hosts(int fatal)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100879{
Simon Kelley824af852008-02-12 20:43:05 +0000880 /* If the same IP appears in more than one host config, then DISCOVER
881 for one of the hosts will get the address, but REQUEST will be NAKed,
882 since the address is reserved by the other one -> protocol loop.
883 Also check that FQDNs match the domain we are using. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100884
Simon Kelley824af852008-02-12 20:43:05 +0000885 struct dhcp_config *configs, *cp;
886
887 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100888 {
Simon Kelley824af852008-02-12 20:43:05 +0000889 char *domain;
890
891 if ((configs->flags & DHOPT_BANK) || fatal)
892 {
893 for (cp = configs->next; cp; cp = cp->next)
894 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
895 {
896 if (fatal)
897 die(_("duplicate IP address %s in dhcp-config directive."),
898 inet_ntoa(cp->addr), EC_BADCONF);
899 else
Simon Kelley7622fc02009-06-04 20:32:05 +0100900 my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
Simon Kelley824af852008-02-12 20:43:05 +0000901 inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
902 configs->flags &= ~CONFIG_ADDR;
903 }
904
Simon Kelley9009d742008-11-14 20:04:27 +0000905 /* split off domain part */
Simon Kelley824af852008-02-12 20:43:05 +0000906 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
Simon Kelley9009d742008-11-14 20:04:27 +0000907 configs->domain = domain;
Simon Kelley824af852008-02-12 20:43:05 +0000908 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100909 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100910}
911
Simon Kelley44a2a312004-03-10 20:04:35 +0000912void dhcp_update_configs(struct dhcp_config *configs)
913{
914 /* Some people like to keep all static IP addresses in /etc/hosts.
915 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100916 records which don't have an address and whose name matches.
917 We take care to maintain the invariant that any IP address can appear
Simon Kelley849a8352006-06-09 21:02:31 +0100918 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
919 restore the status-quo ante first. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000920
Simon Kelley1ab84e22004-01-29 16:48:35 +0000921 struct dhcp_config *config;
922 struct crec *crec;
Simon Kelley849a8352006-06-09 21:02:31 +0100923
924 for (config = configs; config; config = config->next)
925 if (config->flags & CONFIG_ADDR_HOSTS)
926 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
Simon Kelley44a2a312004-03-10 20:04:35 +0000927
Simon Kelley824af852008-02-12 20:43:05 +0000928
929 if (daemon->port != 0)
930 for (config = configs; config; config = config->next)
931 if (!(config->flags & CONFIG_ADDR) &&
932 (config->flags & CONFIG_NAME) &&
933 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
934 (crec->flags & F_HOSTS))
935 {
936 if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
937 {
938 /* use primary (first) address */
939 while (crec && !(crec->flags & F_REVERSE))
940 crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
941 if (!crec)
942 continue; /* should be never */
Simon Kelley7622fc02009-06-04 20:32:05 +0100943 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
Simon Kelley824af852008-02-12 20:43:05 +0000944 config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
945 }
946
947 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
Simon Kelley7622fc02009-06-04 20:32:05 +0100948 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
Simon Kelley824af852008-02-12 20:43:05 +0000949 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
950 else
951 {
952 config->addr = crec->addr.addr.addr.addr4;
953 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
954 }
955 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000956}
Simon Kelley44a2a312004-03-10 20:04:35 +0000957
Simon Kelleybb01cb92004-12-13 20:56:23 +0000958/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
959 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100960 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
961 so check here that the domain name is legal as a hostname. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100962char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000963{
Simon Kelley824af852008-02-12 20:43:05 +0000964 struct crec *lookup;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000965 char *hostname = NULL;
Simon Kelley9009d742008-11-14 20:04:27 +0000966 char *d1, *d2;
Simon Kelley824af852008-02-12 20:43:05 +0000967
968 if (daemon->port == 0)
969 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000970
Simon Kelley824af852008-02-12 20:43:05 +0000971 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000972 if (lookup && (lookup->flags & F_HOSTS))
973 {
974 hostname = daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000975 strncpy(hostname, cache_get_name(lookup), 256);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100976 hostname[255] = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000977 d1 = strip_hostname(hostname);
978 d2 = get_domain(addr);
Simon Kelley1f15b812009-10-13 17:49:32 +0100979 if (!legal_hostname(hostname) || (d1 && (!d2 || !hostname_isequal(d1, d2))))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100980 hostname = NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000981 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100982
Simon Kelleybb01cb92004-12-13 20:56:23 +0000983 return hostname;
984}
985
Simon Kelley9009d742008-11-14 20:04:27 +0000986/* return domain or NULL if none. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100987char *strip_hostname(char *hostname)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000988{
989 char *dot = strchr(hostname, '.');
Simon Kelley5aabfc72007-08-29 11:24:47 +0100990
991 if (!dot)
992 return NULL;
993
994 *dot = 0; /* truncate */
Simon Kelley9009d742008-11-14 20:04:27 +0000995 if (strlen(dot+1) != 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100996 return dot+1;
997
998 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000999}
Simon Kelley9009d742008-11-14 20:04:27 +00001000
Simon Kelley7622fc02009-06-04 20:32:05 +01001001#endif
Simon Kelley9009d742008-11-14 20:04:27 +00001002