blob: 08952c83d073413e1059f54e5080e7bb5433a2d1 [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;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100152#ifdef HAVE_LINUX_NETWORK
153 struct arpreq arp_req;
154#endif
155
Simon Kelley44a2a312004-03-10 20:04:35 +0000156 union {
157 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000158#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000159 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000160#elif defined(HAVE_SOLARIS_NETWORK)
161 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100162#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000163 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
164#endif
165 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000166 struct dhcp_bridge *bridge, *alias;
167
168 msg.msg_controllen = sizeof(control_u);
169 msg.msg_control = control_u.control;
170 msg.msg_name = &dest;
171 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100172 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000173 msg.msg_iovlen = 1;
174
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
176 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000177 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000178
179 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100180 if (msg.msg_controllen >= sizeof(struct cmsghdr))
181 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000182 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100183 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100184 union {
185 unsigned char *c;
186 struct in_pktinfo *p;
187 } p;
188 p.c = CMSG_DATA(cmptr);
189 iface_index = p.p->ipi_ifindex;
190 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100191 unicast_dest = 1;
192 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100193
194#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100195 if (msg.msg_controllen >= sizeof(struct cmsghdr))
196 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
197 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100198 {
199 union {
200 unsigned char *c;
201 struct sockaddr_dl *s;
202 } p;
203 p.c = CMSG_DATA(cmptr);
204 iface_index = p.s->sdl_index;
205 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100206
Simon Kelley7622fc02009-06-04 20:32:05 +0100207#elif defined(HAVE_SOLARIS_NETWORK)
208 if (msg.msg_controllen >= sizeof(struct cmsghdr))
209 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
210 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100211 {
212 union {
213 unsigned char *c;
214 unsigned int *i;
215 } p;
216 p.c = CMSG_DATA(cmptr);
217 iface_index = *(p.i);
218 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100219#endif
220
221 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
222 return;
223
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100224#ifdef HAVE_LINUX_NETWORK
225 /* ARP fiddling uses original interface even if we pretend to use a different one. */
226 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
227#endif
228
Neil Jerram4918bd52015-06-10 22:23:20 +0100229 /* If the interface on which the DHCP request was received is an
230 alias of some other interface (as specified by the
231 --bridge-interface option), change ifr.ifr_name so that we look
232 for DHCP contexts associated with the aliased interface instead
233 of with the aliasing one. */
Neil Jerramff325642016-05-03 22:45:14 +0100234 rcvd_iface_index = iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000235 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
236 {
237 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100238 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000239 {
240 if (!(iface_index = if_nametoindex(bridge->iface)))
241 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100242 my_syslog(MS_DHCP | LOG_WARNING,
243 _("unknown interface %s in bridge-interface"),
244 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000245 return;
246 }
247 else
248 {
249 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
250 break;
251 }
252 }
253
254 if (alias)
255 break;
256 }
257
Simon Kelley4011c4e2006-10-28 16:26:19 +0100258#ifdef MSG_BCAST
259 /* OpenBSD tells us when a packet was broadcast */
260 if (!(msg.msg_flags & MSG_BCAST))
261 unicast_dest = 1;
262#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000263
Simon Kelleyff7eea22013-09-04 18:01:38 +0100264 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
265 {
266 /* Reply from server, using us as relay. */
267 iface_index = relay->iface_index;
268 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
269 return;
270 is_relay_reply = 1;
271 iov.iov_len = sz;
272#ifdef HAVE_LINUX_NETWORK
273 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
274#endif
275 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000276 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000277 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100278 ifr.ifr_addr.sa_family = AF_INET;
279 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
280 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
281 else
282 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100283 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
284 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 +0100285 return;
286 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100287
Simon Kelleyff7eea22013-09-04 18:01:38 +0100288 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
289 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
290 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000291
Simon Kelleyff7eea22013-09-04 18:01:38 +0100292 /* unlinked contexts/relays are marked by context->current == context */
293 for (context = daemon->dhcp; context; context = context->next)
294 context->current = context;
295
296 for (relay = daemon->relay4; relay; relay = relay->next)
297 relay->current = relay;
298
299 parm.current = NULL;
300 parm.relay = NULL;
301 parm.relay_local.s_addr = 0;
302 parm.ind = iface_index;
303
304 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
305 {
306 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
307 for a secondary */
308 struct match_param match;
309
310 match.matched = 0;
311 match.ind = iface_index;
312
313 if (!daemon->if_addrs ||
314 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
315 !match.matched)
316 return;
317
318 iface_addr = match.addr;
319 /* make sure secondary address gets priority in case
320 there is more than one address on the interface in the same subnet */
321 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
322 }
323
324 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000325 return;
326
Simon Kelleyff7eea22013-09-04 18:01:38 +0100327 /* We're relaying this request */
328 if (parm.relay_local.s_addr != 0 &&
329 relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
330 return;
331
332 /* May have configured relay, but not DHCP server */
333 if (!daemon->dhcp)
334 return;
335
336 lease_prune(NULL, now); /* lose any expired leases */
337 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
338 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
339 lease_update_file(now);
340 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000341
Simon Kelleyff7eea22013-09-04 18:01:38 +0100342 if (iov.iov_len == 0)
343 return;
344 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000345
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100346 msg.msg_name = &dest;
347 msg.msg_namelen = sizeof(dest);
348 msg.msg_control = NULL;
349 msg.msg_controllen = 0;
350 msg.msg_iov = &iov;
351 iov.iov_base = daemon->dhcp_packet.iov_base;
352
353 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000354 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100355
Simon Kelley3be34542004-09-11 19:12:13 +0100356#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100357 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100358#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000359
Simon Kelley316e2732010-01-22 20:16:09 +0000360 if (pxe_fd)
361 {
362 if (mess->ciaddr.s_addr != 0)
363 dest.sin_addr = mess->ciaddr;
364 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100365 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366 {
367 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100368 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100369 dest.sin_addr = mess->giaddr;
370 }
371 else if (mess->ciaddr.s_addr)
372 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100373 /* If the client's idea of its own address tallys with
374 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100375 source port too, and send back to that. If we're replying
376 to a DHCPINFORM, trust the source address always. */
377 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100378 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100379 {
Simon Kelley9e038942008-05-30 20:06:34 +0100380 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100381 dest.sin_addr = mess->ciaddr;
382 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100383 }
Simon Kelley824af852008-02-12 20:43:05 +0000384#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000385 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100386 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000387 /* fill cmsg for outbound interface (both broadcast & unicast) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100388 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100389 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100390 msg.msg_controllen = sizeof(control_u);
391 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100392 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
Neil Jerramff325642016-05-03 22:45:14 +0100393 pkt->ipi_ifindex = rcvd_iface_index;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100394 pkt->ipi_spec_dst.s_addr = 0;
395 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000396 cmptr->cmsg_level = IPPROTO_IP;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000397 cmptr->cmsg_type = IP_PKTINFO;
398
399 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
400 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
401 {
402 /* broadcast to 255.255.255.255 (or mac address invalid) */
403 dest.sin_addr.s_addr = INADDR_BROADCAST;
404 dest.sin_port = htons(daemon->dhcp_client_port);
405 }
406 else
407 {
408 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
409 struct sockaddr limits size to 14 bytes. */
410 dest.sin_addr = mess->yiaddr;
411 dest.sin_port = htons(daemon->dhcp_client_port);
412 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
413 arp_req.arp_ha.sa_family = mess->htype;
414 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
415 /* interface name already copied in */
416 arp_req.arp_flags = ATF_COM;
417 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
418 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
419 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000420 }
Simon Kelley824af852008-02-12 20:43:05 +0000421#elif defined(HAVE_SOLARIS_NETWORK)
422 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
423 {
424 /* broadcast to 255.255.255.255 (or mac address invalid) */
425 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100426 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000427 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100428 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000429 }
430 else
431 {
432 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
433 Note that this only works for ethernet on solaris, because we use SIOCSARP
434 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
435 mysteriously. Bah. Fall back to broadcast for other net types. */
436 struct arpreq req;
437 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100438 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000439 *((struct sockaddr_in *)&req.arp_pa) = dest;
440 req.arp_ha.sa_family = AF_UNSPEC;
441 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
442 req.arp_flags = ATF_COM;
443 ioctl(daemon->dhcpfd, SIOCSARP, &req);
444 }
445#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100446 else
447 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100448 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100449 return;
450 }
451#endif
452
Simon Kelley824af852008-02-12 20:43:05 +0000453#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000454 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000455#endif
456
Simon Kelleyff841eb2015-03-11 21:36:30 +0000457 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100458
459 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
460 if (errno != 0)
461 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
462 inet_ntoa(dest.sin_addr), strerror(errno));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000463}
Simon Kelley98079ea2015-10-13 20:30:32 +0100464
Simon Kelleyc72daea2012-01-05 21:33:27 +0000465/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100466static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000467 struct in_addr netmask, struct in_addr broadcast, void *vparam)
468{
469 struct match_param *param = vparam;
470 struct iname *tmp;
471
Simon Kelley3f2873d2013-05-14 11:28:47 +0100472 (void) label;
473
Simon Kelleyc72daea2012-01-05 21:33:27 +0000474 if (if_index == param->ind)
475 {
476 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
477 if ( tmp->addr.sa.sa_family == AF_INET &&
478 tmp->addr.in.sin_addr.s_addr == local.s_addr)
479 {
480 param->matched = 1;
481 param->addr = local;
482 param->netmask = netmask;
483 param->broadcast = broadcast;
484 break;
485 }
486 }
487
488 return 1;
489}
490
Simon Kelley0a852542005-03-23 20:28:59 +0000491/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100492 of each interface (and any relay address) and does the following things:
493
494 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
495 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
496 3) Fills in local (this host) and router (this host or relay) addresses.
497 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
498
klemens43517fc2017-02-19 15:53:37 +0000499 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100500
Simon Kelley3f2873d2013-05-14 11:28:47 +0100501static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100502 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000503{
504 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100505 struct dhcp_relay *relay;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100506 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100507
508 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100509
Simon Kelley0a852542005-03-23 20:28:59 +0000510 for (context = daemon->dhcp; context; context = context->next)
511 {
512 if (!(context->flags & CONTEXT_NETMASK) &&
513 (is_same_net(local, context->start, netmask) ||
514 is_same_net(local, context->end, netmask)))
515 {
516 if (context->netmask.s_addr != netmask.s_addr &&
517 !(is_same_net(local, context->start, netmask) &&
518 is_same_net(local, context->end, netmask)))
519 {
520 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
521 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100522 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100523 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000524 }
525 context->netmask = netmask;
526 }
527
Simon Kelley7de060b2011-08-26 17:24:52 +0100528 if (context->netmask.s_addr != 0 &&
529 is_same_net(local, context->start, context->netmask) &&
530 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000531 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100532 /* link it onto the current chain if we've not seen it before */
533 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000534 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100535 context->router = local;
536 context->local = local;
537 context->current = param->current;
538 param->current = context;
539 }
540
541 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000542 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100543 if (is_same_net(broadcast, context->start, context->netmask))
544 context->broadcast = broadcast;
545 else
Simon Kelley0a852542005-03-23 20:28:59 +0000546 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
547 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100548 }
Simon Kelley0a852542005-03-23 20:28:59 +0000549 }
550
Simon Kelleyff7eea22013-09-04 18:01:38 +0100551 for (relay = daemon->relay4; relay; relay = relay->next)
552 if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
553 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
554 {
555 relay->current = param->relay;
556 param->relay = relay;
557 param->relay_local = local;
558 }
559
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100560 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000561}
562
Simon Kelley824af852008-02-12 20:43:05 +0000563struct dhcp_context *address_available(struct dhcp_context *context,
564 struct in_addr taddr,
565 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000566{
Simon Kelley36717ee2004-09-20 19:20:58 +0100567 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100568 possible ranges. Make sure that the address isn't in use
569 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000570
Simon Kelley36717ee2004-09-20 19:20:58 +0100571 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100572 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100573
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100574 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100575 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100576 return NULL;
577
578 for (tmp = context; tmp; tmp = tmp->current)
579 {
580 start = ntohl(tmp->start.s_addr);
581 end = ntohl(tmp->end.s_addr);
582
Simon Kelley7de060b2011-08-26 17:24:52 +0100583 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100584 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000585 addr <= end &&
586 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100587 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100588 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000589
Simon Kelley59353a62004-11-21 19:34:28 +0000590 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000591}
Simon Kelley59353a62004-11-21 19:34:28 +0000592
Simon Kelley824af852008-02-12 20:43:05 +0000593struct dhcp_context *narrow_context(struct dhcp_context *context,
594 struct in_addr taddr,
595 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000596{
Simon Kelleye17fb622006-01-14 20:33:46 +0000597 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000598 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000599 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000600 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000601 any dhcp-range. In that case we return a static range if possible, or failing that,
602 any context on the correct subnet. (If there's more than one, this is a dodgy
603 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000604
Simon Kelleye17fb622006-01-14 20:33:46 +0000605 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000606
Simon Kelley824af852008-02-12 20:43:05 +0000607 if (!(tmp = address_available(context, taddr, netids)))
608 {
609 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100610 if (match_netid(tmp->filter, netids, 1) &&
611 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100612 (tmp->flags & CONTEXT_STATIC))
613 break;
Simon Kelley824af852008-02-12 20:43:05 +0000614
615 if (!tmp)
616 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100617 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100618 is_same_net(taddr, tmp->start, tmp->netmask) &&
619 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000620 break;
621 }
Simon Kelley59353a62004-11-21 19:34:28 +0000622
Simon Kelley824af852008-02-12 20:43:05 +0000623 /* Only one context allowed now */
624 if (tmp)
625 tmp->current = NULL;
626
627 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000628}
629
Simon Kelleydfa666f2004-08-02 18:27:27 +0100630struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
631{
632 struct dhcp_config *config;
633
634 for (config = configs; config; config = config->next)
635 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
636 return config;
637
638 return NULL;
639}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000640
Simon Kelley5aabfc72007-08-29 11:24:47 +0100641int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000642 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100643 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100645 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000646 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000647 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000648
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100649 struct in_addr start, addr;
650 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000651 int i, pass;
652 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100653
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100654 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
655 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100656 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100657 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100658
Simon Kelleycdeda282006-03-16 20:16:06 +0000659 for (pass = 0; pass <= 1; pass++)
660 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100661 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000662 continue;
663 else if (!match_netid(c->filter, netids, pass))
664 continue;
665 else
666 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100667 if (option_bool(OPT_CONSEC_ADDR))
668 /* seed is largest extant lease addr in this context */
669 start = lease_find_max_addr(c);
670 else
671 /* pick a seed based on hwaddr */
672 start.s_addr = htonl(ntohl(c->start.s_addr) +
673 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
674
675 /* iterate until we find a free address. */
676 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000677
678 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100679 /* eliminate addresses in use by the server. */
680 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100681 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100682 break;
683
Simon Kelley73a08a22009-02-05 20:28:08 +0000684 /* Addresses which end in .255 and .0 are broken in Windows even when using
685 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
686 then 192.168.0.255 is a valid IP address, but not for Windows as it's
687 in the class C range. See KB281579. We therefore don't allocate these
688 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100689 if (!d &&
690 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000691 !config_find_by_address(daemon->dhcp_conf, addr) &&
692 (!IN_CLASSC(ntohl(addr.s_addr)) ||
693 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000694 {
695 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100696 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
697 ((float)PING_WAIT)));
698
699 *addrp = addr;
700
Simon Kelleycdeda282006-03-16 20:16:06 +0000701 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100702 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000703 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100704 on us repeatedly. As a final check, if we did more
705 than 60% of the possible ping checks in the last
706 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000707 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100708 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000709 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100710 else
711 {
712 count++;
713 if (r->addr.s_addr == addr.s_addr)
714 {
715 /* consec-ip mode: we offered this address for another client
716 (different hash) recently, don't offer it to this one. */
717 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
718 break;
719
720 return 1;
721 }
722 }
723
724 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100725 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100726 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000727 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100728 /* address in use: perturb address selection so that we are
729 less likely to try this address again. */
730 if (!option_bool(OPT_CONSEC_ADDR))
731 c->addr_epoch++;
732 }
733 else
734 {
735 /* at this point victim may hold an expired record */
736 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000737 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100738 if ((victim = whine_malloc(sizeof(struct ping_result))))
739 {
740 victim->next = daemon->ping_results;
741 daemon->ping_results = victim;
742 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000743 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100744
745 /* record that this address is OK for 30s
746 without more ping checks */
747 if (victim)
748 {
749 victim->addr = addr;
750 victim->time = now;
751 victim->hash = j;
752 }
753 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000754 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100755 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000756 }
Simon Kelley3be34542004-09-11 19:12:13 +0100757
Simon Kelleycdeda282006-03-16 20:16:06 +0000758 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
759
760 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
761 addr = c->start;
762
763 } while (addr.s_addr != start.s_addr);
764 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100765
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000766 return 0;
767}
768
Simon Kelley5aabfc72007-08-29 11:24:47 +0100769void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000770{
Simon Kelley44a2a312004-03-10 20:04:35 +0000771 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000772 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100773 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100774 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000775 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100776 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100777 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100778 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000779 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100780
781 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000782
783 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100784 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100785 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100786 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100787 }
788
Simon Kelley849a8352006-06-09 21:02:31 +0100789 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100790 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100791 {
792 tmp = config->next;
793 if (config->flags & CONFIG_FROM_ETHERS)
794 {
795 *up = tmp;
796 /* cannot have a clid */
797 if (config->flags & CONFIG_NAME)
798 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000799 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100800 free(config);
801 }
802 else
803 up = &config->next;
804 }
805
Simon Kelley44a2a312004-03-10 20:04:35 +0000806 while (fgets(buff, MAXDNAME, f))
807 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100808 char *host = NULL;
809
Simon Kelleyb8187c82005-11-26 21:46:27 +0000810 lineno++;
811
Simon Kelley824af852008-02-12 20:43:05 +0000812 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000813 buff[strlen(buff)-1] = 0;
814
Simon Kelley73a08a22009-02-05 20:28:08 +0000815 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000816 continue;
817
Simon Kelley824af852008-02-12 20:43:05 +0000818 for (ip = buff; *ip && !isspace((int)*ip); ip++);
819 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000820 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000821 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000822 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100823 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000824 continue;
825 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000826
827 /* check for name or dotted-quad */
828 for (cp = ip; *cp; cp++)
829 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
830 break;
831
832 if (!*cp)
833 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000834 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000835 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100836 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000837 continue;
838 }
839
Simon Kelley33820b72004-04-03 21:10:00 +0100840 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000841
Simon Kelley16972692006-10-16 20:04:18 +0100842 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100843 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000844 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000845 }
846 else
847 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100848 int nomem;
849 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000850 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100851 if (!nomem)
852 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
853 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000854 continue;
855 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100856
Simon Kelley33820b72004-04-03 21:10:00 +0100857 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000858
Simon Kelley16972692006-10-16 20:04:18 +0100859 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100860 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000861 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000862 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100863
864 if (config && (config->flags & CONFIG_FROM_ETHERS))
865 {
866 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
867 continue;
868 }
869
Simon Kelley1cff1662004-03-12 08:12:58 +0000870 if (!config)
871 {
Simon Kelley16972692006-10-16 20:04:18 +0100872 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000873 {
874 struct hwaddr_config *conf_addr = config->hwaddr;
875 if (conf_addr &&
876 conf_addr->next == NULL &&
877 conf_addr->wildcard_mask == 0 &&
878 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
879 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
880 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
881 break;
882 }
Simon Kelley33820b72004-04-03 21:10:00 +0100883
884 if (!config)
885 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100886 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100887 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100888 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000889 config->hwaddr = NULL;
890 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100891 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100892 config->next = daemon->dhcp_conf;
893 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100894 }
895
896 config->flags |= flags;
897
898 if (flags & CONFIG_NAME)
899 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100900 config->hostname = host;
901 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100902 }
903
904 if (flags & CONFIG_ADDR)
905 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000906 }
Simon Kelley33820b72004-04-03 21:10:00 +0100907
Simon Kelley9009d742008-11-14 20:04:27 +0000908 config->flags |= CONFIG_NOCLID;
909 if (!config->hwaddr)
910 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
911 if (config->hwaddr)
912 {
913 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
914 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
915 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
916 config->hwaddr->wildcard_mask = 0;
917 config->hwaddr->next = NULL;
918 }
Simon Kelley33820b72004-04-03 21:10:00 +0100919 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100920
921 free(host);
922
Simon Kelley44a2a312004-03-10 20:04:35 +0000923 }
924
925 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100926
Simon Kelley7622fc02009-06-04 20:32:05 +0100927 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000928}
929
Simon Kelley44a2a312004-03-10 20:04:35 +0000930
Simon Kelleybb01cb92004-12-13 20:56:23 +0000931/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
932 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100933 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100934 so check here that the domain name is legal as a hostname.
935 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100936char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000937{
Simon Kelley824af852008-02-12 20:43:05 +0000938 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000939
940 if (daemon->port == 0)
941 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000942
Simon Kelley824af852008-02-12 20:43:05 +0000943 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100944
Simon Kelleybb01cb92004-12-13 20:56:23 +0000945 if (lookup && (lookup->flags & F_HOSTS))
946 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100947 char *dot, *hostname = cache_get_name(lookup);
948 dot = strchr(hostname, '.');
949
950 if (dot && strlen(dot+1) != 0)
951 {
952 char *d2 = get_domain(addr);
953 if (!d2 || !hostname_isequal(dot+1, d2))
954 return NULL; /* wrong domain */
955 }
956
957 if (!legal_hostname(hostname))
958 return NULL;
959
960 strncpy(daemon->dhcp_buff, hostname, 256);
961 daemon->dhcp_buff[255] = 0;
962 strip_hostname(daemon->dhcp_buff);
963
964 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000965 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100966
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100967 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000968}
969
Simon Kelleyff7eea22013-09-04 18:01:38 +0100970static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
971{
972 /* ->local is same value for all relays on ->current chain */
973 struct all_addr from;
974
975 if (mess->op != BOOTREQUEST)
976 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000977
Simon Kelleyff7eea22013-09-04 18:01:38 +0100978 /* source address == relay address */
979 from.addr.addr4 = relay->local.addr.addr4;
980
981 /* already gatewayed ? */
982 if (mess->giaddr.s_addr)
983 {
984 /* if so check if by us, to stomp on loops. */
985 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
986 return 1;
987 }
988 else
989 {
990 /* plug in our address */
991 mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
992 }
993
994 if ((mess->hops++) > 20)
995 return 1;
996
997 for (; relay; relay = relay->current)
998 {
999 union mysockaddr to;
1000
1001 to.sa.sa_family = AF_INET;
1002 to.in.sin_addr = relay->server.addr.addr4;
1003 to.in.sin_port = htons(daemon->dhcp_server_port);
1004
1005 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1006
1007 if (option_bool(OPT_LOG_OPTS))
1008 {
1009 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1010 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
1011 }
1012
1013 /* Save this for replies */
1014 relay->iface_index = iface_index;
1015 }
1016
1017 return 1;
1018}
1019
1020
1021static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1022{
1023 struct dhcp_relay *relay;
1024
1025 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1026 return NULL;
1027
1028 for (relay = daemon->relay4; relay; relay = relay->next)
1029 {
1030 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1031 {
1032 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1033 return relay->iface_index != 0 ? relay : NULL;
1034 }
1035 }
1036
1037 return NULL;
1038}
1039
1040#endif