blob: b65facd8707b03919d6f78afd2ea8dd25078e5e9 [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 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;
23 int ind;
24};
25
Simon Kelleyc72daea2012-01-05 21:33:27 +000026struct match_param {
27 int ind, matched;
28 struct in_addr netmask, broadcast, addr;
29};
30
Simon Kelley3f2873d2013-05-14 11:28:47 +010031static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010032 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelley3f2873d2013-05-14 11:28:47 +010033static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +000034 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070035static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
Simon Kelleyff7eea22013-09-04 18:01:38 +010036static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010037
Simon Kelley316e2732010-01-22 20:16:09 +000038static int make_fd(int port)
Simon Kelley44a2a312004-03-10 20:04:35 +000039{
40 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
41 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010042 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000043#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
44 int mtu = IP_PMTUDISC_DONT;
45#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000046#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
47 int tos = IPTOS_CLASS_CS6;
48#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010049
Simon Kelley44a2a312004-03-10 20:04:35 +000050 if (fd == -1)
Simon Kelley7622fc02009-06-04 20:32:05 +010051 die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000052
Simon Kelley824af852008-02-12 20:43:05 +000053 if (!fix_fd(fd) ||
54#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +000055 setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
56#endif
57#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
58 setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +000059#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010060#if defined(HAVE_LINUX_NETWORK)
Simon Kelleyc72daea2012-01-05 21:33:27 +000061 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley7622fc02009-06-04 20:32:05 +010062#else
Simon Kelley3be34542004-09-11 19:12:13 +010063 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000064#endif
Simon Kelley3be34542004-09-11 19:12:13 +010065 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010066 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000067
Josh Soref730c6742017-02-06 16:14:04 +000068 /* When bind-interfaces is set, there might be more than one dnsmasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010069 instance binding port 67. That's OK if they serve different networks.
Josh Soref730c6742017-02-06 16:14:04 +000070 Need to set REUSEADDR|REUSEPORT to make this possible.
Simon Kelley56a11422013-04-02 17:02:58 +010071 Handle the case that REUSEPORT is defined, but the kernel doesn't
72 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley54dd3932012-06-20 11:23:38 +010073 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley4011c4e2006-10-28 16:26:19 +010074 {
Simon Kelleyffbad342013-08-14 15:53:57 +010075 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010076
Simon Kelley4011c4e2006-10-28 16:26:19 +010077#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010078 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010079 errno == ENOPROTOOPT)
80 rc = 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +010081#endif
Simon Kelley56a11422013-04-02 17:02:58 +010082
Simon Kelleyffbad342013-08-14 15:53:57 +010083 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010084 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
85
Simon Kelley4011c4e2006-10-28 16:26:19 +010086 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010087 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010088 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000089
Simon Kelley849a8352006-06-09 21:02:31 +010090 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000091 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000092 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000093 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010094#ifdef HAVE_SOCKADDR_SA_LEN
95 saddr.sin_len = sizeof(struct sockaddr_in);
96#endif
97
Simon Kelley44a2a312004-03-10 20:04:35 +000098 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010099 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +0000100
Simon Kelley316e2732010-01-22 20:16:09 +0000101 return fd;
102}
103
104void dhcp_init(void)
105{
106#if defined(HAVE_BSD_NETWORK)
107 int oneopt = 1;
108#endif
109
110 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
111 if (daemon->enable_pxe)
112 daemon->pxefd = make_fd(PXE_PORT);
113 else
114 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100115
Simon Kelley824af852008-02-12 20:43:05 +0000116#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100117 /* When we're not using capabilities, we need to do this here before
118 we drop root. Also, set buffer size small, to avoid wasting
119 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000120
Simon Kelley28866e92011-02-14 20:19:14 +0000121 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100122 daemon->dhcp_icmp_fd = -1;
123 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
124 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100125 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100126
127 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100128 init_bpf();
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100129#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000130}
131
Simon Kelley316e2732010-01-22 20:16:09 +0000132void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000133{
Simon Kelley316e2732010-01-22 20:16:09 +0000134 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100135 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000136 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100137 struct dhcp_relay *relay;
138 int is_relay_reply = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000139 struct iname *tmp;
140 struct ifreq ifr;
141 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100142 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000143 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 struct iovec iov;
145 ssize_t sz;
Simon Kelleyc7be0162017-05-10 22:21:53 +0100146 int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0;
Neil Jerramff325642016-05-03 22:45:14 +0100147 int rcvd_iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000148 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100149 struct iface_param parm;
Floris Bos503c6092017-04-09 23:07:13 +0100150 time_t recvtime = now;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100151#ifdef HAVE_LINUX_NETWORK
152 struct arpreq arp_req;
Floris Bos503c6092017-04-09 23:07:13 +0100153 struct timeval tv;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100154#endif
155
Simon Kelley44a2a312004-03-10 20:04:35 +0000156 union {
157 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000158#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000159 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000160#elif defined(HAVE_SOLARIS_NETWORK)
161 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100162#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000163 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
164#endif
165 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000166 struct dhcp_bridge *bridge, *alias;
167
168 msg.msg_controllen = sizeof(control_u);
169 msg.msg_control = control_u.control;
170 msg.msg_name = &dest;
171 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100172 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000173 msg.msg_iovlen = 1;
174
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
176 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000177 return;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700178
179#ifdef HAVE_DUMPFILE
180 dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
181#endif
182
183#if defined (HAVE_LINUX_NETWORK)
Floris Bos503c6092017-04-09 23:07:13 +0100184 if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
185 recvtime = tv.tv_sec;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700186
Simon Kelley4011c4e2006-10-28 16:26:19 +0100187 if (msg.msg_controllen >= sizeof(struct cmsghdr))
188 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000189 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100190 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100191 union {
192 unsigned char *c;
193 struct in_pktinfo *p;
194 } p;
195 p.c = CMSG_DATA(cmptr);
196 iface_index = p.p->ipi_ifindex;
197 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100198 unicast_dest = 1;
199 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100200
201#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100202 if (msg.msg_controllen >= sizeof(struct cmsghdr))
203 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
204 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100205 {
206 union {
207 unsigned char *c;
208 struct sockaddr_dl *s;
209 } p;
210 p.c = CMSG_DATA(cmptr);
211 iface_index = p.s->sdl_index;
212 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100213
Simon Kelley7622fc02009-06-04 20:32:05 +0100214#elif defined(HAVE_SOLARIS_NETWORK)
215 if (msg.msg_controllen >= sizeof(struct cmsghdr))
216 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
217 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100218 {
219 union {
220 unsigned char *c;
221 unsigned int *i;
222 } p;
223 p.c = CMSG_DATA(cmptr);
224 iface_index = *(p.i);
225 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100226#endif
227
Simon Kelleyc7be0162017-05-10 22:21:53 +0100228 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
Simon Kelley7ab78b92017-05-11 20:33:21 +0100229 ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100230 return;
Simon Kelleyc7be0162017-05-10 22:21:53 +0100231
232 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
233 loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
234
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100235#ifdef HAVE_LINUX_NETWORK
236 /* ARP fiddling uses original interface even if we pretend to use a different one. */
Petr Menšík47b45b22018-08-15 18:17:00 +0200237 safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100238#endif
239
Neil Jerram4918bd52015-06-10 22:23:20 +0100240 /* If the interface on which the DHCP request was received is an
241 alias of some other interface (as specified by the
242 --bridge-interface option), change ifr.ifr_name so that we look
243 for DHCP contexts associated with the aliased interface instead
244 of with the aliasing one. */
Neil Jerramff325642016-05-03 22:45:14 +0100245 rcvd_iface_index = iface_index;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000246 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
247 {
248 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100249 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000250 {
251 if (!(iface_index = if_nametoindex(bridge->iface)))
252 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100253 my_syslog(MS_DHCP | LOG_WARNING,
254 _("unknown interface %s in bridge-interface"),
255 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000256 return;
257 }
258 else
259 {
Petr Menšík47b45b22018-08-15 18:17:00 +0200260 safe_strncpy(ifr.ifr_name, bridge->iface, sizeof(ifr.ifr_name));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000261 break;
262 }
263 }
264
265 if (alias)
266 break;
267 }
268
Simon Kelley4011c4e2006-10-28 16:26:19 +0100269#ifdef MSG_BCAST
270 /* OpenBSD tells us when a packet was broadcast */
271 if (!(msg.msg_flags & MSG_BCAST))
272 unicast_dest = 1;
273#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000274
Simon Kelleyff7eea22013-09-04 18:01:38 +0100275 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
276 {
277 /* Reply from server, using us as relay. */
Simon Kelley1649f702017-06-25 21:19:30 +0100278 rcvd_iface_index = relay->iface_index;
279 if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100280 return;
281 is_relay_reply = 1;
282 iov.iov_len = sz;
283#ifdef HAVE_LINUX_NETWORK
Petr Menšík47b45b22018-08-15 18:17:00 +0200284 safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
Simon Kelleyff7eea22013-09-04 18:01:38 +0100285#endif
286 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000287 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000288 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100289 ifr.ifr_addr.sa_family = AF_INET;
290 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
291 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
292 else
293 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100294 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
295 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 +0100296 return;
297 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100298
Simon Kelleyff7eea22013-09-04 18:01:38 +0100299 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700300 if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100301 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000302
Simon Kelleyff7eea22013-09-04 18:01:38 +0100303 /* unlinked contexts/relays are marked by context->current == context */
304 for (context = daemon->dhcp; context; context = context->next)
305 context->current = context;
306
Simon Kelleyff7eea22013-09-04 18:01:38 +0100307 parm.current = NULL;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100308 parm.ind = iface_index;
309
Simon Kelleycc921df2019-01-02 22:48:59 +0000310 if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100311 {
312 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
313 for a secondary */
314 struct match_param match;
315
316 match.matched = 0;
317 match.ind = iface_index;
318
319 if (!daemon->if_addrs ||
320 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
321 !match.matched)
322 return;
323
324 iface_addr = match.addr;
325 /* make sure secondary address gets priority in case
326 there is more than one address on the interface in the same subnet */
327 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
328 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700329
330 if (relay_upstream4(iface_index, mess, (size_t)sz))
331 return;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100332
333 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000334 return;
335
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700336 /* Check for a relay again after iface_enumerate/complete_context has had
337 chance to fill in relay->iface_index fields. This handles first time through
338 and any changes in interface config. */
339 if (relay_upstream4(iface_index, mess, (size_t)sz))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100340 return;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700341
Simon Kelleyff7eea22013-09-04 18:01:38 +0100342 /* May have configured relay, but not DHCP server */
343 if (!daemon->dhcp)
344 return;
345
346 lease_prune(NULL, now); /* lose any expired leases */
347 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100348 now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100349 lease_update_file(now);
350 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000351
Simon Kelleyff7eea22013-09-04 18:01:38 +0100352 if (iov.iov_len == 0)
353 return;
354 }
Floris Bos503c6092017-04-09 23:07:13 +0100355
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100356 msg.msg_name = &dest;
357 msg.msg_namelen = sizeof(dest);
358 msg.msg_control = NULL;
359 msg.msg_controllen = 0;
360 msg.msg_iov = &iov;
361 iov.iov_base = daemon->dhcp_packet.iov_base;
362
363 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000364 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100365
Simon Kelley3be34542004-09-11 19:12:13 +0100366#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100367 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100368#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000369
Simon Kelley316e2732010-01-22 20:16:09 +0000370 if (pxe_fd)
371 {
372 if (mess->ciaddr.s_addr != 0)
373 dest.sin_addr = mess->ciaddr;
374 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100375 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100376 {
377 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100378 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100379 dest.sin_addr = mess->giaddr;
380 }
381 else if (mess->ciaddr.s_addr)
382 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100383 /* If the client's idea of its own address tallys with
384 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100385 source port too, and send back to that. If we're replying
386 to a DHCPINFORM, trust the source address always. */
387 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100388 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100389 {
Simon Kelley9e038942008-05-30 20:06:34 +0100390 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100391 dest.sin_addr = mess->ciaddr;
392 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100393 }
Simon Kelley824af852008-02-12 20:43:05 +0000394#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000395 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100396 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000397 /* fill cmsg for outbound interface (both broadcast & unicast) */
Tarun Kundud0a550e2024-09-20 14:17:52 -0700398 struct in_pktinfo *pkt;
399 msg.msg_control = control_u.control;
400 msg.msg_controllen = sizeof(control_u);
401 cmptr = CMSG_FIRSTHDR(&msg);
402 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
403 pkt->ipi_ifindex = rcvd_iface_index;
404 pkt->ipi_spec_dst.s_addr = 0;
405 msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
406 cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
407 cmptr->cmsg_level = IPPROTO_IP;
408 cmptr->cmsg_type = IP_PKTINFO;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000409
410 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
411 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
412 {
413 /* broadcast to 255.255.255.255 (or mac address invalid) */
414 dest.sin_addr.s_addr = INADDR_BROADCAST;
415 dest.sin_port = htons(daemon->dhcp_client_port);
416 }
417 else
418 {
419 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
420 struct sockaddr limits size to 14 bytes. */
421 dest.sin_addr = mess->yiaddr;
422 dest.sin_port = htons(daemon->dhcp_client_port);
423 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
424 arp_req.arp_ha.sa_family = mess->htype;
425 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
426 /* interface name already copied in */
427 arp_req.arp_flags = ATF_COM;
428 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
429 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
430 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000431 }
Simon Kelley824af852008-02-12 20:43:05 +0000432#elif defined(HAVE_SOLARIS_NETWORK)
433 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
434 {
435 /* broadcast to 255.255.255.255 (or mac address invalid) */
436 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100437 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000438 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100439 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000440 }
441 else
442 {
443 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
444 Note that this only works for ethernet on solaris, because we use SIOCSARP
445 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
446 mysteriously. Bah. Fall back to broadcast for other net types. */
447 struct arpreq req;
448 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100449 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000450 *((struct sockaddr_in *)&req.arp_pa) = dest;
451 req.arp_ha.sa_family = AF_UNSPEC;
452 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
453 req.arp_flags = ATF_COM;
454 ioctl(daemon->dhcpfd, SIOCSARP, &req);
455 }
456#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100457 else
458 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700459#ifdef HAVE_DUMPFILE
460 if (ntohs(mess->flags) & 0x8000)
461 dest.sin_addr.s_addr = INADDR_BROADCAST;
462 else
463 dest.sin_addr = mess->yiaddr;
464 dest.sin_port = htons(daemon->dhcp_client_port);
465
466 dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
467 (union mysockaddr *)&dest, fd);
468#endif
469
Simon Kelley5aabfc72007-08-29 11:24:47 +0100470 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100471 return;
472 }
473#endif
474
Simon Kelley824af852008-02-12 20:43:05 +0000475#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000476 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000477#endif
Kyle Swenson545712c2021-11-17 12:25:04 -0700478
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700479#ifdef HAVE_DUMPFILE
480 dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
481 (union mysockaddr *)&dest, fd);
Kyle Swenson545712c2021-11-17 12:25:04 -0700482#endif
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700483
Simon Kelleyff841eb2015-03-11 21:36:30 +0000484 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100485
486 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
487 if (errno != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700488 {
489 inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
490 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
491 daemon->addrbuff, strerror(errno));
492 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000493}
Simon Kelley98079ea2015-10-13 20:30:32 +0100494
Simon Kelleyc72daea2012-01-05 21:33:27 +0000495/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100496static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000497 struct in_addr netmask, struct in_addr broadcast, void *vparam)
498{
499 struct match_param *param = vparam;
500 struct iname *tmp;
501
Simon Kelley3f2873d2013-05-14 11:28:47 +0100502 (void) label;
503
Simon Kelleyc72daea2012-01-05 21:33:27 +0000504 if (if_index == param->ind)
505 {
506 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
507 if ( tmp->addr.sa.sa_family == AF_INET &&
508 tmp->addr.in.sin_addr.s_addr == local.s_addr)
509 {
510 param->matched = 1;
511 param->addr = local;
512 param->netmask = netmask;
513 param->broadcast = broadcast;
514 break;
515 }
516 }
517
518 return 1;
519}
520
Simon Kelley0a852542005-03-23 20:28:59 +0000521/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100522 of each interface (and any relay address) and does the following things:
523
524 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
525 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
526 3) Fills in local (this host) and router (this host or relay) addresses.
527 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
528
klemens43517fc2017-02-19 15:53:37 +0000529 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100530
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000531static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
Simon Kelley0a852542005-03-23 20:28:59 +0000532{
533 struct dhcp_context *context;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100534
Simon Kelley0a852542005-03-23 20:28:59 +0000535 for (context = daemon->dhcp; context; context = context->next)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000536 if (!(context->flags & CONTEXT_NETMASK) &&
537 (is_same_net(addr, context->start, netmask) ||
538 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000539 {
540 if (context->netmask.s_addr != netmask.s_addr &&
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000541 !(is_same_net(addr, context->start, netmask) &&
542 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000543 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700544 inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
545 inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
546 inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley7622fc02009-06-04 20:32:05 +0100547 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700548 daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
Simon Kelley0a852542005-03-23 20:28:59 +0000549 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000550 context->netmask = netmask;
Simon Kelley0a852542005-03-23 20:28:59 +0000551 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000552}
553
554static int complete_context(struct in_addr local, int if_index, char *label,
555 struct in_addr netmask, struct in_addr broadcast, void *vparam)
556{
557 struct dhcp_context *context;
558 struct dhcp_relay *relay;
559 struct iface_param *param = vparam;
560 struct shared_network *share;
Tarun Kundud0a550e2024-09-20 14:17:52 -0700561
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000562 (void)label;
563
564 for (share = daemon->shared_networks; share; share = share->next)
565 {
Simon Kelley0a852542005-03-23 20:28:59 +0000566
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000567#ifdef HAVE_DHCP6
568 if (share->shared_addr.s_addr == 0)
569 continue;
570#endif
571
572 if (share->if_index != 0)
573 {
574 if (share->if_index != if_index)
575 continue;
576 }
577 else
578 {
579 if (share->match_addr.s_addr != local.s_addr)
580 continue;
581 }
582
583 for (context = daemon->dhcp; context; context = context->next)
584 {
585 if (context->netmask.s_addr != 0 &&
586 is_same_net(share->shared_addr, context->start, context->netmask) &&
587 is_same_net(share->shared_addr, context->end, context->netmask))
588 {
589 /* link it onto the current chain if we've not seen it before */
590 if (context->current == context)
591 {
592 /* For a shared network, we have no way to guess what the default route should be. */
593 context->router.s_addr = 0;
594 context->local = local; /* Use configured address for Server Identifier */
595 context->current = param->current;
596 param->current = context;
597 }
598
599 if (!(context->flags & CONTEXT_BRDCAST))
600 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
601 }
602 }
603 }
604
605 guess_range_netmask(local, netmask);
606
607 for (context = daemon->dhcp; context; context = context->next)
608 {
Tarun Kundud0a550e2024-09-20 14:17:52 -0700609 if (context->netmask.s_addr != 0 &&
610 is_same_net(local, context->start, context->netmask) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100611 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000612 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100613 /* link it onto the current chain if we've not seen it before */
614 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000615 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100616 context->router = local;
617 context->local = local;
618 context->current = param->current;
619 param->current = context;
620 }
621
622 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000623 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100624 if (is_same_net(broadcast, context->start, context->netmask))
625 context->broadcast = broadcast;
626 else
Simon Kelley0a852542005-03-23 20:28:59 +0000627 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
628 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100629 }
Simon Kelley0a852542005-03-23 20:28:59 +0000630 }
631
Simon Kelleyff7eea22013-09-04 18:01:38 +0100632 for (relay = daemon->relay4; relay; relay = relay->next)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700633 if (relay->local.addr4.s_addr == local.s_addr)
634 relay->iface_index = if_index;
635
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000637}
638
Simon Kelley824af852008-02-12 20:43:05 +0000639struct dhcp_context *address_available(struct dhcp_context *context,
640 struct in_addr taddr,
641 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000642{
Simon Kelley36717ee2004-09-20 19:20:58 +0100643 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100644 possible ranges. Make sure that the address isn't in use
645 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000646
Simon Kelley36717ee2004-09-20 19:20:58 +0100647 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100648 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100649
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100650 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100651 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100652 return NULL;
653
654 for (tmp = context; tmp; tmp = tmp->current)
655 {
656 start = ntohl(tmp->start.s_addr);
657 end = ntohl(tmp->end.s_addr);
658
Simon Kelley7de060b2011-08-26 17:24:52 +0100659 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100660 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000661 addr <= end &&
662 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100663 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100664 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000665
Simon Kelley59353a62004-11-21 19:34:28 +0000666 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000667}
Simon Kelley59353a62004-11-21 19:34:28 +0000668
Simon Kelley824af852008-02-12 20:43:05 +0000669struct dhcp_context *narrow_context(struct dhcp_context *context,
670 struct in_addr taddr,
671 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000672{
Simon Kelleye17fb622006-01-14 20:33:46 +0000673 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000674 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000675 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000676 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000677 any dhcp-range. In that case we return a static range if possible, or failing that,
678 any context on the correct subnet. (If there's more than one, this is a dodgy
679 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000680
Simon Kelleye17fb622006-01-14 20:33:46 +0000681 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000682
Simon Kelley824af852008-02-12 20:43:05 +0000683 if (!(tmp = address_available(context, taddr, netids)))
684 {
685 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100686 if (match_netid(tmp->filter, netids, 1) &&
687 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100688 (tmp->flags & CONTEXT_STATIC))
689 break;
Simon Kelley824af852008-02-12 20:43:05 +0000690
691 if (!tmp)
692 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100693 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100694 is_same_net(taddr, tmp->start, tmp->netmask) &&
695 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000696 break;
697 }
Simon Kelley59353a62004-11-21 19:34:28 +0000698
Simon Kelley824af852008-02-12 20:43:05 +0000699 /* Only one context allowed now */
700 if (tmp)
701 tmp->current = NULL;
702
703 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000704}
705
Simon Kelleydfa666f2004-08-02 18:27:27 +0100706struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
707{
708 struct dhcp_config *config;
709
710 for (config = configs; config; config = config->next)
711 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
712 return config;
713
714 return NULL;
715}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716
Simon Kelley5ce3e762017-04-28 22:14:20 +0100717/* Check if and address is in use by sending ICMP ping.
718 This wrapper handles a cache and load-limiting.
719 Return is NULL is address in use, or a pointer to a cache entry
720 recording that it isn't. */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100721struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100722{
723 static struct ping_result dummy;
724 struct ping_result *r, *victim = NULL;
725 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
726 ((float)PING_WAIT)));
727
728 /* check if we failed to ping addr sometime in the last
729 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
730 This avoids problems when a stupid client bangs
731 on us repeatedly. As a final check, if we did more
732 than 60% of the possible ping checks in the last
733 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
734 for (count = 0, r = daemon->ping_results; r; r = r->next)
735 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
736 victim = r; /* old record */
737 else
738 {
739 count++;
740 if (r->addr.s_addr == addr.s_addr)
741 return r;
742 }
743
744 /* didn't find cached entry */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100745 if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100746 {
Simon Kelleyc7be0162017-05-10 22:21:53 +0100747 /* overloaded, or configured not to check, loopback interface, return "not in use" */
Simon Kelley0669ee72018-05-04 16:46:24 +0100748 dummy.hash = hash;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100749 return &dummy;
750 }
751 else if (icmp_ping(addr))
752 return NULL; /* address in use. */
753 else
754 {
755 /* at this point victim may hold an expired record */
756 if (!victim)
757 {
758 if ((victim = whine_malloc(sizeof(struct ping_result))))
759 {
760 victim->next = daemon->ping_results;
761 daemon->ping_results = victim;
762 }
763 }
764
765 /* record that this address is OK for 30s
766 without more ping checks */
767 if (victim)
768 {
769 victim->addr = addr;
770 victim->time = now;
771 victim->hash = hash;
772 }
773 return victim;
774 }
775}
776
Simon Kelley5aabfc72007-08-29 11:24:47 +0100777int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000778 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100779 struct dhcp_netid *netids, time_t now, int loopback)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000780{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100781 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000782 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000783 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000784
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100785 struct in_addr start, addr;
786 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000787 int i, pass;
788 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100789
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100790 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
791 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100792 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100793 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100794
795 /* j == 0 is marker */
796 if (j == 0)
797 j = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100798
Simon Kelleycdeda282006-03-16 20:16:06 +0000799 for (pass = 0; pass <= 1; pass++)
800 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100801 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000802 continue;
803 else if (!match_netid(c->filter, netids, pass))
804 continue;
805 else
806 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100807 if (option_bool(OPT_CONSEC_ADDR))
808 /* seed is largest extant lease addr in this context */
809 start = lease_find_max_addr(c);
810 else
811 /* pick a seed based on hwaddr */
812 start.s_addr = htonl(ntohl(c->start.s_addr) +
813 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
814
815 /* iterate until we find a free address. */
816 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000817
818 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100819 /* eliminate addresses in use by the server. */
820 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100821 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100822 break;
823
Simon Kelley73a08a22009-02-05 20:28:08 +0000824 /* Addresses which end in .255 and .0 are broken in Windows even when using
825 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
826 then 192.168.0.255 is a valid IP address, but not for Windows as it's
827 in the class C range. See KB281579. We therefore don't allocate these
828 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100829 if (!d &&
830 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000831 !config_find_by_address(daemon->dhcp_conf, addr) &&
832 (!IN_CLASSC(ntohl(addr.s_addr)) ||
833 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000834 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000835 /* in consec-ip mode, skip addresses equal to
836 the number of addresses rejected by clients. This
837 should avoid the same client being offered the same
838 address after it has rjected it. */
839 if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
840 c->addr_epoch--;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100841 else
842 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000843 struct ping_result *r;
844
845 if ((r = do_icmp_ping(now, addr, j, loopback)))
846 {
847 /* consec-ip mode: we offered this address for another client
848 (different hash) recently, don't offer it to this one. */
849 if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
850 {
851 *addrp = addr;
852 return 1;
853 }
854 }
855 else
856 {
857 /* address in use: perturb address selection so that we are
858 less likely to try this address again. */
859 if (!option_bool(OPT_CONSEC_ADDR))
860 c->addr_epoch++;
861 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100862 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000863 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100864
Simon Kelleycdeda282006-03-16 20:16:06 +0000865 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
866
867 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
868 addr = c->start;
869
870 } while (addr.s_addr != start.s_addr);
871 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100872
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000873 return 0;
874}
875
Simon Kelley5aabfc72007-08-29 11:24:47 +0100876void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000877{
Simon Kelley44a2a312004-03-10 20:04:35 +0000878 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000879 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100880 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100881 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000882 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100883 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100884 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100885 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000886 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100887
888 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000889
890 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100891 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100892 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100893 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100894 }
895
Simon Kelley849a8352006-06-09 21:02:31 +0100896 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100897 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100898 {
899 tmp = config->next;
900 if (config->flags & CONFIG_FROM_ETHERS)
901 {
902 *up = tmp;
903 /* cannot have a clid */
904 if (config->flags & CONFIG_NAME)
905 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000906 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100907 free(config);
908 }
909 else
910 up = &config->next;
911 }
912
Simon Kelley44a2a312004-03-10 20:04:35 +0000913 while (fgets(buff, MAXDNAME, f))
914 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100915 char *host = NULL;
916
Simon Kelleyb8187c82005-11-26 21:46:27 +0000917 lineno++;
918
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700919 while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000920 buff[strlen(buff)-1] = 0;
921
Simon Kelley73a08a22009-02-05 20:28:08 +0000922 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000923 continue;
924
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700925 for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
926 for(; *ip && isspace((unsigned char)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000927 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000928 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000929 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100930 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000931 continue;
932 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000933
934 /* check for name or dotted-quad */
935 for (cp = ip; *cp; cp++)
936 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
937 break;
938
939 if (!*cp)
940 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700941 if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000942 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100943 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000944 continue;
945 }
946
Simon Kelley33820b72004-04-03 21:10:00 +0100947 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000948
Simon Kelley16972692006-10-16 20:04:18 +0100949 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100950 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000951 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000952 }
953 else
954 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100955 int nomem;
956 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000957 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100958 if (!nomem)
959 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
960 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000961 continue;
962 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100963
Simon Kelley33820b72004-04-03 21:10:00 +0100964 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000965
Simon Kelley16972692006-10-16 20:04:18 +0100966 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100967 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000968 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000969 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100970
971 if (config && (config->flags & CONFIG_FROM_ETHERS))
972 {
973 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
974 continue;
975 }
976
Simon Kelley1cff1662004-03-12 08:12:58 +0000977 if (!config)
978 {
Simon Kelley16972692006-10-16 20:04:18 +0100979 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000980 {
981 struct hwaddr_config *conf_addr = config->hwaddr;
982 if (conf_addr &&
983 conf_addr->next == NULL &&
984 conf_addr->wildcard_mask == 0 &&
985 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
986 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
987 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
988 break;
989 }
Simon Kelley33820b72004-04-03 21:10:00 +0100990
991 if (!config)
992 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100993 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100994 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100995 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000996 config->hwaddr = NULL;
997 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100998 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100999 config->next = daemon->dhcp_conf;
1000 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +01001001 }
1002
1003 config->flags |= flags;
1004
1005 if (flags & CONFIG_NAME)
1006 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001007 config->hostname = host;
1008 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +01001009 }
1010
1011 if (flags & CONFIG_ADDR)
1012 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +00001013 }
Simon Kelley33820b72004-04-03 21:10:00 +01001014
Simon Kelley9009d742008-11-14 20:04:27 +00001015 config->flags |= CONFIG_NOCLID;
1016 if (!config->hwaddr)
1017 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
1018 if (config->hwaddr)
1019 {
1020 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
1021 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
1022 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
1023 config->hwaddr->wildcard_mask = 0;
1024 config->hwaddr->next = NULL;
1025 }
Simon Kelley33820b72004-04-03 21:10:00 +01001026 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +01001027
1028 free(host);
1029
Simon Kelley44a2a312004-03-10 20:04:35 +00001030 }
1031
1032 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +01001033
Simon Kelley7622fc02009-06-04 20:32:05 +01001034 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +00001035}
1036
Simon Kelley44a2a312004-03-10 20:04:35 +00001037
Simon Kelleybb01cb92004-12-13 20:56:23 +00001038/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
1039 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +01001040 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001041 so check here that the domain name is legal as a hostname.
1042 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001043char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +00001044{
Simon Kelley824af852008-02-12 20:43:05 +00001045 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +00001046
1047 if (daemon->port == 0)
1048 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +00001049
Simon Kelleycc921df2019-01-02 22:48:59 +00001050 lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001051
Simon Kelleybb01cb92004-12-13 20:56:23 +00001052 if (lookup && (lookup->flags & F_HOSTS))
1053 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001054 char *dot, *hostname = cache_get_name(lookup);
1055 dot = strchr(hostname, '.');
1056
1057 if (dot && strlen(dot+1) != 0)
1058 {
1059 char *d2 = get_domain(addr);
1060 if (!d2 || !hostname_isequal(dot+1, d2))
1061 return NULL; /* wrong domain */
1062 }
1063
1064 if (!legal_hostname(hostname))
1065 return NULL;
1066
Petr Menšík47b45b22018-08-15 18:17:00 +02001067 safe_strncpy(daemon->dhcp_buff, hostname, 256);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001068 strip_hostname(daemon->dhcp_buff);
1069
1070 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001071 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001072
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001073 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001074}
1075
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001076static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001077{
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001078 struct in_addr giaddr = mess->giaddr;
1079 u8 hops = mess->hops;
1080 struct dhcp_relay *relay;
1081
Simon Kelleyff7eea22013-09-04 18:01:38 +01001082 if (mess->op != BOOTREQUEST)
1083 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001084
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001085 for (relay = daemon->relay4; relay; relay = relay->next)
1086 if (relay->iface_index != 0 && relay->iface_index == iface_index)
1087 break;
1088
1089 /* No relay config. */
1090 if (!relay)
1091 return 0;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001092
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001093 for (; relay; relay = relay->next)
1094 if (relay->iface_index != 0 && relay->iface_index == iface_index)
1095 {
1096 union mysockaddr to;
1097 union all_addr from;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001098
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001099 mess->hops = hops;
1100 mess->giaddr = giaddr;
1101
1102 if ((mess->hops++) > 20)
1103 continue;
1104
1105 /* source address == relay address */
1106 from.addr4 = relay->local.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001107
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001108 /* already gatewayed ? */
1109 if (giaddr.s_addr)
1110 {
1111 /* if so check if by us, to stomp on loops. */
1112 if (giaddr.s_addr == relay->local.addr4.s_addr)
1113 continue;
1114 }
1115 else
1116 {
1117 /* plug in our address */
1118 mess->giaddr.s_addr = relay->local.addr4.s_addr;
1119 }
1120
1121 to.sa.sa_family = AF_INET;
1122 to.in.sin_addr = relay->server.addr4;
1123 to.in.sin_port = htons(relay->port);
1124
1125 /* Broadcasting to server. */
1126 if (relay->server.addr4.s_addr == 0)
1127 {
1128 struct ifreq ifr;
1129
1130 if (relay->interface)
1131 safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
1132
1133 if (!relay->interface || strchr(relay->interface, '*') ||
1134 ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
1135 {
1136 my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
1137 continue;
1138 }
1139
1140 to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
1141 }
1142
1143#ifdef HAVE_DUMPFILE
Simon Kelleyff7eea22013-09-04 18:01:38 +01001144 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001145 union mysockaddr fromsock;
1146 fromsock.in.sin_port = htons(daemon->dhcp_server_port);
1147 fromsock.in.sin_addr = from.addr4;
1148 fromsock.sa.sa_family = AF_INET;
1149
1150 dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
Simon Kelleyff7eea22013-09-04 18:01:38 +01001151 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001152#endif
1153
1154 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1155
1156 if (option_bool(OPT_LOG_OPTS))
1157 {
1158 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1159 if (relay->server.addr4.s_addr == 0)
1160 snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
1161 else
1162 inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
1163 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
1164 }
1165 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01001166
1167 return 1;
1168}
1169
1170
1171static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1172{
1173 struct dhcp_relay *relay;
1174
1175 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1176 return NULL;
1177
1178 for (relay = daemon->relay4; relay; relay = relay->next)
1179 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001180 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001181 {
1182 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1183 return relay->iface_index != 0 ? relay : NULL;
1184 }
1185 }
1186
1187 return NULL;
1188}
1189
1190#endif