blob: ada1be8161369d5dc3ff6af6f7a5b5b6efcc513d [file] [log] [blame]
Simon Kelleyc49778d2016-01-06 18:52:33 +00001/* dnsmasq is Copyright (c) 2000-2016 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 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010022 struct dhcp_context *current;
Simon Kelleyff7eea22013-09-04 18:01:38 +010023 struct dhcp_relay *relay;
24 struct in_addr relay_local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010025 int ind;
26};
27
Simon Kelleyc72daea2012-01-05 21:33:27 +000028struct match_param {
29 int ind, matched;
30 struct in_addr netmask, broadcast, addr;
31};
32
Simon Kelley3f2873d2013-05-14 11:28:47 +010033static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010034 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelley3f2873d2013-05-14 11:28:47 +010035static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +000036 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelleyff7eea22013-09-04 18:01:38 +010037static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
38static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010039
Simon Kelley316e2732010-01-22 20:16:09 +000040static int make_fd(int port)
Simon Kelley44a2a312004-03-10 20:04:35 +000041{
42 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
43 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010044 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000045#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
46 int mtu = IP_PMTUDISC_DONT;
47#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000048#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
49 int tos = IPTOS_CLASS_CS6;
50#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010051
Simon Kelley44a2a312004-03-10 20:04:35 +000052 if (fd == -1)
Simon Kelley7622fc02009-06-04 20:32:05 +010053 die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000054
Simon Kelley824af852008-02-12 20:43:05 +000055 if (!fix_fd(fd) ||
56#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +000057 setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
58#endif
59#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
60 setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +000061#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010062#if defined(HAVE_LINUX_NETWORK)
Simon Kelleyc72daea2012-01-05 21:33:27 +000063 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley7622fc02009-06-04 20:32:05 +010064#else
Simon Kelley3be34542004-09-11 19:12:13 +010065 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000066#endif
Simon Kelley3be34542004-09-11 19:12:13 +010067 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010068 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000069
Josh Soref730c6742017-02-06 16:14:04 +000070 /* When bind-interfaces is set, there might be more than one dnsmasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010071 instance binding port 67. That's OK if they serve different networks.
Josh Soref730c6742017-02-06 16:14:04 +000072 Need to set REUSEADDR|REUSEPORT to make this possible.
Simon Kelley56a11422013-04-02 17:02:58 +010073 Handle the case that REUSEPORT is defined, but the kernel doesn't
74 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley54dd3932012-06-20 11:23:38 +010075 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley4011c4e2006-10-28 16:26:19 +010076 {
Simon Kelleyffbad342013-08-14 15:53:57 +010077 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010078
Simon Kelley4011c4e2006-10-28 16:26:19 +010079#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010080 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010081 errno == ENOPROTOOPT)
82 rc = 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +010083#endif
Simon Kelley56a11422013-04-02 17:02:58 +010084
Simon Kelleyffbad342013-08-14 15:53:57 +010085 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010086 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
87
Simon Kelley4011c4e2006-10-28 16:26:19 +010088 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010089 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010090 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000091
Simon Kelley849a8352006-06-09 21:02:31 +010092 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000093 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000094 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000095 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010096#ifdef HAVE_SOCKADDR_SA_LEN
97 saddr.sin_len = sizeof(struct sockaddr_in);
98#endif
99
Simon Kelley44a2a312004-03-10 20:04:35 +0000100 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100101 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +0000102
Simon Kelley316e2732010-01-22 20:16:09 +0000103 return fd;
104}
105
106void dhcp_init(void)
107{
108#if defined(HAVE_BSD_NETWORK)
109 int oneopt = 1;
110#endif
111
112 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
113 if (daemon->enable_pxe)
114 daemon->pxefd = make_fd(PXE_PORT);
115 else
116 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100117
Simon Kelley824af852008-02-12 20:43:05 +0000118#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119 /* When we're not using capabilities, we need to do this here before
120 we drop root. Also, set buffer size small, to avoid wasting
121 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000122
Simon Kelley28866e92011-02-14 20:19:14 +0000123 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100124 daemon->dhcp_icmp_fd = -1;
125 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
126 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100127 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100128
129 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100130 init_bpf();
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100131#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000132}
133
Simon Kelley316e2732010-01-22 20:16:09 +0000134void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000135{
Simon Kelley316e2732010-01-22 20:16:09 +0000136 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000138 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100139 struct dhcp_relay *relay;
140 int is_relay_reply = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000141 struct iname *tmp;
142 struct ifreq ifr;
143 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000145 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100146 struct iovec iov;
147 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100148 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Neil Jerramff325642016-05-03 22:45:14 +0100149 int rcvd_iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000150 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100151 struct iface_param parm;
Floris Bos503c6092017-04-09 23:07:13 +0100152 time_t recvtime = now;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100153#ifdef HAVE_LINUX_NETWORK
154 struct arpreq arp_req;
Floris Bos503c6092017-04-09 23:07:13 +0100155 struct timeval tv;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100156#endif
157
Simon Kelley44a2a312004-03-10 20:04:35 +0000158 union {
159 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000160#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000161 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000162#elif defined(HAVE_SOLARIS_NETWORK)
163 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100164#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000165 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
166#endif
167 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000168 struct dhcp_bridge *bridge, *alias;
169
170 msg.msg_controllen = sizeof(control_u);
171 msg.msg_control = control_u.control;
172 msg.msg_name = &dest;
173 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100174 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000175 msg.msg_iovlen = 1;
176
Simon Kelleyc72daea2012-01-05 21:33:27 +0000177 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
178 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000179 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000180
181 #if defined (HAVE_LINUX_NETWORK)
Floris Bos503c6092017-04-09 23:07:13 +0100182 if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
183 recvtime = tv.tv_sec;
184
Simon Kelley4011c4e2006-10-28 16:26:19 +0100185 if (msg.msg_controllen >= sizeof(struct cmsghdr))
186 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000187 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100188 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100189 union {
190 unsigned char *c;
191 struct in_pktinfo *p;
192 } p;
193 p.c = CMSG_DATA(cmptr);
194 iface_index = p.p->ipi_ifindex;
195 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100196 unicast_dest = 1;
197 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100198
199#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100200 if (msg.msg_controllen >= sizeof(struct cmsghdr))
201 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
202 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100203 {
204 union {
205 unsigned char *c;
206 struct sockaddr_dl *s;
207 } p;
208 p.c = CMSG_DATA(cmptr);
209 iface_index = p.s->sdl_index;
210 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100211
Simon Kelley7622fc02009-06-04 20:32:05 +0100212#elif defined(HAVE_SOLARIS_NETWORK)
213 if (msg.msg_controllen >= sizeof(struct cmsghdr))
214 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
215 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100216 {
217 union {
218 unsigned char *c;
219 unsigned int *i;
220 } p;
221 p.c = CMSG_DATA(cmptr);
222 iface_index = *(p.i);
223 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100224#endif
225
226 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
227 return;
228
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100229#ifdef HAVE_LINUX_NETWORK
230 /* ARP fiddling uses original interface even if we pretend to use a different one. */
231 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
232#endif
233
Neil Jerram4918bd52015-06-10 22:23:20 +0100234 /* If the interface on which the DHCP request was received is an
235 alias of some other interface (as specified by the
236 --bridge-interface option), change ifr.ifr_name so that we look
237 for DHCP contexts associated with the aliased interface instead
238 of with the aliasing one. */
Neil Jerramff325642016-05-03 22:45:14 +0100239 rcvd_iface_index = iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000240 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
241 {
242 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100243 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000244 {
245 if (!(iface_index = if_nametoindex(bridge->iface)))
246 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100247 my_syslog(MS_DHCP | LOG_WARNING,
248 _("unknown interface %s in bridge-interface"),
249 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000250 return;
251 }
252 else
253 {
254 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
255 break;
256 }
257 }
258
259 if (alias)
260 break;
261 }
262
Simon Kelley4011c4e2006-10-28 16:26:19 +0100263#ifdef MSG_BCAST
264 /* OpenBSD tells us when a packet was broadcast */
265 if (!(msg.msg_flags & MSG_BCAST))
266 unicast_dest = 1;
267#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000268
Simon Kelleyff7eea22013-09-04 18:01:38 +0100269 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
270 {
271 /* Reply from server, using us as relay. */
272 iface_index = relay->iface_index;
273 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
274 return;
275 is_relay_reply = 1;
276 iov.iov_len = sz;
277#ifdef HAVE_LINUX_NETWORK
278 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
279#endif
280 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000281 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000282 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100283 ifr.ifr_addr.sa_family = AF_INET;
284 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
285 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
286 else
287 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100288 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
289 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100290 return;
291 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100292
Simon Kelleyff7eea22013-09-04 18:01:38 +0100293 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
294 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
295 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000296
Simon Kelleyff7eea22013-09-04 18:01:38 +0100297 /* unlinked contexts/relays are marked by context->current == context */
298 for (context = daemon->dhcp; context; context = context->next)
299 context->current = context;
300
301 for (relay = daemon->relay4; relay; relay = relay->next)
302 relay->current = relay;
303
304 parm.current = NULL;
305 parm.relay = NULL;
306 parm.relay_local.s_addr = 0;
307 parm.ind = iface_index;
308
309 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
310 {
311 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
312 for a secondary */
313 struct match_param match;
314
315 match.matched = 0;
316 match.ind = iface_index;
317
318 if (!daemon->if_addrs ||
319 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
320 !match.matched)
321 return;
322
323 iface_addr = match.addr;
324 /* make sure secondary address gets priority in case
325 there is more than one address on the interface in the same subnet */
326 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
327 }
328
329 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000330 return;
331
Simon Kelleyff7eea22013-09-04 18:01:38 +0100332 /* We're relaying this request */
333 if (parm.relay_local.s_addr != 0 &&
334 relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
335 return;
336
337 /* May have configured relay, but not DHCP server */
338 if (!daemon->dhcp)
339 return;
340
341 lease_prune(NULL, now); /* lose any expired leases */
342 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Floris Bos503c6092017-04-09 23:07:13 +0100343 now, unicast_dest, &is_inform, pxe_fd, iface_addr, recvtime);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100344 lease_update_file(now);
345 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000346
Simon Kelleyff7eea22013-09-04 18:01:38 +0100347 if (iov.iov_len == 0)
348 return;
349 }
Floris Bos503c6092017-04-09 23:07:13 +0100350
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100351 msg.msg_name = &dest;
352 msg.msg_namelen = sizeof(dest);
353 msg.msg_control = NULL;
354 msg.msg_controllen = 0;
355 msg.msg_iov = &iov;
356 iov.iov_base = daemon->dhcp_packet.iov_base;
357
358 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000359 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100360
Simon Kelley3be34542004-09-11 19:12:13 +0100361#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100362 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100363#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000364
Simon Kelley316e2732010-01-22 20:16:09 +0000365 if (pxe_fd)
366 {
367 if (mess->ciaddr.s_addr != 0)
368 dest.sin_addr = mess->ciaddr;
369 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100370 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100371 {
372 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100373 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100374 dest.sin_addr = mess->giaddr;
375 }
376 else if (mess->ciaddr.s_addr)
377 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100378 /* If the client's idea of its own address tallys with
379 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100380 source port too, and send back to that. If we're replying
381 to a DHCPINFORM, trust the source address always. */
382 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100383 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100384 {
Simon Kelley9e038942008-05-30 20:06:34 +0100385 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100386 dest.sin_addr = mess->ciaddr;
387 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100388 }
Simon Kelley824af852008-02-12 20:43:05 +0000389#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000390 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100391 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000392 /* fill cmsg for outbound interface (both broadcast & unicast) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100393 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100394 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100395 msg.msg_controllen = sizeof(control_u);
396 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100397 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
Neil Jerramff325642016-05-03 22:45:14 +0100398 pkt->ipi_ifindex = rcvd_iface_index;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 pkt->ipi_spec_dst.s_addr = 0;
400 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000401 cmptr->cmsg_level = IPPROTO_IP;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000402 cmptr->cmsg_type = IP_PKTINFO;
403
404 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
405 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
406 {
407 /* broadcast to 255.255.255.255 (or mac address invalid) */
408 dest.sin_addr.s_addr = INADDR_BROADCAST;
409 dest.sin_port = htons(daemon->dhcp_client_port);
410 }
411 else
412 {
413 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
414 struct sockaddr limits size to 14 bytes. */
415 dest.sin_addr = mess->yiaddr;
416 dest.sin_port = htons(daemon->dhcp_client_port);
417 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
418 arp_req.arp_ha.sa_family = mess->htype;
419 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
420 /* interface name already copied in */
421 arp_req.arp_flags = ATF_COM;
422 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
423 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
424 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000425 }
Simon Kelley824af852008-02-12 20:43:05 +0000426#elif defined(HAVE_SOLARIS_NETWORK)
427 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
428 {
429 /* broadcast to 255.255.255.255 (or mac address invalid) */
430 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100431 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000432 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100433 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000434 }
435 else
436 {
437 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
438 Note that this only works for ethernet on solaris, because we use SIOCSARP
439 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
440 mysteriously. Bah. Fall back to broadcast for other net types. */
441 struct arpreq req;
442 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100443 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000444 *((struct sockaddr_in *)&req.arp_pa) = dest;
445 req.arp_ha.sa_family = AF_UNSPEC;
446 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
447 req.arp_flags = ATF_COM;
448 ioctl(daemon->dhcpfd, SIOCSARP, &req);
449 }
450#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100451 else
452 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100453 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100454 return;
455 }
456#endif
457
Simon Kelley824af852008-02-12 20:43:05 +0000458#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000459 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000460#endif
461
Simon Kelleyff841eb2015-03-11 21:36:30 +0000462 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100463
464 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
465 if (errno != 0)
466 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
467 inet_ntoa(dest.sin_addr), strerror(errno));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000468}
Simon Kelley98079ea2015-10-13 20:30:32 +0100469
Simon Kelleyc72daea2012-01-05 21:33:27 +0000470/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100471static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000472 struct in_addr netmask, struct in_addr broadcast, void *vparam)
473{
474 struct match_param *param = vparam;
475 struct iname *tmp;
476
Simon Kelley3f2873d2013-05-14 11:28:47 +0100477 (void) label;
478
Simon Kelleyc72daea2012-01-05 21:33:27 +0000479 if (if_index == param->ind)
480 {
481 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
482 if ( tmp->addr.sa.sa_family == AF_INET &&
483 tmp->addr.in.sin_addr.s_addr == local.s_addr)
484 {
485 param->matched = 1;
486 param->addr = local;
487 param->netmask = netmask;
488 param->broadcast = broadcast;
489 break;
490 }
491 }
492
493 return 1;
494}
495
Simon Kelley0a852542005-03-23 20:28:59 +0000496/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100497 of each interface (and any relay address) and does the following things:
498
499 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
500 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
501 3) Fills in local (this host) and router (this host or relay) addresses.
502 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
503
klemens43517fc2017-02-19 15:53:37 +0000504 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100505
Simon Kelley3f2873d2013-05-14 11:28:47 +0100506static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100507 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000508{
509 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100510 struct dhcp_relay *relay;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100511 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100512
513 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100514
Simon Kelley0a852542005-03-23 20:28:59 +0000515 for (context = daemon->dhcp; context; context = context->next)
516 {
517 if (!(context->flags & CONTEXT_NETMASK) &&
518 (is_same_net(local, context->start, netmask) ||
519 is_same_net(local, context->end, netmask)))
520 {
521 if (context->netmask.s_addr != netmask.s_addr &&
522 !(is_same_net(local, context->start, netmask) &&
523 is_same_net(local, context->end, netmask)))
524 {
525 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
526 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100527 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100528 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000529 }
530 context->netmask = netmask;
531 }
532
Simon Kelley7de060b2011-08-26 17:24:52 +0100533 if (context->netmask.s_addr != 0 &&
534 is_same_net(local, context->start, context->netmask) &&
535 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000536 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100537 /* link it onto the current chain if we've not seen it before */
538 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000539 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100540 context->router = local;
541 context->local = local;
542 context->current = param->current;
543 param->current = context;
544 }
545
546 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000547 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100548 if (is_same_net(broadcast, context->start, context->netmask))
549 context->broadcast = broadcast;
550 else
Simon Kelley0a852542005-03-23 20:28:59 +0000551 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
552 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100553 }
Simon Kelley0a852542005-03-23 20:28:59 +0000554 }
555
Simon Kelleyff7eea22013-09-04 18:01:38 +0100556 for (relay = daemon->relay4; relay; relay = relay->next)
557 if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
558 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
559 {
560 relay->current = param->relay;
561 param->relay = relay;
562 param->relay_local = local;
563 }
564
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100565 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000566}
567
Simon Kelley824af852008-02-12 20:43:05 +0000568struct dhcp_context *address_available(struct dhcp_context *context,
569 struct in_addr taddr,
570 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000571{
Simon Kelley36717ee2004-09-20 19:20:58 +0100572 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100573 possible ranges. Make sure that the address isn't in use
574 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000575
Simon Kelley36717ee2004-09-20 19:20:58 +0100576 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100577 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100578
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100579 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100580 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100581 return NULL;
582
583 for (tmp = context; tmp; tmp = tmp->current)
584 {
585 start = ntohl(tmp->start.s_addr);
586 end = ntohl(tmp->end.s_addr);
587
Simon Kelley7de060b2011-08-26 17:24:52 +0100588 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100589 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000590 addr <= end &&
591 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100592 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100593 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000594
Simon Kelley59353a62004-11-21 19:34:28 +0000595 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000596}
Simon Kelley59353a62004-11-21 19:34:28 +0000597
Simon Kelley824af852008-02-12 20:43:05 +0000598struct dhcp_context *narrow_context(struct dhcp_context *context,
599 struct in_addr taddr,
600 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000601{
Simon Kelleye17fb622006-01-14 20:33:46 +0000602 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000603 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000604 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000605 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000606 any dhcp-range. In that case we return a static range if possible, or failing that,
607 any context on the correct subnet. (If there's more than one, this is a dodgy
608 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000609
Simon Kelleye17fb622006-01-14 20:33:46 +0000610 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000611
Simon Kelley824af852008-02-12 20:43:05 +0000612 if (!(tmp = address_available(context, taddr, netids)))
613 {
614 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100615 if (match_netid(tmp->filter, netids, 1) &&
616 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100617 (tmp->flags & CONTEXT_STATIC))
618 break;
Simon Kelley824af852008-02-12 20:43:05 +0000619
620 if (!tmp)
621 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100622 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100623 is_same_net(taddr, tmp->start, tmp->netmask) &&
624 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000625 break;
626 }
Simon Kelley59353a62004-11-21 19:34:28 +0000627
Simon Kelley824af852008-02-12 20:43:05 +0000628 /* Only one context allowed now */
629 if (tmp)
630 tmp->current = NULL;
631
632 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000633}
634
Simon Kelleydfa666f2004-08-02 18:27:27 +0100635struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
636{
637 struct dhcp_config *config;
638
639 for (config = configs; config; config = config->next)
640 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
641 return config;
642
643 return NULL;
644}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000645
Simon Kelley5aabfc72007-08-29 11:24:47 +0100646int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000647 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100648 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000649{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100650 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000651 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000652 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000653
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100654 struct in_addr start, addr;
655 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000656 int i, pass;
657 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100658
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100659 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
660 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100661 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100662 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100663
Simon Kelleycdeda282006-03-16 20:16:06 +0000664 for (pass = 0; pass <= 1; pass++)
665 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100666 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000667 continue;
668 else if (!match_netid(c->filter, netids, pass))
669 continue;
670 else
671 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100672 if (option_bool(OPT_CONSEC_ADDR))
673 /* seed is largest extant lease addr in this context */
674 start = lease_find_max_addr(c);
675 else
676 /* pick a seed based on hwaddr */
677 start.s_addr = htonl(ntohl(c->start.s_addr) +
678 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
679
680 /* iterate until we find a free address. */
681 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000682
683 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100684 /* eliminate addresses in use by the server. */
685 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100686 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100687 break;
688
Simon Kelley73a08a22009-02-05 20:28:08 +0000689 /* Addresses which end in .255 and .0 are broken in Windows even when using
690 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
691 then 192.168.0.255 is a valid IP address, but not for Windows as it's
692 in the class C range. See KB281579. We therefore don't allocate these
693 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100694 if (!d &&
695 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000696 !config_find_by_address(daemon->dhcp_conf, addr) &&
697 (!IN_CLASSC(ntohl(addr.s_addr)) ||
698 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000699 {
700 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100701 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
702 ((float)PING_WAIT)));
703
704 *addrp = addr;
705
Simon Kelleycdeda282006-03-16 20:16:06 +0000706 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100707 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000708 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100709 on us repeatedly. As a final check, if we did more
710 than 60% of the possible ping checks in the last
711 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000712 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100713 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000714 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100715 else
716 {
717 count++;
718 if (r->addr.s_addr == addr.s_addr)
719 {
720 /* consec-ip mode: we offered this address for another client
721 (different hash) recently, don't offer it to this one. */
722 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
723 break;
724
725 return 1;
726 }
727 }
728
729 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100730 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100731 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000732 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100733 /* address in use: perturb address selection so that we are
734 less likely to try this address again. */
735 if (!option_bool(OPT_CONSEC_ADDR))
736 c->addr_epoch++;
737 }
738 else
739 {
740 /* at this point victim may hold an expired record */
741 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000742 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100743 if ((victim = whine_malloc(sizeof(struct ping_result))))
744 {
745 victim->next = daemon->ping_results;
746 daemon->ping_results = victim;
747 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000748 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100749
750 /* record that this address is OK for 30s
751 without more ping checks */
752 if (victim)
753 {
754 victim->addr = addr;
755 victim->time = now;
756 victim->hash = j;
757 }
758 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000759 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100760 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000761 }
Simon Kelley3be34542004-09-11 19:12:13 +0100762
Simon Kelleycdeda282006-03-16 20:16:06 +0000763 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
764
765 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
766 addr = c->start;
767
768 } while (addr.s_addr != start.s_addr);
769 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100770
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000771 return 0;
772}
773
Simon Kelley5aabfc72007-08-29 11:24:47 +0100774void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000775{
Simon Kelley44a2a312004-03-10 20:04:35 +0000776 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000777 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100778 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100779 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000780 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100781 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100782 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100783 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000784 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100785
786 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000787
788 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100789 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100790 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100791 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100792 }
793
Simon Kelley849a8352006-06-09 21:02:31 +0100794 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100795 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100796 {
797 tmp = config->next;
798 if (config->flags & CONFIG_FROM_ETHERS)
799 {
800 *up = tmp;
801 /* cannot have a clid */
802 if (config->flags & CONFIG_NAME)
803 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000804 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100805 free(config);
806 }
807 else
808 up = &config->next;
809 }
810
Simon Kelley44a2a312004-03-10 20:04:35 +0000811 while (fgets(buff, MAXDNAME, f))
812 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100813 char *host = NULL;
814
Simon Kelleyb8187c82005-11-26 21:46:27 +0000815 lineno++;
816
Simon Kelley824af852008-02-12 20:43:05 +0000817 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000818 buff[strlen(buff)-1] = 0;
819
Simon Kelley73a08a22009-02-05 20:28:08 +0000820 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000821 continue;
822
Simon Kelley824af852008-02-12 20:43:05 +0000823 for (ip = buff; *ip && !isspace((int)*ip); ip++);
824 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000825 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000826 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000827 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100828 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000829 continue;
830 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000831
832 /* check for name or dotted-quad */
833 for (cp = ip; *cp; cp++)
834 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
835 break;
836
837 if (!*cp)
838 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000839 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000840 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100841 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000842 continue;
843 }
844
Simon Kelley33820b72004-04-03 21:10:00 +0100845 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000846
Simon Kelley16972692006-10-16 20:04:18 +0100847 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100848 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000849 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000850 }
851 else
852 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100853 int nomem;
854 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000855 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100856 if (!nomem)
857 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
858 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000859 continue;
860 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100861
Simon Kelley33820b72004-04-03 21:10:00 +0100862 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000863
Simon Kelley16972692006-10-16 20:04:18 +0100864 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100865 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000866 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000867 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100868
869 if (config && (config->flags & CONFIG_FROM_ETHERS))
870 {
871 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
872 continue;
873 }
874
Simon Kelley1cff1662004-03-12 08:12:58 +0000875 if (!config)
876 {
Simon Kelley16972692006-10-16 20:04:18 +0100877 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000878 {
879 struct hwaddr_config *conf_addr = config->hwaddr;
880 if (conf_addr &&
881 conf_addr->next == NULL &&
882 conf_addr->wildcard_mask == 0 &&
883 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
884 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
885 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
886 break;
887 }
Simon Kelley33820b72004-04-03 21:10:00 +0100888
889 if (!config)
890 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100891 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100892 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100893 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000894 config->hwaddr = NULL;
895 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100896 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100897 config->next = daemon->dhcp_conf;
898 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100899 }
900
901 config->flags |= flags;
902
903 if (flags & CONFIG_NAME)
904 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100905 config->hostname = host;
906 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100907 }
908
909 if (flags & CONFIG_ADDR)
910 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000911 }
Simon Kelley33820b72004-04-03 21:10:00 +0100912
Simon Kelley9009d742008-11-14 20:04:27 +0000913 config->flags |= CONFIG_NOCLID;
914 if (!config->hwaddr)
915 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
916 if (config->hwaddr)
917 {
918 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
919 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
920 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
921 config->hwaddr->wildcard_mask = 0;
922 config->hwaddr->next = NULL;
923 }
Simon Kelley33820b72004-04-03 21:10:00 +0100924 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100925
926 free(host);
927
Simon Kelley44a2a312004-03-10 20:04:35 +0000928 }
929
930 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100931
Simon Kelley7622fc02009-06-04 20:32:05 +0100932 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000933}
934
Simon Kelley44a2a312004-03-10 20:04:35 +0000935
Simon Kelleybb01cb92004-12-13 20:56:23 +0000936/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
937 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100938 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100939 so check here that the domain name is legal as a hostname.
940 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100941char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000942{
Simon Kelley824af852008-02-12 20:43:05 +0000943 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000944
945 if (daemon->port == 0)
946 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000947
Simon Kelley824af852008-02-12 20:43:05 +0000948 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100949
Simon Kelleybb01cb92004-12-13 20:56:23 +0000950 if (lookup && (lookup->flags & F_HOSTS))
951 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100952 char *dot, *hostname = cache_get_name(lookup);
953 dot = strchr(hostname, '.');
954
955 if (dot && strlen(dot+1) != 0)
956 {
957 char *d2 = get_domain(addr);
958 if (!d2 || !hostname_isequal(dot+1, d2))
959 return NULL; /* wrong domain */
960 }
961
962 if (!legal_hostname(hostname))
963 return NULL;
964
965 strncpy(daemon->dhcp_buff, hostname, 256);
966 daemon->dhcp_buff[255] = 0;
967 strip_hostname(daemon->dhcp_buff);
968
969 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000970 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100971
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100972 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000973}
974
Simon Kelleyff7eea22013-09-04 18:01:38 +0100975static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
976{
977 /* ->local is same value for all relays on ->current chain */
978 struct all_addr from;
979
980 if (mess->op != BOOTREQUEST)
981 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000982
Simon Kelleyff7eea22013-09-04 18:01:38 +0100983 /* source address == relay address */
984 from.addr.addr4 = relay->local.addr.addr4;
985
986 /* already gatewayed ? */
987 if (mess->giaddr.s_addr)
988 {
989 /* if so check if by us, to stomp on loops. */
990 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
991 return 1;
992 }
993 else
994 {
995 /* plug in our address */
996 mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
997 }
998
999 if ((mess->hops++) > 20)
1000 return 1;
1001
1002 for (; relay; relay = relay->current)
1003 {
1004 union mysockaddr to;
1005
1006 to.sa.sa_family = AF_INET;
1007 to.in.sin_addr = relay->server.addr.addr4;
1008 to.in.sin_port = htons(daemon->dhcp_server_port);
1009
1010 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1011
1012 if (option_bool(OPT_LOG_OPTS))
1013 {
1014 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1015 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
1016 }
1017
1018 /* Save this for replies */
1019 relay->iface_index = iface_index;
1020 }
1021
1022 return 1;
1023}
1024
1025
1026static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1027{
1028 struct dhcp_relay *relay;
1029
1030 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1031 return NULL;
1032
1033 for (relay = daemon->relay4; relay; relay = relay->next)
1034 {
1035 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1036 {
1037 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1038 return relay->iface_index != 0 ? relay : NULL;
1039 }
1040 }
1041
1042 return NULL;
1043}
1044
1045#endif