blob: dcd76d4f1a5c757c0e7bcc3e3a162c6ddf7d5c11 [file] [log] [blame]
Simon Kelley824af852008-02-12 20:43:05 +00001/* dnsmasq is Copyright (c) 2000-2007 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
13 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 Kelley5e9e0ef2006-04-17 14:24:29 +010019struct iface_param {
20 struct in_addr relay, primary;
21 struct dhcp_context *current;
22 int ind;
23};
24
Simon Kelley5aabfc72007-08-29 11:24:47 +010025static int complete_context(struct in_addr local, int if_index,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010026 struct in_addr netmask, struct in_addr broadcast, void *vparam);
27
Simon Kelley5aabfc72007-08-29 11:24:47 +010028void dhcp_init(void)
Simon Kelley44a2a312004-03-10 20:04:35 +000029{
30 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
31 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010032 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000033#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
34 int mtu = IP_PMTUDISC_DONT;
35#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010036
Simon Kelley44a2a312004-03-10 20:04:35 +000037 if (fd == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010038 die (_("cannot create DHCP socket : %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000039
Simon Kelley824af852008-02-12 20:43:05 +000040 if (!fix_fd(fd) ||
41#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
42 setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
43#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010044#if defined(HAVE_LINUX_NETWORK)
Simon Kelley3be34542004-09-11 19:12:13 +010045 setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000046#elif defined(IP_RECVIF)
Simon Kelley3be34542004-09-11 19:12:13 +010047 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000048#endif
Simon Kelley3be34542004-09-11 19:12:13 +010049 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010050 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000051
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000052 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010053 instance binding port 67. That's OK if they serve different networks.
54 Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD.
55 OpenBSD <= 4.0 screws up IP_RECVIF when SO_REUSEPORT is set, but
56 OpenBSD <= 3.9 doesn't have IP_RECVIF anyway, so we just have to elide
57 this for OpenBSD 4.0, if you want more than one instance on oBSD4.0, tough. */
58
59#ifndef OpenBSD4_0
60 if (daemon->options & OPT_NOWILD)
61 {
62#ifdef SO_REUSEPORT
63 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
64#else
65 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
66#endif
67 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010068 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010069 }
70#endif
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000071
Simon Kelley849a8352006-06-09 21:02:31 +010072 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000073 saddr.sin_family = AF_INET;
74 saddr.sin_port = htons(DHCP_SERVER_PORT);
75 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010076#ifdef HAVE_SOCKADDR_SA_LEN
77 saddr.sin_len = sizeof(struct sockaddr_in);
78#endif
79
Simon Kelley44a2a312004-03-10 20:04:35 +000080 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010081 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000082
Simon Kelley3be34542004-09-11 19:12:13 +010083 daemon->dhcpfd = fd;
84
Simon Kelley824af852008-02-12 20:43:05 +000085#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010086 /* When we're not using capabilities, we need to do this here before
87 we drop root. Also, set buffer size small, to avoid wasting
88 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +000089
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010090 if (daemon->options & OPT_NO_PING)
91 daemon->dhcp_icmp_fd = -1;
92 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
93 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +010094 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010095
96 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +010097 init_bpf();
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010098#endif
Simon Kelley3be34542004-09-11 19:12:13 +010099
Simon Kelley824af852008-02-12 20:43:05 +0000100 check_dhcp_hosts(1);
101
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100102 daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
103 daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000104}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100105
Simon Kelley5aabfc72007-08-29 11:24:47 +0100106void dhcp_packet(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000107{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100108 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000109 struct dhcp_context *context;
110 struct iname *tmp;
111 struct ifreq ifr;
112 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100113 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000114 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 struct iovec iov;
116 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100117 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000118 struct in_addr iface_addr, *addrp = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119 struct iface_param parm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000120
Simon Kelley44a2a312004-03-10 20:04:35 +0000121 union {
122 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000123#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000124 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000125#elif defined(HAVE_SOLARIS_NETWORK)
126 char control[CMSG_SPACE(sizeof(unsigned int))];
127#elif defined(IP_RECVIF)
Simon Kelley44a2a312004-03-10 20:04:35 +0000128 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
129#endif
130 } control_u;
131
Simon Kelley4011c4e2006-10-28 16:26:19 +0100132 msg.msg_control = NULL;
133 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000134 msg.msg_name = NULL;
135 msg.msg_namelen = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100136 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000137 msg.msg_iovlen = 1;
138
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100139 do
140 {
141 msg.msg_flags = 0;
142 while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
143 }
144 while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
145 expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
Simon Kelley44a2a312004-03-10 20:04:35 +0000146
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100147 /* expand_buf may have moved buffer */
Simon Kelley824af852008-02-12 20:43:05 +0000148 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100149 msg.msg_controllen = sizeof(control_u);
Simon Kelley4011c4e2006-10-28 16:26:19 +0100150 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100151 msg.msg_flags = 0;
152 msg.msg_name = &dest;
153 msg.msg_namelen = sizeof(dest);
154
Simon Kelley16972692006-10-16 20:04:18 +0100155 while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100156
Simon Kelley4011c4e2006-10-28 16:26:19 +0100157 if (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000158 return;
159
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100160#if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100161 if (msg.msg_controllen >= sizeof(struct cmsghdr))
162 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
163 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
164 {
165 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
166 if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
167 unicast_dest = 1;
168 }
169
Simon Kelley8a911cc2004-03-16 18:35:52 +0000170 if (!(ifr.ifr_ifindex = iface_index) ||
Simon Kelley3be34542004-09-11 19:12:13 +0100171 ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000172 return;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000173
Simon Kelley44a2a312004-03-10 20:04:35 +0000174#elif defined(IP_RECVIF)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100175 if (msg.msg_controllen >= sizeof(struct cmsghdr))
176 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
177 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley824af852008-02-12 20:43:05 +0000178#ifdef HAVE_SOLARIS_NETWORK
179 iface_index = *((unsigned int *)CMSG_DATA(cmptr));
180#else
181 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
182#endif
183
Simon Kelley44a2a312004-03-10 20:04:35 +0000184 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
185 return;
Simon Kelley4011c4e2006-10-28 16:26:19 +0100186
187#ifdef MSG_BCAST
188 /* OpenBSD tells us when a packet was broadcast */
189 if (!(msg.msg_flags & MSG_BCAST))
190 unicast_dest = 1;
191#endif
Simon Kelley832af0b2007-01-21 20:01:28 +0000192
Simon Kelley44a2a312004-03-10 20:04:35 +0000193#else
Simon Kelley4011c4e2006-10-28 16:26:19 +0100194 /* fallback for systems without IP_RECVIF - allow only one interface
195 and assume packets arrive from it - yuk. */
Simon Kelley3be34542004-09-11 19:12:13 +0100196 {
197 struct iname *name;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100198 for (name = daemon->if_names; name->isloop; name = name->next);
Simon Kelley3be34542004-09-11 19:12:13 +0100199 strcpy(ifr.ifr_name, name->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100200 iface_index = if_nametoindex(name->name);
Simon Kelley3be34542004-09-11 19:12:13 +0100201 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000202#endif
Simon Kelley824af852008-02-12 20:43:05 +0000203
Simon Kelley44a2a312004-03-10 20:04:35 +0000204 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000205 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
206 {
207 addrp = &iface_addr;
208 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
209 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000210
Simon Kelley5aabfc72007-08-29 11:24:47 +0100211 if (!iface_check(AF_INET, (struct all_addr *)addrp, &ifr, &iface_index))
Simon Kelley832af0b2007-01-21 20:01:28 +0000212 return;
213
Simon Kelley3d8df262005-08-29 12:19:27 +0100214 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
215 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
216 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000217
Simon Kelley832af0b2007-01-21 20:01:28 +0000218 /* interface may have been changed by alias in iface_check */
219 if (!addrp)
220 {
221 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1)
222 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100223 my_syslog(LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
Simon Kelley832af0b2007-01-21 20:01:28 +0000224 return;
225 }
226 else
227 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
228 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000229
Simon Kelley91dccd02005-03-31 17:48:32 +0100230 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100231 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100232 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000233
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100234 parm.relay = mess->giaddr;
235 parm.primary = iface_addr;
236 parm.current = NULL;
237 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000238
Simon Kelley5aabfc72007-08-29 11:24:47 +0100239 if (!iface_enumerate(&parm, complete_context, NULL))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100240 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000241 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000242 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100243 now, unicast_dest, &is_inform);
244 lease_update_file(now);
245 lease_update_dns();
Simon Kelley16972692006-10-16 20:04:18 +0100246
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100247 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000248 return;
249
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100250 msg.msg_name = &dest;
251 msg.msg_namelen = sizeof(dest);
252 msg.msg_control = NULL;
253 msg.msg_controllen = 0;
254 msg.msg_iov = &iov;
255 iov.iov_base = daemon->dhcp_packet.iov_base;
256
257 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000258 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100259
Simon Kelley3be34542004-09-11 19:12:13 +0100260#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100261 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100262#endif
263
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100264 if (mess->giaddr.s_addr)
265 {
266 /* Send to BOOTP relay */
Simon Kelley208b65c2006-08-05 21:41:37 +0100267 dest.sin_port = htons(DHCP_SERVER_PORT);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100268 dest.sin_addr = mess->giaddr;
269 }
270 else if (mess->ciaddr.s_addr)
271 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100272 /* If the client's idea of its own address tallys with
273 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100274 source port too, and send back to that. If we're replying
275 to a DHCPINFORM, trust the source address always. */
276 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
277 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100278 {
279 dest.sin_port = htons(DHCP_CLIENT_PORT);
280 dest.sin_addr = mess->ciaddr;
281 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100282 }
Simon Kelley824af852008-02-12 20:43:05 +0000283#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100284 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
285 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100286 {
Simon Kelley849a8352006-06-09 21:02:31 +0100287 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100288 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100289 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100290 msg.msg_controllen = sizeof(control_u);
291 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100292 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;
Simon Kelley824af852008-02-12 20:43:05 +0000297 cmptr->cmsg_type = IP_PKTINFO;
298 dest.sin_addr.s_addr = INADDR_BROADCAST;
299 dest.sin_port = htons(DHCP_CLIENT_PORT);
Simon Kelley44a2a312004-03-10 20:04:35 +0000300 }
301 else
302 {
Simon Kelley849a8352006-06-09 21:02:31 +0100303 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
304 struct sockaddr limits size to 14 bytes. */
305 struct arpreq req;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100306 dest.sin_addr = mess->yiaddr;
307 dest.sin_port = htons(DHCP_CLIENT_PORT);
Simon Kelley849a8352006-06-09 21:02:31 +0100308 *((struct sockaddr_in *)&req.arp_pa) = dest;
309 req.arp_ha.sa_family = mess->htype;
310 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
311 strncpy(req.arp_dev, ifr.ifr_name, 16);
312 req.arp_flags = ATF_COM;
313 ioctl(daemon->dhcpfd, SIOCSARP, &req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000314 }
Simon Kelley824af852008-02-12 20:43:05 +0000315#elif defined(HAVE_SOLARIS_NETWORK)
316 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
317 {
318 /* broadcast to 255.255.255.255 (or mac address invalid) */
319 dest.sin_addr.s_addr = INADDR_BROADCAST;
320 dest.sin_port = htons(DHCP_CLIENT_PORT);
321 /* note that we don't specify the interface here: that's done by the
322 IP_XMIT_IF sockopt lower down. */
323 }
324 else
325 {
326 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
327 Note that this only works for ethernet on solaris, because we use SIOCSARP
328 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
329 mysteriously. Bah. Fall back to broadcast for other net types. */
330 struct arpreq req;
331 dest.sin_addr = mess->yiaddr;
332 dest.sin_port = htons(DHCP_CLIENT_PORT);
333 *((struct sockaddr_in *)&req.arp_pa) = dest;
334 req.arp_ha.sa_family = AF_UNSPEC;
335 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
336 req.arp_flags = ATF_COM;
337 ioctl(daemon->dhcpfd, SIOCSARP, &req);
338 }
339#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100340 else
341 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100342 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100343 return;
344 }
345#endif
346
Simon Kelley824af852008-02-12 20:43:05 +0000347#ifdef HAVE_SOLARIS_NETWORK
348 setsockopt(daemon->dhcpfd, IPPROTO_IP, IP_XMIT_IF, &iface_index, sizeof(iface_index));
349#endif
350
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100351 while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000352}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100353
Simon Kelley0a852542005-03-23 20:28:59 +0000354/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100355 of each interface (and any relay address) and does the following things:
356
357 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
358 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
359 3) Fills in local (this host) and router (this host or relay) addresses.
360 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
361
Simon Kelley0a852542005-03-23 20:28:59 +0000362 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100363
Simon Kelley5aabfc72007-08-29 11:24:47 +0100364static int complete_context(struct in_addr local, int if_index,
365 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000366{
367 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100368 struct iface_param *param = vparam;
369
Simon Kelley0a852542005-03-23 20:28:59 +0000370 for (context = daemon->dhcp; context; context = context->next)
371 {
372 if (!(context->flags & CONTEXT_NETMASK) &&
373 (is_same_net(local, context->start, netmask) ||
374 is_same_net(local, context->end, netmask)))
375 {
376 if (context->netmask.s_addr != netmask.s_addr &&
377 !(is_same_net(local, context->start, netmask) &&
378 is_same_net(local, context->end, netmask)))
379 {
380 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
381 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100382 my_syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
383 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000384 }
385 context->netmask = netmask;
386 }
387
388 if (context->netmask.s_addr)
389 {
390 if (is_same_net(local, context->start, context->netmask) &&
391 is_same_net(local, context->end, context->netmask))
392 {
Simon Kelley91dccd02005-03-31 17:48:32 +0100393 /* link it onto the current chain if we've not seen it before */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100394 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000395 {
396 context->router = local;
397 context->local = local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100398 context->current = param->current;
399 param->current = context;
Simon Kelley0a852542005-03-23 20:28:59 +0000400 }
401
402 if (!(context->flags & CONTEXT_BRDCAST))
403 {
404 if (is_same_net(broadcast, context->start, context->netmask))
405 context->broadcast = broadcast;
406 else
407 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
408 }
409 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100410 else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000411 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100412 context->router = param->relay;
413 context->local = param->primary;
Simon Kelley0a852542005-03-23 20:28:59 +0000414 /* fill in missing broadcast addresses for relayed ranges */
415 if (!(context->flags & CONTEXT_BRDCAST))
416 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
417 }
418
419 }
420 }
421
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100422 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000423}
424
Simon Kelley824af852008-02-12 20:43:05 +0000425struct dhcp_context *address_available(struct dhcp_context *context,
426 struct in_addr taddr,
427 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000428{
Simon Kelley36717ee2004-09-20 19:20:58 +0100429 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100430 possible ranges. Make sure that the address isn't in use
431 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000432
Simon Kelley36717ee2004-09-20 19:20:58 +0100433 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100434 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100435
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100436 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100437 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100438 return NULL;
439
440 for (tmp = context; tmp; tmp = tmp->current)
441 {
442 start = ntohl(tmp->start.s_addr);
443 end = ntohl(tmp->end.s_addr);
444
445 if (!(tmp->flags & CONTEXT_STATIC) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100446 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000447 addr <= end &&
448 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100449 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100450 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000451
Simon Kelley59353a62004-11-21 19:34:28 +0000452 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000453}
Simon Kelley59353a62004-11-21 19:34:28 +0000454
Simon Kelley824af852008-02-12 20:43:05 +0000455struct dhcp_context *narrow_context(struct dhcp_context *context,
456 struct in_addr taddr,
457 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000458{
Simon Kelleye17fb622006-01-14 20:33:46 +0000459 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000460 These are chained on ->current.
461 Here we have an address, and return the actual context correponding to that
462 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000463 any dhcp-range. In that case we return a static range if possible, or failing that,
464 any context on the correct subnet. (If there's more than one, this is a dodgy
465 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000466
Simon Kelleye17fb622006-01-14 20:33:46 +0000467 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000468
Simon Kelley824af852008-02-12 20:43:05 +0000469 if (!(tmp = address_available(context, taddr, netids)))
470 {
471 for (tmp = context; tmp; tmp = tmp->current)
472 if (is_same_net(taddr, tmp->start, tmp->netmask) &&
473 (tmp->flags & CONTEXT_STATIC))
474 break;
475
476 if (!tmp)
477 for (tmp = context; tmp; tmp = tmp->current)
478 if (is_same_net(taddr, tmp->start, tmp->netmask))
479 break;
480 }
Simon Kelley59353a62004-11-21 19:34:28 +0000481
Simon Kelley824af852008-02-12 20:43:05 +0000482 /* Only one context allowed now */
483 if (tmp)
484 tmp->current = NULL;
485
486 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000487}
488
Simon Kelleydfa666f2004-08-02 18:27:27 +0100489struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
490{
491 struct dhcp_config *config;
492
493 for (config = configs; config; config = config->next)
494 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
495 return config;
496
497 return NULL;
498}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000499
Simon Kelleycdeda282006-03-16 20:16:06 +0000500/* Is every member of check matched by a member of pool?
Simon Kelley824af852008-02-12 20:43:05 +0000501 If tagnotneeded, untagged is OK */
502int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000503{
504 struct dhcp_netid *tmp1;
505
Simon Kelley824af852008-02-12 20:43:05 +0000506 if (!check && !tagnotneeded)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000507 return 0;
508
509 for (; check; check = check->next)
510 {
511 if (check->net[0] != '#')
512 {
513 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
514 if (strcmp(check->net, tmp1->net) == 0)
515 break;
Simon Kelley824af852008-02-12 20:43:05 +0000516 if (!tmp1)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000517 return 0;
518 }
519 else
520 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
521 if (strcmp((check->net)+1, tmp1->net) == 0)
522 return 0;
523 }
524 return 1;
525}
526
Simon Kelley5aabfc72007-08-29 11:24:47 +0100527int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000528 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100529 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000530{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100531 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000532 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000533 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000534
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100535 struct in_addr start, addr;
536 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000537 int i, pass;
538 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100539
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100540 /* hash hwaddr */
541 for (j = 0, i = 0; i < hw_len; i++)
542 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
543
Simon Kelleycdeda282006-03-16 20:16:06 +0000544 for (pass = 0; pass <= 1; pass++)
545 for (c = context; c; c = c->current)
546 if (c->flags & CONTEXT_STATIC)
547 continue;
548 else if (!match_netid(c->filter, netids, pass))
549 continue;
550 else
551 {
552 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000553 start.s_addr = addr.s_addr =
554 htonl(ntohl(c->start.s_addr) +
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100555 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
Simon Kelleycdeda282006-03-16 20:16:06 +0000556
557 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100558 /* eliminate addresses in use by the server. */
559 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100560 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100561 break;
562
563 if (!d &&
564 !lease_find_by_addr(addr) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000565 !config_find_by_address(daemon->dhcp_conf, addr))
566 {
567 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100568 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
569 ((float)PING_WAIT)));
570
571 *addrp = addr;
572
573 if (daemon->options & OPT_NO_PING)
574 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000575
576 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100577 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000578 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100579 on us repeatedly. As a final check, if we did more
580 than 60% of the possible ping checks in the last
581 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000582 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100583 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000584 victim = r; /* old record */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100585 else if (++count == max || r->addr.s_addr == addr.s_addr)
586 return 1;
587
Simon Kelley5aabfc72007-08-29 11:24:47 +0100588 if (icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000589 /* address in use: perturb address selection so that we are
590 less likely to try this address again. */
591 c->addr_epoch++;
592 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100593 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000594 /* at this point victim may hold an expired record */
595 if (!victim)
596 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100597 if ((victim = whine_malloc(sizeof(struct ping_result))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000598 {
599 victim->next = daemon->ping_results;
600 daemon->ping_results = victim;
601 }
602 }
603
604 /* record that this address is OK for 30s
605 without more ping checks */
606 if (victim)
607 {
608 victim->addr = addr;
609 victim->time = now;
610 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100611 return 1;
612 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000613 }
Simon Kelley3be34542004-09-11 19:12:13 +0100614
Simon Kelleycdeda282006-03-16 20:16:06 +0000615 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
616
617 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
618 addr = c->start;
619
620 } while (addr.s_addr != start.s_addr);
621 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000622 return 0;
623}
624
625static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
626{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000627 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000628 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100629 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000630 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000631 for (; context; context = context->current)
632 if (is_same_net(config->addr, context->start, context->netmask))
633 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000634
635 return 0;
636}
637
Simon Kelley0a852542005-03-23 20:28:59 +0000638
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000639struct dhcp_config *find_config(struct dhcp_config *configs,
640 struct dhcp_context *context,
641 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000642 unsigned char *hwaddr, int hw_len,
643 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644{
645 struct dhcp_config *config;
646
Simon Kelley0a852542005-03-23 20:28:59 +0000647 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000648 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100649 if (config->flags & CONFIG_CLID)
650 {
651 if (config->clid_len == clid_len &&
652 memcmp(config->clid, clid, clid_len) == 0 &&
653 is_addr_in_context(context, config))
654 return config;
655
656 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
657 cope with that here */
658 if (*clid == 0 && config->clid_len == clid_len-1 &&
659 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
660 is_addr_in_context(context, config))
661 return config;
662 }
663
Simon Kelley0a852542005-03-23 20:28:59 +0000664
Simon Kelleycdeda282006-03-16 20:16:06 +0000665 for (config = configs; config; config = config->next)
666 if ((config->flags & CONFIG_HWADDR) &&
667 config->wildcard_mask == 0 &&
668 config->hwaddr_len == hw_len &&
669 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
670 memcmp(config->hwaddr, hwaddr, hw_len) == 0 &&
671 is_addr_in_context(context, config))
672 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100673
Simon Kelley0a852542005-03-23 20:28:59 +0000674 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000675 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100676 if ((config->flags & CONFIG_NAME) &&
677 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000678 is_addr_in_context(context, config))
679 return config;
680
Simon Kelleycdeda282006-03-16 20:16:06 +0000681 for (config = configs; config; config = config->next)
682 if ((config->flags & CONFIG_HWADDR) &&
683 config->wildcard_mask != 0 &&
684 config->hwaddr_len == hw_len &&
685 (config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100686 is_addr_in_context(context, config) &&
687 memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
688 return config;
689
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000690 return NULL;
691}
692
Simon Kelley5aabfc72007-08-29 11:24:47 +0100693void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000694{
Simon Kelley44a2a312004-03-10 20:04:35 +0000695 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000696 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100697 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100698 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000699 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100700 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100701 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100702 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000703 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100704
705 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000706
707 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100708 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100709 my_syslog(LOG_ERR, _("failed to read %s:%s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100710 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100711 }
712
Simon Kelley849a8352006-06-09 21:02:31 +0100713 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100714 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100715 {
716 tmp = config->next;
717 if (config->flags & CONFIG_FROM_ETHERS)
718 {
719 *up = tmp;
720 /* cannot have a clid */
721 if (config->flags & CONFIG_NAME)
722 free(config->hostname);
723 free(config);
724 }
725 else
726 up = &config->next;
727 }
728
Simon Kelley44a2a312004-03-10 20:04:35 +0000729 while (fgets(buff, MAXDNAME, f))
730 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000731 lineno++;
732
Simon Kelley824af852008-02-12 20:43:05 +0000733 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000734 buff[strlen(buff)-1] = 0;
735
736 if ((*buff == '#') || (*buff == '+'))
737 continue;
738
Simon Kelley824af852008-02-12 20:43:05 +0000739 for (ip = buff; *ip && !isspace((int)*ip); ip++);
740 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000741 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000742 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000743 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100744 my_syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000745 continue;
746 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000747
748 /* check for name or dotted-quad */
749 for (cp = ip; *cp; cp++)
750 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
751 break;
752
753 if (!*cp)
754 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000755 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000756 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100757 my_syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000758 continue;
759 }
760
Simon Kelley33820b72004-04-03 21:10:00 +0100761 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000762
Simon Kelley16972692006-10-16 20:04:18 +0100763 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100764 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000765 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000766 }
767 else
768 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100769 if (!canonicalise(ip) || strip_hostname(ip))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000770 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100771 my_syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000772 continue;
773 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100774
Simon Kelley33820b72004-04-03 21:10:00 +0100775 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000776
Simon Kelley16972692006-10-16 20:04:18 +0100777 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100778 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000779 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000780 }
781
Simon Kelley1cff1662004-03-12 08:12:58 +0000782 if (!config)
783 {
Simon Kelley16972692006-10-16 20:04:18 +0100784 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100785 if ((config->flags & CONFIG_HWADDR) &&
Simon Kelley91dccd02005-03-31 17:48:32 +0100786 config->wildcard_mask == 0 &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000787 config->hwaddr_len == ETHER_ADDR_LEN &&
788 (config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100789 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
790 break;
791
792 if (!config)
793 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100794 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100795 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100796 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley91dccd02005-03-31 17:48:32 +0100797 config->wildcard_mask = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100798 config->next = daemon->dhcp_conf;
799 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100800 }
801
802 config->flags |= flags;
803
804 if (flags & CONFIG_NAME)
805 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100806 if ((config->hostname = whine_malloc(strlen(ip)+1)))
Simon Kelley33820b72004-04-03 21:10:00 +0100807 strcpy(config->hostname, ip);
808 else
809 config->flags &= ~CONFIG_NAME;
810 }
811
812 if (flags & CONFIG_ADDR)
813 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000814 }
Simon Kelley33820b72004-04-03 21:10:00 +0100815
Simon Kelleyde379512004-06-22 20:23:33 +0100816 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100817 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelleycdeda282006-03-16 20:16:06 +0000818 config->hwaddr_len = ETHER_ADDR_LEN;
819 config->hwaddr_type = ARPHRD_ETHER;
Simon Kelley33820b72004-04-03 21:10:00 +0100820 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000821 }
822
823 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100824
Simon Kelleyf2621c72007-04-29 19:47:21 +0100825 my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000826}
827
Simon Kelley824af852008-02-12 20:43:05 +0000828void check_dhcp_hosts(int fatal)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100829{
Simon Kelley824af852008-02-12 20:43:05 +0000830 /* If the same IP appears in more than one host config, then DISCOVER
831 for one of the hosts will get the address, but REQUEST will be NAKed,
832 since the address is reserved by the other one -> protocol loop.
833 Also check that FQDNs match the domain we are using. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100834
Simon Kelley824af852008-02-12 20:43:05 +0000835 struct dhcp_config *configs, *cp;
836
837 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100838 {
Simon Kelley824af852008-02-12 20:43:05 +0000839 char *domain;
840
841 if ((configs->flags & DHOPT_BANK) || fatal)
842 {
843 for (cp = configs->next; cp; cp = cp->next)
844 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
845 {
846 if (fatal)
847 die(_("duplicate IP address %s in dhcp-config directive."),
848 inet_ntoa(cp->addr), EC_BADCONF);
849 else
850 my_syslog(LOG_ERR, _("duplicate IP address %s in %s."),
851 inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
852 configs->flags &= ~CONFIG_ADDR;
853 }
854
855 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
856 {
857 if (fatal)
858 die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
859 else
860 my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
861 free(configs->hostname);
862 configs->flags &= ~CONFIG_NAME;
863 }
864 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100865 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100866}
867
Simon Kelley44a2a312004-03-10 20:04:35 +0000868void dhcp_update_configs(struct dhcp_config *configs)
869{
870 /* Some people like to keep all static IP addresses in /etc/hosts.
871 This goes through /etc/hosts and sets static addresses for any DHCP config
Simon Kelley3d8df262005-08-29 12:19:27 +0100872 records which don't have an address and whose name matches.
873 We take care to maintain the invariant that any IP address can appear
Simon Kelley849a8352006-06-09 21:02:31 +0100874 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
875 restore the status-quo ante first. */
Simon Kelley44a2a312004-03-10 20:04:35 +0000876
Simon Kelley1ab84e22004-01-29 16:48:35 +0000877 struct dhcp_config *config;
878 struct crec *crec;
Simon Kelley849a8352006-06-09 21:02:31 +0100879
880 for (config = configs; config; config = config->next)
881 if (config->flags & CONFIG_ADDR_HOSTS)
882 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
Simon Kelley44a2a312004-03-10 20:04:35 +0000883
Simon Kelley824af852008-02-12 20:43:05 +0000884
885 if (daemon->port != 0)
886 for (config = configs; config; config = config->next)
887 if (!(config->flags & CONFIG_ADDR) &&
888 (config->flags & CONFIG_NAME) &&
889 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
890 (crec->flags & F_HOSTS))
891 {
892 if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
893 {
894 /* use primary (first) address */
895 while (crec && !(crec->flags & F_REVERSE))
896 crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
897 if (!crec)
898 continue; /* should be never */
899 my_syslog(LOG_WARNING, _("%s has more then one address in hostsfile, using %s for DHCP"),
900 config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
901 }
902
903 if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
904 my_syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
905 inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
906 else
907 {
908 config->addr = crec->addr.addr.addr.addr4;
909 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
910 }
911 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000912}
Simon Kelley44a2a312004-03-10 20:04:35 +0000913
Simon Kelleybb01cb92004-12-13 20:56:23 +0000914/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
915 for this address. If it has a domain part, that must match the set domain and
916 it gets stripped. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100917char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000918{
Simon Kelley824af852008-02-12 20:43:05 +0000919 struct crec *lookup;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000920 char *hostname = NULL;
Simon Kelley824af852008-02-12 20:43:05 +0000921
922 if (daemon->port == 0)
923 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000924
Simon Kelley824af852008-02-12 20:43:05 +0000925 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000926 if (lookup && (lookup->flags & F_HOSTS))
927 {
928 hostname = daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000929 strncpy(hostname, cache_get_name(lookup), 256);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100930 hostname[255] = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100931 if (strip_hostname(hostname))
932 hostname = NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000933 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100934
Simon Kelleybb01cb92004-12-13 20:56:23 +0000935 return hostname;
936}
937
Simon Kelley5aabfc72007-08-29 11:24:47 +0100938/* return illegal domain or NULL if OK */
939char *strip_hostname(char *hostname)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000940{
941 char *dot = strchr(hostname, '.');
Simon Kelley5aabfc72007-08-29 11:24:47 +0100942
943 if (!dot)
944 return NULL;
945
946 *dot = 0; /* truncate */
947
948 if (*(dot+1) && (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)))
949 return dot+1;
950
951 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000952}