blob: bea468819d13aad12729cc3ef53989488ee430ce [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 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 Kelleyc7be0162017-05-10 22:21:53 +0100148 int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 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
Simon Kelleyc7be0162017-05-10 22:21:53 +0100226 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
Simon Kelley7ab78b92017-05-11 20:33:21 +0100227 ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100228 return;
Simon Kelleyc7be0162017-05-10 22:21:53 +0100229
230 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
231 loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
232
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100233#ifdef HAVE_LINUX_NETWORK
234 /* ARP fiddling uses original interface even if we pretend to use a different one. */
Petr Menšík47b45b22018-08-15 18:17:00 +0200235 safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100236#endif
237
Neil Jerram4918bd52015-06-10 22:23:20 +0100238 /* If the interface on which the DHCP request was received is an
239 alias of some other interface (as specified by the
240 --bridge-interface option), change ifr.ifr_name so that we look
241 for DHCP contexts associated with the aliased interface instead
242 of with the aliasing one. */
Neil Jerramff325642016-05-03 22:45:14 +0100243 rcvd_iface_index = iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000244 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
245 {
246 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100247 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000248 {
249 if (!(iface_index = if_nametoindex(bridge->iface)))
250 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100251 my_syslog(MS_DHCP | LOG_WARNING,
252 _("unknown interface %s in bridge-interface"),
253 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000254 return;
255 }
256 else
257 {
Petr Menšík47b45b22018-08-15 18:17:00 +0200258 safe_strncpy(ifr.ifr_name, bridge->iface, sizeof(ifr.ifr_name));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000259 break;
260 }
261 }
262
263 if (alias)
264 break;
265 }
266
Simon Kelley4011c4e2006-10-28 16:26:19 +0100267#ifdef MSG_BCAST
268 /* OpenBSD tells us when a packet was broadcast */
269 if (!(msg.msg_flags & MSG_BCAST))
270 unicast_dest = 1;
271#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000272
Simon Kelleyff7eea22013-09-04 18:01:38 +0100273 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
274 {
275 /* Reply from server, using us as relay. */
Simon Kelley1649f702017-06-25 21:19:30 +0100276 rcvd_iface_index = relay->iface_index;
277 if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100278 return;
279 is_relay_reply = 1;
280 iov.iov_len = sz;
281#ifdef HAVE_LINUX_NETWORK
Petr Menšík47b45b22018-08-15 18:17:00 +0200282 safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
Simon Kelleyff7eea22013-09-04 18:01:38 +0100283#endif
284 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000285 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000286 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100287 ifr.ifr_addr.sa_family = AF_INET;
288 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
289 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
290 else
291 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100292 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
293 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 +0100294 return;
295 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100296
Simon Kelleyff7eea22013-09-04 18:01:38 +0100297 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
298 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
299 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000300
Simon Kelleyff7eea22013-09-04 18:01:38 +0100301 /* unlinked contexts/relays are marked by context->current == context */
302 for (context = daemon->dhcp; context; context = context->next)
303 context->current = context;
304
305 for (relay = daemon->relay4; relay; relay = relay->next)
306 relay->current = relay;
307
308 parm.current = NULL;
309 parm.relay = NULL;
310 parm.relay_local.s_addr = 0;
311 parm.ind = iface_index;
312
Simon Kelleycc921df2019-01-02 22:48:59 +0000313 if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100314 {
315 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
316 for a secondary */
317 struct match_param match;
318
319 match.matched = 0;
320 match.ind = iface_index;
321
322 if (!daemon->if_addrs ||
323 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
324 !match.matched)
325 return;
326
327 iface_addr = match.addr;
328 /* make sure secondary address gets priority in case
329 there is more than one address on the interface in the same subnet */
330 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
331 }
332
333 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000334 return;
335
Simon Kelleyff7eea22013-09-04 18:01:38 +0100336 /* We're relaying this request */
337 if (parm.relay_local.s_addr != 0 &&
Simon Kelleyc7be0162017-05-10 22:21:53 +0100338 relay_upstream4(parm.relay, mess, (size_t)sz, iface_index))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100339 return;
340
341 /* May have configured relay, but not DHCP server */
342 if (!daemon->dhcp)
343 return;
344
345 lease_prune(NULL, now); /* lose any expired leases */
346 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100347 now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100348 lease_update_file(now);
349 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000350
Simon Kelleyff7eea22013-09-04 18:01:38 +0100351 if (iov.iov_len == 0)
352 return;
353 }
Floris Bos503c6092017-04-09 23:07:13 +0100354
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100355 msg.msg_name = &dest;
356 msg.msg_namelen = sizeof(dest);
357 msg.msg_control = NULL;
358 msg.msg_controllen = 0;
359 msg.msg_iov = &iov;
360 iov.iov_base = daemon->dhcp_packet.iov_base;
361
362 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000363 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100364
Simon Kelley3be34542004-09-11 19:12:13 +0100365#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100367#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000368
Simon Kelley316e2732010-01-22 20:16:09 +0000369 if (pxe_fd)
370 {
371 if (mess->ciaddr.s_addr != 0)
372 dest.sin_addr = mess->ciaddr;
373 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100374 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100375 {
376 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100377 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100378 dest.sin_addr = mess->giaddr;
379 }
380 else if (mess->ciaddr.s_addr)
381 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100382 /* If the client's idea of its own address tallys with
383 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100384 source port too, and send back to that. If we're replying
385 to a DHCPINFORM, trust the source address always. */
386 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100387 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100388 {
Simon Kelley9e038942008-05-30 20:06:34 +0100389 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100390 dest.sin_addr = mess->ciaddr;
391 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100392 }
Simon Kelley824af852008-02-12 20:43:05 +0000393#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000394 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100395 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000396 /* fill cmsg for outbound interface (both broadcast & unicast) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100397 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100398 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 msg.msg_controllen = sizeof(control_u);
400 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100401 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
Neil Jerramff325642016-05-03 22:45:14 +0100402 pkt->ipi_ifindex = rcvd_iface_index;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100403 pkt->ipi_spec_dst.s_addr = 0;
Simon Kelley76730132019-03-28 22:04:10 +0000404 msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
405 cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000406 cmptr->cmsg_level = IPPROTO_IP;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000407 cmptr->cmsg_type = IP_PKTINFO;
408
409 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
410 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
411 {
412 /* broadcast to 255.255.255.255 (or mac address invalid) */
413 dest.sin_addr.s_addr = INADDR_BROADCAST;
414 dest.sin_port = htons(daemon->dhcp_client_port);
415 }
416 else
417 {
418 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
419 struct sockaddr limits size to 14 bytes. */
420 dest.sin_addr = mess->yiaddr;
421 dest.sin_port = htons(daemon->dhcp_client_port);
422 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
423 arp_req.arp_ha.sa_family = mess->htype;
424 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
425 /* interface name already copied in */
426 arp_req.arp_flags = ATF_COM;
427 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
428 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
429 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000430 }
Simon Kelley824af852008-02-12 20:43:05 +0000431#elif defined(HAVE_SOLARIS_NETWORK)
432 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
433 {
434 /* broadcast to 255.255.255.255 (or mac address invalid) */
435 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100436 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000437 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100438 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000439 }
440 else
441 {
442 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
443 Note that this only works for ethernet on solaris, because we use SIOCSARP
444 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
445 mysteriously. Bah. Fall back to broadcast for other net types. */
446 struct arpreq req;
447 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100448 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000449 *((struct sockaddr_in *)&req.arp_pa) = dest;
450 req.arp_ha.sa_family = AF_UNSPEC;
451 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
452 req.arp_flags = ATF_COM;
453 ioctl(daemon->dhcpfd, SIOCSARP, &req);
454 }
455#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100456 else
457 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100458 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100459 return;
460 }
461#endif
462
Simon Kelley824af852008-02-12 20:43:05 +0000463#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000464 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000465#endif
466
Simon Kelleyff841eb2015-03-11 21:36:30 +0000467 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100468
469 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
470 if (errno != 0)
471 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
472 inet_ntoa(dest.sin_addr), strerror(errno));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000473}
Simon Kelley98079ea2015-10-13 20:30:32 +0100474
Simon Kelleyc72daea2012-01-05 21:33:27 +0000475/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100476static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000477 struct in_addr netmask, struct in_addr broadcast, void *vparam)
478{
479 struct match_param *param = vparam;
480 struct iname *tmp;
481
Simon Kelley3f2873d2013-05-14 11:28:47 +0100482 (void) label;
483
Simon Kelleyc72daea2012-01-05 21:33:27 +0000484 if (if_index == param->ind)
485 {
486 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
487 if ( tmp->addr.sa.sa_family == AF_INET &&
488 tmp->addr.in.sin_addr.s_addr == local.s_addr)
489 {
490 param->matched = 1;
491 param->addr = local;
492 param->netmask = netmask;
493 param->broadcast = broadcast;
494 break;
495 }
496 }
497
498 return 1;
499}
500
Simon Kelley0a852542005-03-23 20:28:59 +0000501/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100502 of each interface (and any relay address) and does the following things:
503
504 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
505 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
506 3) Fills in local (this host) and router (this host or relay) addresses.
507 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
508
klemens43517fc2017-02-19 15:53:37 +0000509 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100510
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000511static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
Simon Kelley0a852542005-03-23 20:28:59 +0000512{
513 struct dhcp_context *context;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100514
Simon Kelley0a852542005-03-23 20:28:59 +0000515 for (context = daemon->dhcp; context; context = context->next)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000516 if (!(context->flags & CONTEXT_NETMASK) &&
517 (is_same_net(addr, context->start, netmask) ||
518 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000519 {
520 if (context->netmask.s_addr != netmask.s_addr &&
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000521 !(is_same_net(addr, context->start, netmask) &&
522 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000523 {
524 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
525 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100526 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100527 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000528 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000529 context->netmask = netmask;
Simon Kelley0a852542005-03-23 20:28:59 +0000530 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000531}
532
533static int complete_context(struct in_addr local, int if_index, char *label,
534 struct in_addr netmask, struct in_addr broadcast, void *vparam)
535{
536 struct dhcp_context *context;
537 struct dhcp_relay *relay;
538 struct iface_param *param = vparam;
539 struct shared_network *share;
540
541 (void)label;
542
543 for (share = daemon->shared_networks; share; share = share->next)
544 {
Simon Kelley0a852542005-03-23 20:28:59 +0000545
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000546#ifdef HAVE_DHCP6
547 if (share->shared_addr.s_addr == 0)
548 continue;
549#endif
550
551 if (share->if_index != 0)
552 {
553 if (share->if_index != if_index)
554 continue;
555 }
556 else
557 {
558 if (share->match_addr.s_addr != local.s_addr)
559 continue;
560 }
561
562 for (context = daemon->dhcp; context; context = context->next)
563 {
564 if (context->netmask.s_addr != 0 &&
565 is_same_net(share->shared_addr, context->start, context->netmask) &&
566 is_same_net(share->shared_addr, context->end, context->netmask))
567 {
568 /* link it onto the current chain if we've not seen it before */
569 if (context->current == context)
570 {
571 /* For a shared network, we have no way to guess what the default route should be. */
572 context->router.s_addr = 0;
573 context->local = local; /* Use configured address for Server Identifier */
574 context->current = param->current;
575 param->current = context;
576 }
577
578 if (!(context->flags & CONTEXT_BRDCAST))
579 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
580 }
581 }
582 }
583
584 guess_range_netmask(local, netmask);
585
586 for (context = daemon->dhcp; context; context = context->next)
587 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100588 if (context->netmask.s_addr != 0 &&
589 is_same_net(local, context->start, context->netmask) &&
590 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000591 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100592 /* link it onto the current chain if we've not seen it before */
593 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000594 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100595 context->router = local;
596 context->local = local;
597 context->current = param->current;
598 param->current = context;
599 }
600
601 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000602 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100603 if (is_same_net(broadcast, context->start, context->netmask))
604 context->broadcast = broadcast;
605 else
Simon Kelley0a852542005-03-23 20:28:59 +0000606 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
607 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100608 }
Simon Kelley0a852542005-03-23 20:28:59 +0000609 }
610
Simon Kelleyff7eea22013-09-04 18:01:38 +0100611 for (relay = daemon->relay4; relay; relay = relay->next)
Simon Kelleycc921df2019-01-02 22:48:59 +0000612 if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay &&
Simon Kelleyff7eea22013-09-04 18:01:38 +0100613 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
614 {
615 relay->current = param->relay;
616 param->relay = relay;
617 param->relay_local = local;
618 }
619
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100620 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000621}
622
Simon Kelley824af852008-02-12 20:43:05 +0000623struct dhcp_context *address_available(struct dhcp_context *context,
624 struct in_addr taddr,
625 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000626{
Simon Kelley36717ee2004-09-20 19:20:58 +0100627 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100628 possible ranges. Make sure that the address isn't in use
629 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000630
Simon Kelley36717ee2004-09-20 19:20:58 +0100631 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100632 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100633
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100634 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100635 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636 return NULL;
637
638 for (tmp = context; tmp; tmp = tmp->current)
639 {
640 start = ntohl(tmp->start.s_addr);
641 end = ntohl(tmp->end.s_addr);
642
Simon Kelley7de060b2011-08-26 17:24:52 +0100643 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100644 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000645 addr <= end &&
646 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100647 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100648 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000649
Simon Kelley59353a62004-11-21 19:34:28 +0000650 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000651}
Simon Kelley59353a62004-11-21 19:34:28 +0000652
Simon Kelley824af852008-02-12 20:43:05 +0000653struct dhcp_context *narrow_context(struct dhcp_context *context,
654 struct in_addr taddr,
655 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000656{
Simon Kelleye17fb622006-01-14 20:33:46 +0000657 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000658 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000659 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000660 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000661 any dhcp-range. In that case we return a static range if possible, or failing that,
662 any context on the correct subnet. (If there's more than one, this is a dodgy
663 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000664
Simon Kelleye17fb622006-01-14 20:33:46 +0000665 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000666
Simon Kelley824af852008-02-12 20:43:05 +0000667 if (!(tmp = address_available(context, taddr, netids)))
668 {
669 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100670 if (match_netid(tmp->filter, netids, 1) &&
671 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100672 (tmp->flags & CONTEXT_STATIC))
673 break;
Simon Kelley824af852008-02-12 20:43:05 +0000674
675 if (!tmp)
676 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100677 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100678 is_same_net(taddr, tmp->start, tmp->netmask) &&
679 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000680 break;
681 }
Simon Kelley59353a62004-11-21 19:34:28 +0000682
Simon Kelley824af852008-02-12 20:43:05 +0000683 /* Only one context allowed now */
684 if (tmp)
685 tmp->current = NULL;
686
687 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000688}
689
Simon Kelleydfa666f2004-08-02 18:27:27 +0100690struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
691{
692 struct dhcp_config *config;
693
694 for (config = configs; config; config = config->next)
695 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
696 return config;
697
698 return NULL;
699}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000700
Simon Kelley5ce3e762017-04-28 22:14:20 +0100701/* Check if and address is in use by sending ICMP ping.
702 This wrapper handles a cache and load-limiting.
703 Return is NULL is address in use, or a pointer to a cache entry
704 recording that it isn't. */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100705struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100706{
707 static struct ping_result dummy;
708 struct ping_result *r, *victim = NULL;
709 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
710 ((float)PING_WAIT)));
711
712 /* check if we failed to ping addr sometime in the last
713 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
714 This avoids problems when a stupid client bangs
715 on us repeatedly. As a final check, if we did more
716 than 60% of the possible ping checks in the last
717 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
718 for (count = 0, r = daemon->ping_results; r; r = r->next)
719 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
720 victim = r; /* old record */
721 else
722 {
723 count++;
724 if (r->addr.s_addr == addr.s_addr)
725 return r;
726 }
727
728 /* didn't find cached entry */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100729 if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100730 {
Simon Kelleyc7be0162017-05-10 22:21:53 +0100731 /* overloaded, or configured not to check, loopback interface, return "not in use" */
Simon Kelley0669ee72018-05-04 16:46:24 +0100732 dummy.hash = hash;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100733 return &dummy;
734 }
735 else if (icmp_ping(addr))
736 return NULL; /* address in use. */
737 else
738 {
739 /* at this point victim may hold an expired record */
740 if (!victim)
741 {
742 if ((victim = whine_malloc(sizeof(struct ping_result))))
743 {
744 victim->next = daemon->ping_results;
745 daemon->ping_results = victim;
746 }
747 }
748
749 /* record that this address is OK for 30s
750 without more ping checks */
751 if (victim)
752 {
753 victim->addr = addr;
754 victim->time = now;
755 victim->hash = hash;
756 }
757 return victim;
758 }
759}
760
Simon Kelley5aabfc72007-08-29 11:24:47 +0100761int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000762 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100763 struct dhcp_netid *netids, time_t now, int loopback)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000764{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100765 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000766 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000767 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000768
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100769 struct in_addr start, addr;
770 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000771 int i, pass;
772 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100773
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100774 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
775 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100776 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100777 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100778
779 /* j == 0 is marker */
780 if (j == 0)
781 j = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100782
Simon Kelleycdeda282006-03-16 20:16:06 +0000783 for (pass = 0; pass <= 1; pass++)
784 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100785 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000786 continue;
787 else if (!match_netid(c->filter, netids, pass))
788 continue;
789 else
790 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100791 if (option_bool(OPT_CONSEC_ADDR))
792 /* seed is largest extant lease addr in this context */
793 start = lease_find_max_addr(c);
794 else
795 /* pick a seed based on hwaddr */
796 start.s_addr = htonl(ntohl(c->start.s_addr) +
797 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
798
799 /* iterate until we find a free address. */
800 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000801
802 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100803 /* eliminate addresses in use by the server. */
804 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100805 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100806 break;
807
Simon Kelley73a08a22009-02-05 20:28:08 +0000808 /* Addresses which end in .255 and .0 are broken in Windows even when using
809 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
810 then 192.168.0.255 is a valid IP address, but not for Windows as it's
811 in the class C range. See KB281579. We therefore don't allocate these
812 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100813 if (!d &&
814 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000815 !config_find_by_address(daemon->dhcp_conf, addr) &&
816 (!IN_CLASSC(ntohl(addr.s_addr)) ||
817 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000818 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000819 /* in consec-ip mode, skip addresses equal to
820 the number of addresses rejected by clients. This
821 should avoid the same client being offered the same
822 address after it has rjected it. */
823 if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
824 c->addr_epoch--;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100825 else
826 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000827 struct ping_result *r;
828
829 if ((r = do_icmp_ping(now, addr, j, loopback)))
830 {
831 /* consec-ip mode: we offered this address for another client
832 (different hash) recently, don't offer it to this one. */
833 if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
834 {
835 *addrp = addr;
836 return 1;
837 }
838 }
839 else
840 {
841 /* address in use: perturb address selection so that we are
842 less likely to try this address again. */
843 if (!option_bool(OPT_CONSEC_ADDR))
844 c->addr_epoch++;
845 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100846 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000847 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100848
Simon Kelleycdeda282006-03-16 20:16:06 +0000849 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
850
851 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
852 addr = c->start;
853
854 } while (addr.s_addr != start.s_addr);
855 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100856
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000857 return 0;
858}
859
Simon Kelley5aabfc72007-08-29 11:24:47 +0100860void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000861{
Simon Kelley44a2a312004-03-10 20:04:35 +0000862 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000863 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100864 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100865 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000866 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100867 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100868 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100869 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000870 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100871
872 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000873
874 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100875 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100876 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100877 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100878 }
879
Simon Kelley849a8352006-06-09 21:02:31 +0100880 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100881 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100882 {
883 tmp = config->next;
884 if (config->flags & CONFIG_FROM_ETHERS)
885 {
886 *up = tmp;
887 /* cannot have a clid */
888 if (config->flags & CONFIG_NAME)
889 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000890 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100891 free(config);
892 }
893 else
894 up = &config->next;
895 }
896
Simon Kelley44a2a312004-03-10 20:04:35 +0000897 while (fgets(buff, MAXDNAME, f))
898 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100899 char *host = NULL;
900
Simon Kelleyb8187c82005-11-26 21:46:27 +0000901 lineno++;
902
Simon Kelley824af852008-02-12 20:43:05 +0000903 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000904 buff[strlen(buff)-1] = 0;
905
Simon Kelley73a08a22009-02-05 20:28:08 +0000906 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000907 continue;
908
Simon Kelley824af852008-02-12 20:43:05 +0000909 for (ip = buff; *ip && !isspace((int)*ip); ip++);
910 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000911 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000912 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000913 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100914 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000915 continue;
916 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000917
918 /* check for name or dotted-quad */
919 for (cp = ip; *cp; cp++)
920 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
921 break;
922
923 if (!*cp)
924 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000925 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000926 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100927 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000928 continue;
929 }
930
Simon Kelley33820b72004-04-03 21:10:00 +0100931 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000932
Simon Kelley16972692006-10-16 20:04:18 +0100933 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100934 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000935 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000936 }
937 else
938 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100939 int nomem;
940 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000941 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100942 if (!nomem)
943 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
944 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000945 continue;
946 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100947
Simon Kelley33820b72004-04-03 21:10:00 +0100948 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000949
Simon Kelley16972692006-10-16 20:04:18 +0100950 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100951 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000952 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000953 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100954
955 if (config && (config->flags & CONFIG_FROM_ETHERS))
956 {
957 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
958 continue;
959 }
960
Simon Kelley1cff1662004-03-12 08:12:58 +0000961 if (!config)
962 {
Simon Kelley16972692006-10-16 20:04:18 +0100963 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000964 {
965 struct hwaddr_config *conf_addr = config->hwaddr;
966 if (conf_addr &&
967 conf_addr->next == NULL &&
968 conf_addr->wildcard_mask == 0 &&
969 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
970 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
971 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
972 break;
973 }
Simon Kelley33820b72004-04-03 21:10:00 +0100974
975 if (!config)
976 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100977 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100978 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100979 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000980 config->hwaddr = NULL;
981 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100982 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100983 config->next = daemon->dhcp_conf;
984 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100985 }
986
987 config->flags |= flags;
988
989 if (flags & CONFIG_NAME)
990 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100991 config->hostname = host;
992 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100993 }
994
995 if (flags & CONFIG_ADDR)
996 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000997 }
Simon Kelley33820b72004-04-03 21:10:00 +0100998
Simon Kelley9009d742008-11-14 20:04:27 +0000999 config->flags |= CONFIG_NOCLID;
1000 if (!config->hwaddr)
1001 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
1002 if (config->hwaddr)
1003 {
1004 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
1005 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
1006 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
1007 config->hwaddr->wildcard_mask = 0;
1008 config->hwaddr->next = NULL;
1009 }
Simon Kelley33820b72004-04-03 21:10:00 +01001010 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +01001011
1012 free(host);
1013
Simon Kelley44a2a312004-03-10 20:04:35 +00001014 }
1015
1016 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +01001017
Simon Kelley7622fc02009-06-04 20:32:05 +01001018 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +00001019}
1020
Simon Kelley44a2a312004-03-10 20:04:35 +00001021
Simon Kelleybb01cb92004-12-13 20:56:23 +00001022/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
1023 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +01001024 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001025 so check here that the domain name is legal as a hostname.
1026 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001027char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +00001028{
Simon Kelley824af852008-02-12 20:43:05 +00001029 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +00001030
1031 if (daemon->port == 0)
1032 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +00001033
Simon Kelleycc921df2019-01-02 22:48:59 +00001034 lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001035
Simon Kelleybb01cb92004-12-13 20:56:23 +00001036 if (lookup && (lookup->flags & F_HOSTS))
1037 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001038 char *dot, *hostname = cache_get_name(lookup);
1039 dot = strchr(hostname, '.');
1040
1041 if (dot && strlen(dot+1) != 0)
1042 {
1043 char *d2 = get_domain(addr);
1044 if (!d2 || !hostname_isequal(dot+1, d2))
1045 return NULL; /* wrong domain */
1046 }
1047
1048 if (!legal_hostname(hostname))
1049 return NULL;
1050
Petr Menšík47b45b22018-08-15 18:17:00 +02001051 safe_strncpy(daemon->dhcp_buff, hostname, 256);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001052 strip_hostname(daemon->dhcp_buff);
1053
1054 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001055 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001056
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001057 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001058}
1059
Simon Kelleyff7eea22013-09-04 18:01:38 +01001060static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
1061{
1062 /* ->local is same value for all relays on ->current chain */
Simon Kelleycc921df2019-01-02 22:48:59 +00001063 union all_addr from;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001064
1065 if (mess->op != BOOTREQUEST)
1066 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001067
Simon Kelleyff7eea22013-09-04 18:01:38 +01001068 /* source address == relay address */
Simon Kelleycc921df2019-01-02 22:48:59 +00001069 from.addr4 = relay->local.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001070
1071 /* already gatewayed ? */
1072 if (mess->giaddr.s_addr)
1073 {
1074 /* if so check if by us, to stomp on loops. */
Simon Kelleycc921df2019-01-02 22:48:59 +00001075 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001076 return 1;
1077 }
1078 else
1079 {
1080 /* plug in our address */
Simon Kelleycc921df2019-01-02 22:48:59 +00001081 mess->giaddr.s_addr = relay->local.addr4.s_addr;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001082 }
1083
1084 if ((mess->hops++) > 20)
1085 return 1;
1086
1087 for (; relay; relay = relay->current)
1088 {
1089 union mysockaddr to;
1090
1091 to.sa.sa_family = AF_INET;
Simon Kelleycc921df2019-01-02 22:48:59 +00001092 to.in.sin_addr = relay->server.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001093 to.in.sin_port = htons(daemon->dhcp_server_port);
1094
1095 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1096
1097 if (option_bool(OPT_LOG_OPTS))
1098 {
1099 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleycc921df2019-01-02 22:48:59 +00001100 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr4));
Simon Kelleyff7eea22013-09-04 18:01:38 +01001101 }
1102
1103 /* Save this for replies */
1104 relay->iface_index = iface_index;
1105 }
1106
1107 return 1;
1108}
1109
1110
1111static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1112{
1113 struct dhcp_relay *relay;
1114
1115 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1116 return NULL;
1117
1118 for (relay = daemon->relay4; relay; relay = relay->next)
1119 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001120 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001121 {
1122 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1123 return relay->iface_index != 0 ? relay : NULL;
1124 }
1125 }
1126
1127 return NULL;
1128}
1129
1130#endif