blob: bbd35002db0de8bbfdffa2605031047c1b8810e7 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 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
Kyle Swenson545712c2021-11-17 12:25:04 -0700284/* CRADLEPOINT */
285 parm.current = NULL;
286/* CRADLEPOINT */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100287 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000288 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000289 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100290 ifr.ifr_addr.sa_family = AF_INET;
291 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
292 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
293 else
294 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100295 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
296 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 +0100297 return;
298 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100299
Simon Kelleyff7eea22013-09-04 18:01:38 +0100300 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
301 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
302 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000303
Simon Kelleyff7eea22013-09-04 18:01:38 +0100304 /* unlinked contexts/relays are marked by context->current == context */
305 for (context = daemon->dhcp; context; context = context->next)
306 context->current = context;
307
308 for (relay = daemon->relay4; relay; relay = relay->next)
309 relay->current = relay;
310
311 parm.current = NULL;
312 parm.relay = NULL;
313 parm.relay_local.s_addr = 0;
314 parm.ind = iface_index;
315
Simon Kelleycc921df2019-01-02 22:48:59 +0000316 if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100317 {
318 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
319 for a secondary */
320 struct match_param match;
321
322 match.matched = 0;
323 match.ind = iface_index;
324
325 if (!daemon->if_addrs ||
326 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
327 !match.matched)
328 return;
329
330 iface_addr = match.addr;
331 /* make sure secondary address gets priority in case
332 there is more than one address on the interface in the same subnet */
333 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
334 }
335
336 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000337 return;
338
Simon Kelleyff7eea22013-09-04 18:01:38 +0100339 /* We're relaying this request */
340 if (parm.relay_local.s_addr != 0 &&
Simon Kelleyc7be0162017-05-10 22:21:53 +0100341 relay_upstream4(parm.relay, mess, (size_t)sz, iface_index))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100342 return;
343
344 /* May have configured relay, but not DHCP server */
345 if (!daemon->dhcp)
346 return;
347
348 lease_prune(NULL, now); /* lose any expired leases */
349 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100350 now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100351 lease_update_file(now);
352 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000353
Simon Kelleyff7eea22013-09-04 18:01:38 +0100354 if (iov.iov_len == 0)
355 return;
356 }
Floris Bos503c6092017-04-09 23:07:13 +0100357
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100358 msg.msg_name = &dest;
359 msg.msg_namelen = sizeof(dest);
360 msg.msg_control = NULL;
361 msg.msg_controllen = 0;
362 msg.msg_iov = &iov;
363 iov.iov_base = daemon->dhcp_packet.iov_base;
364
365 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000366 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100367
Simon Kelley3be34542004-09-11 19:12:13 +0100368#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100369 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100370#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000371
Simon Kelley316e2732010-01-22 20:16:09 +0000372 if (pxe_fd)
373 {
374 if (mess->ciaddr.s_addr != 0)
375 dest.sin_addr = mess->ciaddr;
376 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100377 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100378 {
379 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100380 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100381 dest.sin_addr = mess->giaddr;
382 }
383 else if (mess->ciaddr.s_addr)
384 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100385 /* If the client's idea of its own address tallys with
386 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100387 source port too, and send back to that. If we're replying
388 to a DHCPINFORM, trust the source address always. */
389 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100390 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100391 {
Simon Kelley9e038942008-05-30 20:06:34 +0100392 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100393 dest.sin_addr = mess->ciaddr;
394 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100395 }
Simon Kelley824af852008-02-12 20:43:05 +0000396#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000397 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100398 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000399 /* fill cmsg for outbound interface (both broadcast & unicast) */
Kyle Swenson545712c2021-11-17 12:25:04 -0700400/* CRADLEPOINT */
401 if (!parm.current || parm.current->forcedaddress.s_addr == 0)
402 {
403 struct in_pktinfo *pkt;
404 msg.msg_control = control_u.control;
405 msg.msg_controllen = sizeof(control_u);
406 cmptr = CMSG_FIRSTHDR(&msg);
407 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
408 pkt->ipi_ifindex = rcvd_iface_index;
409 pkt->ipi_spec_dst.s_addr = 0;
410 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
411 cmptr->cmsg_level = IPPROTO_IP;
412 cmptr->cmsg_type = IP_PKTINFO;
413 }
414/* CRADLEPOINT */
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000415
416 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
417 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
418 {
419 /* broadcast to 255.255.255.255 (or mac address invalid) */
420 dest.sin_addr.s_addr = INADDR_BROADCAST;
421 dest.sin_port = htons(daemon->dhcp_client_port);
422 }
423 else
424 {
425 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
426 struct sockaddr limits size to 14 bytes. */
427 dest.sin_addr = mess->yiaddr;
428 dest.sin_port = htons(daemon->dhcp_client_port);
429 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
430 arp_req.arp_ha.sa_family = mess->htype;
431 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
432 /* interface name already copied in */
433 arp_req.arp_flags = ATF_COM;
434 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
435 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
436 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000437 }
Simon Kelley824af852008-02-12 20:43:05 +0000438#elif defined(HAVE_SOLARIS_NETWORK)
439 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
440 {
441 /* broadcast to 255.255.255.255 (or mac address invalid) */
442 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100443 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000444 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100445 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000446 }
447 else
448 {
449 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
450 Note that this only works for ethernet on solaris, because we use SIOCSARP
451 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
452 mysteriously. Bah. Fall back to broadcast for other net types. */
453 struct arpreq req;
454 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100455 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000456 *((struct sockaddr_in *)&req.arp_pa) = dest;
457 req.arp_ha.sa_family = AF_UNSPEC;
458 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
459 req.arp_flags = ATF_COM;
460 ioctl(daemon->dhcpfd, SIOCSARP, &req);
461 }
462#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100463 else
464 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100465 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100466 return;
467 }
468#endif
469
Simon Kelley824af852008-02-12 20:43:05 +0000470#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000471 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000472#endif
Kyle Swenson545712c2021-11-17 12:25:04 -0700473
474/* CRADLEPOINT */
475#ifdef HAVE_LINUX_NETWORK
476 /* if we're forcing the address, use IP_PKTINFO and IP_TRANSPARENT to lie about
477 * our source address and force it out the same iface we got it on. */
478 if (parm.current && parm.current->forcedaddress.s_addr != 0)
479 {
480 struct in_pktinfo *pkt;
481 int transparent = 1;
482
483 msg.msg_control = control_u.control;
484 msg.msg_controllen = sizeof(control_u);
485 cmptr = CMSG_FIRSTHDR(&msg);
486 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
487 pkt->ipi_ifindex = rcvd_iface_index;
488 pkt->ipi_spec_dst = parm.current->forcedaddress;
489 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
490 cmptr->cmsg_level = IPPROTO_IP;
491 cmptr->cmsg_type = IP_PKTINFO;
492 #define IP_TRANSPARENT 19 /* XXX: This isn't in uclibc headers, hardcode for now */
493 setsockopt(fd, SOL_IP, IP_TRANSPARENT, &transparent, sizeof(transparent));
494 }
495#endif
496/* CRADLEPOINT */
497
Simon Kelleyff841eb2015-03-11 21:36:30 +0000498 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100499
500 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
501 if (errno != 0)
502 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
503 inet_ntoa(dest.sin_addr), strerror(errno));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000504}
Simon Kelley98079ea2015-10-13 20:30:32 +0100505
Simon Kelleyc72daea2012-01-05 21:33:27 +0000506/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100507static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000508 struct in_addr netmask, struct in_addr broadcast, void *vparam)
509{
510 struct match_param *param = vparam;
511 struct iname *tmp;
512
Simon Kelley3f2873d2013-05-14 11:28:47 +0100513 (void) label;
514
Simon Kelleyc72daea2012-01-05 21:33:27 +0000515 if (if_index == param->ind)
516 {
517 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
518 if ( tmp->addr.sa.sa_family == AF_INET &&
519 tmp->addr.in.sin_addr.s_addr == local.s_addr)
520 {
521 param->matched = 1;
522 param->addr = local;
523 param->netmask = netmask;
524 param->broadcast = broadcast;
525 break;
526 }
527 }
528
529 return 1;
530}
531
Simon Kelley0a852542005-03-23 20:28:59 +0000532/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100533 of each interface (and any relay address) and does the following things:
534
535 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
536 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
537 3) Fills in local (this host) and router (this host or relay) addresses.
538 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
539
klemens43517fc2017-02-19 15:53:37 +0000540 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100541
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000542static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
Simon Kelley0a852542005-03-23 20:28:59 +0000543{
544 struct dhcp_context *context;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100545
Simon Kelley0a852542005-03-23 20:28:59 +0000546 for (context = daemon->dhcp; context; context = context->next)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000547 if (!(context->flags & CONTEXT_NETMASK) &&
548 (is_same_net(addr, context->start, netmask) ||
549 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000550 {
551 if (context->netmask.s_addr != netmask.s_addr &&
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000552 !(is_same_net(addr, context->start, netmask) &&
553 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000554 {
555 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
556 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100557 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100558 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000559 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000560 context->netmask = netmask;
Simon Kelley0a852542005-03-23 20:28:59 +0000561 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000562}
563
564static int complete_context(struct in_addr local, int if_index, char *label,
565 struct in_addr netmask, struct in_addr broadcast, void *vparam)
566{
567 struct dhcp_context *context;
568 struct dhcp_relay *relay;
569 struct iface_param *param = vparam;
570 struct shared_network *share;
Kyle Swenson545712c2021-11-17 12:25:04 -0700571/* CRADLEPOINT */
572 char namebuff[IF_NAMESIZE];
573/* CRADLEPOINT */
574
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000575 (void)label;
576
577 for (share = daemon->shared_networks; share; share = share->next)
578 {
Simon Kelley0a852542005-03-23 20:28:59 +0000579
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000580#ifdef HAVE_DHCP6
581 if (share->shared_addr.s_addr == 0)
582 continue;
583#endif
584
585 if (share->if_index != 0)
586 {
587 if (share->if_index != if_index)
588 continue;
589 }
590 else
591 {
592 if (share->match_addr.s_addr != local.s_addr)
593 continue;
594 }
595
596 for (context = daemon->dhcp; context; context = context->next)
597 {
598 if (context->netmask.s_addr != 0 &&
599 is_same_net(share->shared_addr, context->start, context->netmask) &&
600 is_same_net(share->shared_addr, context->end, context->netmask))
601 {
602 /* link it onto the current chain if we've not seen it before */
603 if (context->current == context)
604 {
605 /* For a shared network, we have no way to guess what the default route should be. */
606 context->router.s_addr = 0;
607 context->local = local; /* Use configured address for Server Identifier */
608 context->current = param->current;
609 param->current = context;
610 }
611
612 if (!(context->flags & CONTEXT_BRDCAST))
613 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
614 }
615 }
616 }
617
618 guess_range_netmask(local, netmask);
619
620 for (context = daemon->dhcp; context; context = context->next)
621 {
Kyle Swenson545712c2021-11-17 12:25:04 -0700622/* CRADLEPOINT */
623 if (context->forcedinterface &&
624 indextoname(daemon->dhcpfd, if_index, namebuff) &&
625 strcmp(context->forcedinterface, namebuff) == 0)
626 {
627 /* link it onto the current chain if we've not seen it before */
628 if (if_index == param->ind && context->current == context)
629 {
630 if (context->forcedaddress.s_addr != 0)
631 {
632 context->router = context->forcedaddress;
633 context->local = context->forcedaddress;
634 }
635 else
636 {
637 context->router = local;
638 context->local = local;
639 }
640
641 context->current = param->current;
642 param->current = context;
643 }
644
645 if (!(context->flags & CONTEXT_BRDCAST))
646 {
647 if (is_same_net(broadcast, context->start, context->netmask))
648 context->broadcast = broadcast;
649 else
650 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
651 }
652 }
653 else if (context->netmask.s_addr != 0 &&
654/* CRADLEPOINT */
655 is_same_net(local, context->start, context->netmask) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100656 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000657 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100658 /* link it onto the current chain if we've not seen it before */
659 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000660 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100661 context->router = local;
662 context->local = local;
663 context->current = param->current;
664 param->current = context;
665 }
666
667 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000668 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100669 if (is_same_net(broadcast, context->start, context->netmask))
670 context->broadcast = broadcast;
671 else
Simon Kelley0a852542005-03-23 20:28:59 +0000672 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
673 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100674 }
Simon Kelley0a852542005-03-23 20:28:59 +0000675 }
676
Simon Kelleyff7eea22013-09-04 18:01:38 +0100677 for (relay = daemon->relay4; relay; relay = relay->next)
Simon Kelleycc921df2019-01-02 22:48:59 +0000678 if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay &&
Simon Kelleyff7eea22013-09-04 18:01:38 +0100679 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
680 {
681 relay->current = param->relay;
682 param->relay = relay;
683 param->relay_local = local;
684 }
685
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100686 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000687}
688
Simon Kelley824af852008-02-12 20:43:05 +0000689struct dhcp_context *address_available(struct dhcp_context *context,
690 struct in_addr taddr,
691 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000692{
Simon Kelley36717ee2004-09-20 19:20:58 +0100693 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100694 possible ranges. Make sure that the address isn't in use
695 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000696
Simon Kelley36717ee2004-09-20 19:20:58 +0100697 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100698 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100699
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100700 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100701 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100702 return NULL;
703
704 for (tmp = context; tmp; tmp = tmp->current)
705 {
706 start = ntohl(tmp->start.s_addr);
707 end = ntohl(tmp->end.s_addr);
708
Simon Kelley7de060b2011-08-26 17:24:52 +0100709 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100710 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000711 addr <= end &&
712 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100713 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100714 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000715
Simon Kelley59353a62004-11-21 19:34:28 +0000716 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000717}
Simon Kelley59353a62004-11-21 19:34:28 +0000718
Simon Kelley824af852008-02-12 20:43:05 +0000719struct dhcp_context *narrow_context(struct dhcp_context *context,
720 struct in_addr taddr,
721 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000722{
Simon Kelleye17fb622006-01-14 20:33:46 +0000723 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000724 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000725 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000726 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000727 any dhcp-range. In that case we return a static range if possible, or failing that,
728 any context on the correct subnet. (If there's more than one, this is a dodgy
729 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000730
Simon Kelleye17fb622006-01-14 20:33:46 +0000731 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000732
Simon Kelley824af852008-02-12 20:43:05 +0000733 if (!(tmp = address_available(context, taddr, netids)))
734 {
735 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100736 if (match_netid(tmp->filter, netids, 1) &&
737 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100738 (tmp->flags & CONTEXT_STATIC))
739 break;
Simon Kelley824af852008-02-12 20:43:05 +0000740
741 if (!tmp)
742 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100743 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100744 is_same_net(taddr, tmp->start, tmp->netmask) &&
745 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000746 break;
747 }
Simon Kelley59353a62004-11-21 19:34:28 +0000748
Simon Kelley824af852008-02-12 20:43:05 +0000749 /* Only one context allowed now */
750 if (tmp)
751 tmp->current = NULL;
752
753 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000754}
755
Simon Kelleydfa666f2004-08-02 18:27:27 +0100756struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
757{
758 struct dhcp_config *config;
759
760 for (config = configs; config; config = config->next)
761 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
762 return config;
763
764 return NULL;
765}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000766
Simon Kelley5ce3e762017-04-28 22:14:20 +0100767/* Check if and address is in use by sending ICMP ping.
768 This wrapper handles a cache and load-limiting.
769 Return is NULL is address in use, or a pointer to a cache entry
770 recording that it isn't. */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100771struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100772{
773 static struct ping_result dummy;
774 struct ping_result *r, *victim = NULL;
775 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
776 ((float)PING_WAIT)));
777
778 /* check if we failed to ping addr sometime in the last
779 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
780 This avoids problems when a stupid client bangs
781 on us repeatedly. As a final check, if we did more
782 than 60% of the possible ping checks in the last
783 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
784 for (count = 0, r = daemon->ping_results; r; r = r->next)
785 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
786 victim = r; /* old record */
787 else
788 {
789 count++;
790 if (r->addr.s_addr == addr.s_addr)
791 return r;
792 }
793
794 /* didn't find cached entry */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100795 if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100796 {
Simon Kelleyc7be0162017-05-10 22:21:53 +0100797 /* overloaded, or configured not to check, loopback interface, return "not in use" */
Simon Kelley0669ee72018-05-04 16:46:24 +0100798 dummy.hash = hash;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100799 return &dummy;
800 }
801 else if (icmp_ping(addr))
802 return NULL; /* address in use. */
803 else
804 {
805 /* at this point victim may hold an expired record */
806 if (!victim)
807 {
808 if ((victim = whine_malloc(sizeof(struct ping_result))))
809 {
810 victim->next = daemon->ping_results;
811 daemon->ping_results = victim;
812 }
813 }
814
815 /* record that this address is OK for 30s
816 without more ping checks */
817 if (victim)
818 {
819 victim->addr = addr;
820 victim->time = now;
821 victim->hash = hash;
822 }
823 return victim;
824 }
825}
826
Simon Kelley5aabfc72007-08-29 11:24:47 +0100827int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000828 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100829 struct dhcp_netid *netids, time_t now, int loopback)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000830{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100831 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000832 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000833 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000834
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100835 struct in_addr start, addr;
836 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000837 int i, pass;
838 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100839
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100840 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
841 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100842 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100843 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100844
845 /* j == 0 is marker */
846 if (j == 0)
847 j = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100848
Simon Kelleycdeda282006-03-16 20:16:06 +0000849 for (pass = 0; pass <= 1; pass++)
850 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100851 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000852 continue;
853 else if (!match_netid(c->filter, netids, pass))
854 continue;
855 else
856 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100857 if (option_bool(OPT_CONSEC_ADDR))
858 /* seed is largest extant lease addr in this context */
859 start = lease_find_max_addr(c);
860 else
861 /* pick a seed based on hwaddr */
862 start.s_addr = htonl(ntohl(c->start.s_addr) +
863 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
864
865 /* iterate until we find a free address. */
866 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000867
868 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100869 /* eliminate addresses in use by the server. */
870 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100871 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100872 break;
873
Simon Kelley73a08a22009-02-05 20:28:08 +0000874 /* Addresses which end in .255 and .0 are broken in Windows even when using
875 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
876 then 192.168.0.255 is a valid IP address, but not for Windows as it's
877 in the class C range. See KB281579. We therefore don't allocate these
878 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100879 if (!d &&
880 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000881 !config_find_by_address(daemon->dhcp_conf, addr) &&
882 (!IN_CLASSC(ntohl(addr.s_addr)) ||
883 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000884 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000885 /* in consec-ip mode, skip addresses equal to
886 the number of addresses rejected by clients. This
887 should avoid the same client being offered the same
888 address after it has rjected it. */
889 if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
890 c->addr_epoch--;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100891 else
892 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000893 struct ping_result *r;
894
895 if ((r = do_icmp_ping(now, addr, j, loopback)))
896 {
897 /* consec-ip mode: we offered this address for another client
898 (different hash) recently, don't offer it to this one. */
899 if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
900 {
901 *addrp = addr;
902 return 1;
903 }
904 }
905 else
906 {
907 /* address in use: perturb address selection so that we are
908 less likely to try this address again. */
909 if (!option_bool(OPT_CONSEC_ADDR))
910 c->addr_epoch++;
911 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100912 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000913 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100914
Simon Kelleycdeda282006-03-16 20:16:06 +0000915 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
916
917 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
918 addr = c->start;
919
920 } while (addr.s_addr != start.s_addr);
921 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100922
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000923 return 0;
924}
925
Simon Kelley5aabfc72007-08-29 11:24:47 +0100926void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000927{
Simon Kelley44a2a312004-03-10 20:04:35 +0000928 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000929 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100930 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100931 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000932 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100933 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100934 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100935 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000936 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100937
938 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000939
940 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100941 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100942 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100943 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100944 }
945
Simon Kelley849a8352006-06-09 21:02:31 +0100946 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100947 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100948 {
949 tmp = config->next;
950 if (config->flags & CONFIG_FROM_ETHERS)
951 {
952 *up = tmp;
953 /* cannot have a clid */
954 if (config->flags & CONFIG_NAME)
955 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000956 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100957 free(config);
958 }
959 else
960 up = &config->next;
961 }
962
Simon Kelley44a2a312004-03-10 20:04:35 +0000963 while (fgets(buff, MAXDNAME, f))
964 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100965 char *host = NULL;
966
Simon Kelleyb8187c82005-11-26 21:46:27 +0000967 lineno++;
968
Simon Kelley824af852008-02-12 20:43:05 +0000969 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000970 buff[strlen(buff)-1] = 0;
971
Simon Kelley73a08a22009-02-05 20:28:08 +0000972 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000973 continue;
974
Simon Kelley824af852008-02-12 20:43:05 +0000975 for (ip = buff; *ip && !isspace((int)*ip); ip++);
976 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000977 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000978 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000979 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100980 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000981 continue;
982 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000983
984 /* check for name or dotted-quad */
985 for (cp = ip; *cp; cp++)
986 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
987 break;
988
989 if (!*cp)
990 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000991 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000992 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100993 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000994 continue;
995 }
996
Simon Kelley33820b72004-04-03 21:10:00 +0100997 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000998
Simon Kelley16972692006-10-16 20:04:18 +0100999 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +01001000 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +00001001 break;
Simon Kelley44a2a312004-03-10 20:04:35 +00001002 }
1003 else
1004 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001005 int nomem;
1006 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001007 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001008 if (!nomem)
1009 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
1010 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001011 continue;
1012 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001013
Simon Kelley33820b72004-04-03 21:10:00 +01001014 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +00001015
Simon Kelley16972692006-10-16 20:04:18 +01001016 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +01001017 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +00001018 break;
Simon Kelley44a2a312004-03-10 20:04:35 +00001019 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001020
1021 if (config && (config->flags & CONFIG_FROM_ETHERS))
1022 {
1023 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
1024 continue;
1025 }
1026
Simon Kelley1cff1662004-03-12 08:12:58 +00001027 if (!config)
1028 {
Simon Kelley16972692006-10-16 20:04:18 +01001029 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +00001030 {
1031 struct hwaddr_config *conf_addr = config->hwaddr;
1032 if (conf_addr &&
1033 conf_addr->next == NULL &&
1034 conf_addr->wildcard_mask == 0 &&
1035 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
1036 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
1037 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
1038 break;
1039 }
Simon Kelley33820b72004-04-03 21:10:00 +01001040
1041 if (!config)
1042 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001043 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +01001044 continue;
Simon Kelley849a8352006-06-09 21:02:31 +01001045 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +00001046 config->hwaddr = NULL;
1047 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001048 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +01001049 config->next = daemon->dhcp_conf;
1050 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +01001051 }
1052
1053 config->flags |= flags;
1054
1055 if (flags & CONFIG_NAME)
1056 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001057 config->hostname = host;
1058 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +01001059 }
1060
1061 if (flags & CONFIG_ADDR)
1062 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +00001063 }
Simon Kelley33820b72004-04-03 21:10:00 +01001064
Simon Kelley9009d742008-11-14 20:04:27 +00001065 config->flags |= CONFIG_NOCLID;
1066 if (!config->hwaddr)
1067 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
1068 if (config->hwaddr)
1069 {
1070 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
1071 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
1072 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
1073 config->hwaddr->wildcard_mask = 0;
1074 config->hwaddr->next = NULL;
1075 }
Simon Kelley33820b72004-04-03 21:10:00 +01001076 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +01001077
1078 free(host);
1079
Simon Kelley44a2a312004-03-10 20:04:35 +00001080 }
1081
1082 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +01001083
Simon Kelley7622fc02009-06-04 20:32:05 +01001084 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +00001085}
1086
Simon Kelley44a2a312004-03-10 20:04:35 +00001087
Simon Kelleybb01cb92004-12-13 20:56:23 +00001088/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
1089 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +01001090 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001091 so check here that the domain name is legal as a hostname.
1092 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001093char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +00001094{
Simon Kelley824af852008-02-12 20:43:05 +00001095 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +00001096
1097 if (daemon->port == 0)
1098 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +00001099
Simon Kelleycc921df2019-01-02 22:48:59 +00001100 lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001101
Simon Kelleybb01cb92004-12-13 20:56:23 +00001102 if (lookup && (lookup->flags & F_HOSTS))
1103 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001104 char *dot, *hostname = cache_get_name(lookup);
1105 dot = strchr(hostname, '.');
1106
1107 if (dot && strlen(dot+1) != 0)
1108 {
1109 char *d2 = get_domain(addr);
1110 if (!d2 || !hostname_isequal(dot+1, d2))
1111 return NULL; /* wrong domain */
1112 }
1113
1114 if (!legal_hostname(hostname))
1115 return NULL;
1116
Petr Menšík47b45b22018-08-15 18:17:00 +02001117 safe_strncpy(daemon->dhcp_buff, hostname, 256);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001118 strip_hostname(daemon->dhcp_buff);
1119
1120 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001121 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001122
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001123 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001124}
1125
Simon Kelleyff7eea22013-09-04 18:01:38 +01001126static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
1127{
1128 /* ->local is same value for all relays on ->current chain */
Simon Kelleycc921df2019-01-02 22:48:59 +00001129 union all_addr from;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001130
1131 if (mess->op != BOOTREQUEST)
1132 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001133
Simon Kelleyff7eea22013-09-04 18:01:38 +01001134 /* source address == relay address */
Simon Kelleycc921df2019-01-02 22:48:59 +00001135 from.addr4 = relay->local.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001136
1137 /* already gatewayed ? */
1138 if (mess->giaddr.s_addr)
1139 {
1140 /* if so check if by us, to stomp on loops. */
Simon Kelleycc921df2019-01-02 22:48:59 +00001141 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001142 return 1;
1143 }
1144 else
1145 {
1146 /* plug in our address */
Simon Kelleycc921df2019-01-02 22:48:59 +00001147 mess->giaddr.s_addr = relay->local.addr4.s_addr;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001148 }
1149
1150 if ((mess->hops++) > 20)
1151 return 1;
1152
1153 for (; relay; relay = relay->current)
1154 {
1155 union mysockaddr to;
1156
1157 to.sa.sa_family = AF_INET;
Simon Kelleycc921df2019-01-02 22:48:59 +00001158 to.in.sin_addr = relay->server.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001159 to.in.sin_port = htons(daemon->dhcp_server_port);
1160
1161 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1162
1163 if (option_bool(OPT_LOG_OPTS))
1164 {
1165 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleycc921df2019-01-02 22:48:59 +00001166 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr4));
Simon Kelleyff7eea22013-09-04 18:01:38 +01001167 }
1168
1169 /* Save this for replies */
1170 relay->iface_index = iface_index;
1171 }
1172
1173 return 1;
1174}
1175
1176
1177static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1178{
1179 struct dhcp_relay *relay;
1180
1181 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1182 return NULL;
1183
1184 for (relay = daemon->relay4; relay; relay = relay->next)
1185 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001186 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001187 {
1188 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1189 return relay->iface_index != 0 ? relay : NULL;
1190 }
1191 }
1192
1193 return NULL;
1194}
1195
1196#endif