blob: 3bbc7e8882a3ae83756479064b5889aaa38d6f54 [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
Tarun Kundu567116b2024-08-15 16:22:58 -0700286/* CRADLEPOINT */
287 parm.current = NULL;
288/* CRADLEPOINT */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100289 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000290 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000291 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100292 ifr.ifr_addr.sa_family = AF_INET;
293 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
294 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
295 else
296 {
Simon Kelleye94ad0f2016-08-28 18:09:17 +0100297 if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
298 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 +0100299 return;
300 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100301
Simon Kelleyff7eea22013-09-04 18:01:38 +0100302 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700303 if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100304 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000305
Simon Kelleyff7eea22013-09-04 18:01:38 +0100306 /* unlinked contexts/relays are marked by context->current == context */
307 for (context = daemon->dhcp; context; context = context->next)
308 context->current = context;
309
Simon Kelleyff7eea22013-09-04 18:01:38 +0100310 parm.current = NULL;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100311 parm.ind = iface_index;
312
Simon Kelleycc921df2019-01-02 22:48:59 +0000313 if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100314 {
315 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
316 for a secondary */
317 struct match_param match;
318
319 match.matched = 0;
320 match.ind = iface_index;
321
322 if (!daemon->if_addrs ||
323 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
324 !match.matched)
325 return;
326
327 iface_addr = match.addr;
328 /* make sure secondary address gets priority in case
329 there is more than one address on the interface in the same subnet */
330 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
331 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700332
333 if (relay_upstream4(iface_index, mess, (size_t)sz))
334 return;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100335
336 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000337 return;
338
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700339 /* Check for a relay again after iface_enumerate/complete_context has had
340 chance to fill in relay->iface_index fields. This handles first time through
341 and any changes in interface config. */
342 if (relay_upstream4(iface_index, mess, (size_t)sz))
Simon Kelleyff7eea22013-09-04 18:01:38 +0100343 return;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700344
Simon Kelleyff7eea22013-09-04 18:01:38 +0100345 /* May have configured relay, but not DHCP server */
346 if (!daemon->dhcp)
347 return;
348
349 lease_prune(NULL, now); /* lose any expired leases */
350 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100351 now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
Simon Kelleyff7eea22013-09-04 18:01:38 +0100352 lease_update_file(now);
353 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000354
Simon Kelleyff7eea22013-09-04 18:01:38 +0100355 if (iov.iov_len == 0)
356 return;
357 }
Floris Bos503c6092017-04-09 23:07:13 +0100358
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100359 msg.msg_name = &dest;
360 msg.msg_namelen = sizeof(dest);
361 msg.msg_control = NULL;
362 msg.msg_controllen = 0;
363 msg.msg_iov = &iov;
364 iov.iov_base = daemon->dhcp_packet.iov_base;
365
366 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000367 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100368
Simon Kelley3be34542004-09-11 19:12:13 +0100369#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100370 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100371#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000372
Simon Kelley316e2732010-01-22 20:16:09 +0000373 if (pxe_fd)
374 {
375 if (mess->ciaddr.s_addr != 0)
376 dest.sin_addr = mess->ciaddr;
377 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100378 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100379 {
380 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100381 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100382 dest.sin_addr = mess->giaddr;
383 }
384 else if (mess->ciaddr.s_addr)
385 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100386 /* If the client's idea of its own address tallys with
387 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100388 source port too, and send back to that. If we're replying
389 to a DHCPINFORM, trust the source address always. */
390 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100391 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100392 {
Simon Kelley9e038942008-05-30 20:06:34 +0100393 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100394 dest.sin_addr = mess->ciaddr;
395 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100396 }
Simon Kelley824af852008-02-12 20:43:05 +0000397#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000398 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000400 /* fill cmsg for outbound interface (both broadcast & unicast) */
Tarun Kundu567116b2024-08-15 16:22:58 -0700401/* CRADLEPOINT */
402 if (!parm.current || parm.current->forcedaddress.s_addr == 0)
403 {
404 struct in_pktinfo *pkt;
405 msg.msg_control = control_u.control;
406 msg.msg_controllen = sizeof(control_u);
407 cmptr = CMSG_FIRSTHDR(&msg);
408 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
409 pkt->ipi_ifindex = rcvd_iface_index;
410 pkt->ipi_spec_dst.s_addr = 0;
411 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
412 cmptr->cmsg_level = IPPROTO_IP;
413 cmptr->cmsg_type = IP_PKTINFO;
414 }
415/* CRADLEPOINT */
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000416
417 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
418 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
419 {
420 /* broadcast to 255.255.255.255 (or mac address invalid) */
421 dest.sin_addr.s_addr = INADDR_BROADCAST;
422 dest.sin_port = htons(daemon->dhcp_client_port);
423 }
424 else
425 {
426 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
427 struct sockaddr limits size to 14 bytes. */
428 dest.sin_addr = mess->yiaddr;
429 dest.sin_port = htons(daemon->dhcp_client_port);
430 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
431 arp_req.arp_ha.sa_family = mess->htype;
432 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
433 /* interface name already copied in */
434 arp_req.arp_flags = ATF_COM;
435 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
436 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
437 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000438 }
Simon Kelley824af852008-02-12 20:43:05 +0000439#elif defined(HAVE_SOLARIS_NETWORK)
440 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
441 {
442 /* broadcast to 255.255.255.255 (or mac address invalid) */
443 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100444 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000445 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100446 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000447 }
448 else
449 {
450 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
451 Note that this only works for ethernet on solaris, because we use SIOCSARP
452 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
453 mysteriously. Bah. Fall back to broadcast for other net types. */
454 struct arpreq req;
455 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100456 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000457 *((struct sockaddr_in *)&req.arp_pa) = dest;
458 req.arp_ha.sa_family = AF_UNSPEC;
459 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
460 req.arp_flags = ATF_COM;
461 ioctl(daemon->dhcpfd, SIOCSARP, &req);
462 }
463#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100464 else
465 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700466#ifdef HAVE_DUMPFILE
467 if (ntohs(mess->flags) & 0x8000)
468 dest.sin_addr.s_addr = INADDR_BROADCAST;
469 else
470 dest.sin_addr = mess->yiaddr;
471 dest.sin_port = htons(daemon->dhcp_client_port);
472
473 dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
474 (union mysockaddr *)&dest, fd);
475#endif
476
Simon Kelley5aabfc72007-08-29 11:24:47 +0100477 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100478 return;
479 }
480#endif
481
Simon Kelley824af852008-02-12 20:43:05 +0000482#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000483 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000484#endif
Kyle Swenson545712c2021-11-17 12:25:04 -0700485
Tarun Kundu567116b2024-08-15 16:22:58 -0700486/* CRADLEPOINT */
487#ifdef HAVE_LINUX_NETWORK
488 /* if we're forcing the address, use IP_PKTINFO and IP_TRANSPARENT to lie about
489 * our source address and force it out the same iface we got it on. */
490 if (parm.current && parm.current->forcedaddress.s_addr != 0)
491 {
492 struct in_pktinfo *pkt;
493 int transparent = 1;
494
495 msg.msg_control = control_u.control;
496 msg.msg_controllen = sizeof(control_u);
497 cmptr = CMSG_FIRSTHDR(&msg);
498 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
499 pkt->ipi_ifindex = rcvd_iface_index;
500 pkt->ipi_spec_dst = parm.current->forcedaddress;
501 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
502 cmptr->cmsg_level = IPPROTO_IP;
503 cmptr->cmsg_type = IP_PKTINFO;
504 #define IP_TRANSPARENT 19 /* XXX: This isn't in uclibc headers, hardcode for now */
505 setsockopt(fd, SOL_IP, IP_TRANSPARENT, &transparent, sizeof(transparent));
506 }
507#endif
508/* CRADLEPOINT */
509
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700510#ifdef HAVE_DUMPFILE
511 dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
512 (union mysockaddr *)&dest, fd);
Kyle Swenson545712c2021-11-17 12:25:04 -0700513#endif
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700514
Simon Kelleyff841eb2015-03-11 21:36:30 +0000515 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100516
517 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
518 if (errno != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700519 {
520 inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
521 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
522 daemon->addrbuff, strerror(errno));
523 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000524}
Simon Kelley98079ea2015-10-13 20:30:32 +0100525
Simon Kelleyc72daea2012-01-05 21:33:27 +0000526/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100527static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000528 struct in_addr netmask, struct in_addr broadcast, void *vparam)
529{
530 struct match_param *param = vparam;
531 struct iname *tmp;
532
Simon Kelley3f2873d2013-05-14 11:28:47 +0100533 (void) label;
534
Simon Kelleyc72daea2012-01-05 21:33:27 +0000535 if (if_index == param->ind)
536 {
537 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
538 if ( tmp->addr.sa.sa_family == AF_INET &&
539 tmp->addr.in.sin_addr.s_addr == local.s_addr)
540 {
541 param->matched = 1;
542 param->addr = local;
543 param->netmask = netmask;
544 param->broadcast = broadcast;
545 break;
546 }
547 }
548
549 return 1;
550}
551
Simon Kelley0a852542005-03-23 20:28:59 +0000552/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100553 of each interface (and any relay address) and does the following things:
554
555 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
556 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
557 3) Fills in local (this host) and router (this host or relay) addresses.
558 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
559
klemens43517fc2017-02-19 15:53:37 +0000560 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100561
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000562static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
Simon Kelley0a852542005-03-23 20:28:59 +0000563{
564 struct dhcp_context *context;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100565
Simon Kelley0a852542005-03-23 20:28:59 +0000566 for (context = daemon->dhcp; context; context = context->next)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000567 if (!(context->flags & CONTEXT_NETMASK) &&
568 (is_same_net(addr, context->start, netmask) ||
569 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000570 {
571 if (context->netmask.s_addr != netmask.s_addr &&
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000572 !(is_same_net(addr, context->start, netmask) &&
573 is_same_net(addr, context->end, netmask)))
Simon Kelley0a852542005-03-23 20:28:59 +0000574 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700575 inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
576 inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
577 inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley7622fc02009-06-04 20:32:05 +0100578 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700579 daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
Simon Kelley0a852542005-03-23 20:28:59 +0000580 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000581 context->netmask = netmask;
Simon Kelley0a852542005-03-23 20:28:59 +0000582 }
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000583}
584
585static int complete_context(struct in_addr local, int if_index, char *label,
586 struct in_addr netmask, struct in_addr broadcast, void *vparam)
587{
588 struct dhcp_context *context;
589 struct dhcp_relay *relay;
590 struct iface_param *param = vparam;
591 struct shared_network *share;
Tarun Kundu567116b2024-08-15 16:22:58 -0700592/* CRADLEPOINT */
593 char namebuff[IF_NAMESIZE];
594/* CRADLEPOINT */
595
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000596 (void)label;
597
598 for (share = daemon->shared_networks; share; share = share->next)
599 {
Simon Kelley0a852542005-03-23 20:28:59 +0000600
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000601#ifdef HAVE_DHCP6
602 if (share->shared_addr.s_addr == 0)
603 continue;
604#endif
605
606 if (share->if_index != 0)
607 {
608 if (share->if_index != if_index)
609 continue;
610 }
611 else
612 {
613 if (share->match_addr.s_addr != local.s_addr)
614 continue;
615 }
616
617 for (context = daemon->dhcp; context; context = context->next)
618 {
619 if (context->netmask.s_addr != 0 &&
620 is_same_net(share->shared_addr, context->start, context->netmask) &&
621 is_same_net(share->shared_addr, context->end, context->netmask))
622 {
623 /* link it onto the current chain if we've not seen it before */
624 if (context->current == context)
625 {
626 /* For a shared network, we have no way to guess what the default route should be. */
627 context->router.s_addr = 0;
628 context->local = local; /* Use configured address for Server Identifier */
629 context->current = param->current;
630 param->current = context;
631 }
632
633 if (!(context->flags & CONTEXT_BRDCAST))
634 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
635 }
636 }
637 }
638
639 guess_range_netmask(local, netmask);
640
641 for (context = daemon->dhcp; context; context = context->next)
642 {
Tarun Kundu567116b2024-08-15 16:22:58 -0700643/* CRADLEPOINT */
644 if (context->forcedinterface &&
645 indextoname(daemon->dhcpfd, if_index, namebuff) &&
646 strcmp(context->forcedinterface, namebuff) == 0)
647 {
648 /* link it onto the current chain if we've not seen it before */
649 if (if_index == param->ind && context->current == context)
650 {
651 if (context->forcedaddress.s_addr != 0)
652 {
653 context->router = context->forcedaddress;
654 context->local = context->forcedaddress;
655 }
656 else
657 {
658 context->router = local;
659 context->local = local;
660 }
661
662 context->current = param->current;
663 param->current = context;
664 }
665
666 if (!(context->flags & CONTEXT_BRDCAST))
667 {
668 if (is_same_net(broadcast, context->start, context->netmask))
669 context->broadcast = broadcast;
670 else
671 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
672 }
673 }
674 else if (context->netmask.s_addr != 0 &&
675/* CRADLEPOINT */
676 is_same_net(local, context->start, context->netmask) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100677 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000678 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100679 /* link it onto the current chain if we've not seen it before */
680 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000681 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100682 context->router = local;
683 context->local = local;
684 context->current = param->current;
685 param->current = context;
686 }
687
688 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000689 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100690 if (is_same_net(broadcast, context->start, context->netmask))
691 context->broadcast = broadcast;
692 else
Simon Kelley0a852542005-03-23 20:28:59 +0000693 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
694 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100695 }
Simon Kelley0a852542005-03-23 20:28:59 +0000696 }
697
Simon Kelleyff7eea22013-09-04 18:01:38 +0100698 for (relay = daemon->relay4; relay; relay = relay->next)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700699 if (relay->local.addr4.s_addr == local.s_addr)
700 relay->iface_index = if_index;
701
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100702 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000703}
704
Simon Kelley824af852008-02-12 20:43:05 +0000705struct dhcp_context *address_available(struct dhcp_context *context,
706 struct in_addr taddr,
707 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000708{
Simon Kelley36717ee2004-09-20 19:20:58 +0100709 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100710 possible ranges. Make sure that the address isn't in use
711 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000712
Simon Kelley36717ee2004-09-20 19:20:58 +0100713 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100714 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100715
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100716 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100717 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100718 return NULL;
719
720 for (tmp = context; tmp; tmp = tmp->current)
721 {
722 start = ntohl(tmp->start.s_addr);
723 end = ntohl(tmp->end.s_addr);
724
Simon Kelley7de060b2011-08-26 17:24:52 +0100725 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100726 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000727 addr <= end &&
728 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100729 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100730 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000731
Simon Kelley59353a62004-11-21 19:34:28 +0000732 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000733}
Simon Kelley59353a62004-11-21 19:34:28 +0000734
Simon Kelley824af852008-02-12 20:43:05 +0000735struct dhcp_context *narrow_context(struct dhcp_context *context,
736 struct in_addr taddr,
737 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000738{
Simon Kelleye17fb622006-01-14 20:33:46 +0000739 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000740 These are chained on ->current.
Josh Soref730c6742017-02-06 16:14:04 +0000741 Here we have an address, and return the actual context corresponding to that
Simon Kelley59353a62004-11-21 19:34:28 +0000742 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000743 any dhcp-range. In that case we return a static range if possible, or failing that,
744 any context on the correct subnet. (If there's more than one, this is a dodgy
745 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000746
Simon Kelleye17fb622006-01-14 20:33:46 +0000747 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000748
Simon Kelley824af852008-02-12 20:43:05 +0000749 if (!(tmp = address_available(context, taddr, netids)))
750 {
751 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100752 if (match_netid(tmp->filter, netids, 1) &&
753 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100754 (tmp->flags & CONTEXT_STATIC))
755 break;
Simon Kelley824af852008-02-12 20:43:05 +0000756
757 if (!tmp)
758 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100759 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100760 is_same_net(taddr, tmp->start, tmp->netmask) &&
761 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000762 break;
763 }
Simon Kelley59353a62004-11-21 19:34:28 +0000764
Simon Kelley824af852008-02-12 20:43:05 +0000765 /* Only one context allowed now */
766 if (tmp)
767 tmp->current = NULL;
768
769 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000770}
771
Simon Kelleydfa666f2004-08-02 18:27:27 +0100772struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
773{
774 struct dhcp_config *config;
775
776 for (config = configs; config; config = config->next)
777 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
778 return config;
779
780 return NULL;
781}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000782
Simon Kelley5ce3e762017-04-28 22:14:20 +0100783/* Check if and address is in use by sending ICMP ping.
784 This wrapper handles a cache and load-limiting.
785 Return is NULL is address in use, or a pointer to a cache entry
786 recording that it isn't. */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100787struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100788{
789 static struct ping_result dummy;
790 struct ping_result *r, *victim = NULL;
791 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
792 ((float)PING_WAIT)));
793
794 /* check if we failed to ping addr sometime in the last
795 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
796 This avoids problems when a stupid client bangs
797 on us repeatedly. As a final check, if we did more
798 than 60% of the possible ping checks in the last
799 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
800 for (count = 0, r = daemon->ping_results; r; r = r->next)
801 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
802 victim = r; /* old record */
803 else
804 {
805 count++;
806 if (r->addr.s_addr == addr.s_addr)
807 return r;
808 }
809
810 /* didn't find cached entry */
Simon Kelleyc7be0162017-05-10 22:21:53 +0100811 if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
Simon Kelley5ce3e762017-04-28 22:14:20 +0100812 {
Simon Kelleyc7be0162017-05-10 22:21:53 +0100813 /* overloaded, or configured not to check, loopback interface, return "not in use" */
Simon Kelley0669ee72018-05-04 16:46:24 +0100814 dummy.hash = hash;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100815 return &dummy;
816 }
817 else if (icmp_ping(addr))
818 return NULL; /* address in use. */
819 else
820 {
821 /* at this point victim may hold an expired record */
822 if (!victim)
823 {
824 if ((victim = whine_malloc(sizeof(struct ping_result))))
825 {
826 victim->next = daemon->ping_results;
827 daemon->ping_results = victim;
828 }
829 }
830
831 /* record that this address is OK for 30s
832 without more ping checks */
833 if (victim)
834 {
835 victim->addr = addr;
836 victim->time = now;
837 victim->hash = hash;
838 }
839 return victim;
840 }
841}
842
Simon Kelley5aabfc72007-08-29 11:24:47 +0100843int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000844 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelleyc7be0162017-05-10 22:21:53 +0100845 struct dhcp_netid *netids, time_t now, int loopback)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000846{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100847 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000848 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000849 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000850
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100851 struct in_addr start, addr;
852 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000853 int i, pass;
854 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100855
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100856 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
857 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100858 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelleyd6b749a2016-04-25 17:05:15 +0100859 j = hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100860
861 /* j == 0 is marker */
862 if (j == 0)
863 j = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100864
Simon Kelleycdeda282006-03-16 20:16:06 +0000865 for (pass = 0; pass <= 1; pass++)
866 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100867 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000868 continue;
869 else if (!match_netid(c->filter, netids, pass))
870 continue;
871 else
872 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100873 if (option_bool(OPT_CONSEC_ADDR))
874 /* seed is largest extant lease addr in this context */
875 start = lease_find_max_addr(c);
876 else
877 /* pick a seed based on hwaddr */
878 start.s_addr = htonl(ntohl(c->start.s_addr) +
879 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
880
881 /* iterate until we find a free address. */
882 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000883
884 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100885 /* eliminate addresses in use by the server. */
886 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100887 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100888 break;
889
Simon Kelley73a08a22009-02-05 20:28:08 +0000890 /* Addresses which end in .255 and .0 are broken in Windows even when using
891 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
892 then 192.168.0.255 is a valid IP address, but not for Windows as it's
893 in the class C range. See KB281579. We therefore don't allocate these
894 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100895 if (!d &&
896 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000897 !config_find_by_address(daemon->dhcp_conf, addr) &&
898 (!IN_CLASSC(ntohl(addr.s_addr)) ||
899 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000900 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000901 /* in consec-ip mode, skip addresses equal to
902 the number of addresses rejected by clients. This
903 should avoid the same client being offered the same
904 address after it has rjected it. */
905 if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
906 c->addr_epoch--;
Simon Kelley5ce3e762017-04-28 22:14:20 +0100907 else
908 {
Simon Kelley9c0d4452019-01-09 17:57:56 +0000909 struct ping_result *r;
910
911 if ((r = do_icmp_ping(now, addr, j, loopback)))
912 {
913 /* consec-ip mode: we offered this address for another client
914 (different hash) recently, don't offer it to this one. */
915 if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
916 {
917 *addrp = addr;
918 return 1;
919 }
920 }
921 else
922 {
923 /* address in use: perturb address selection so that we are
924 less likely to try this address again. */
925 if (!option_bool(OPT_CONSEC_ADDR))
926 c->addr_epoch++;
927 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100928 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000929 }
Simon Kelley5ce3e762017-04-28 22:14:20 +0100930
Simon Kelleycdeda282006-03-16 20:16:06 +0000931 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
932
933 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
934 addr = c->start;
935
936 } while (addr.s_addr != start.s_addr);
937 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100938
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000939 return 0;
940}
941
Simon Kelley5aabfc72007-08-29 11:24:47 +0100942void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000943{
Simon Kelley44a2a312004-03-10 20:04:35 +0000944 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000945 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100946 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100947 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000948 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100949 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100950 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100951 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000952 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100953
954 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000955
956 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100957 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100958 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100959 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100960 }
961
Simon Kelley849a8352006-06-09 21:02:31 +0100962 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100963 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100964 {
965 tmp = config->next;
966 if (config->flags & CONFIG_FROM_ETHERS)
967 {
968 *up = tmp;
969 /* cannot have a clid */
970 if (config->flags & CONFIG_NAME)
971 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000972 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100973 free(config);
974 }
975 else
976 up = &config->next;
977 }
978
Simon Kelley44a2a312004-03-10 20:04:35 +0000979 while (fgets(buff, MAXDNAME, f))
980 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100981 char *host = NULL;
982
Simon Kelleyb8187c82005-11-26 21:46:27 +0000983 lineno++;
984
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700985 while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000986 buff[strlen(buff)-1] = 0;
987
Simon Kelley73a08a22009-02-05 20:28:08 +0000988 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000989 continue;
990
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700991 for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
992 for(; *ip && isspace((unsigned char)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000993 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000994 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000995 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100996 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000997 continue;
998 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000999
1000 /* check for name or dotted-quad */
1001 for (cp = ip; *cp; cp++)
1002 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
1003 break;
1004
1005 if (!*cp)
1006 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001007 if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001008 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001009 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001010 continue;
1011 }
1012
Simon Kelley33820b72004-04-03 21:10:00 +01001013 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +00001014
Simon Kelley16972692006-10-16 20:04:18 +01001015 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +01001016 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +00001017 break;
Simon Kelley44a2a312004-03-10 20:04:35 +00001018 }
1019 else
1020 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001021 int nomem;
1022 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001023 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001024 if (!nomem)
1025 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
1026 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001027 continue;
1028 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001029
Simon Kelley33820b72004-04-03 21:10:00 +01001030 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +00001031
Simon Kelley16972692006-10-16 20:04:18 +01001032 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +01001033 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +00001034 break;
Simon Kelley44a2a312004-03-10 20:04:35 +00001035 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001036
1037 if (config && (config->flags & CONFIG_FROM_ETHERS))
1038 {
1039 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
1040 continue;
1041 }
1042
Simon Kelley1cff1662004-03-12 08:12:58 +00001043 if (!config)
1044 {
Simon Kelley16972692006-10-16 20:04:18 +01001045 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +00001046 {
1047 struct hwaddr_config *conf_addr = config->hwaddr;
1048 if (conf_addr &&
1049 conf_addr->next == NULL &&
1050 conf_addr->wildcard_mask == 0 &&
1051 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
1052 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
1053 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
1054 break;
1055 }
Simon Kelley33820b72004-04-03 21:10:00 +01001056
1057 if (!config)
1058 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01001059 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +01001060 continue;
Simon Kelley849a8352006-06-09 21:02:31 +01001061 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +00001062 config->hwaddr = NULL;
1063 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001064 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +01001065 config->next = daemon->dhcp_conf;
1066 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +01001067 }
1068
1069 config->flags |= flags;
1070
1071 if (flags & CONFIG_NAME)
1072 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001073 config->hostname = host;
1074 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +01001075 }
1076
1077 if (flags & CONFIG_ADDR)
1078 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +00001079 }
Simon Kelley33820b72004-04-03 21:10:00 +01001080
Simon Kelley9009d742008-11-14 20:04:27 +00001081 config->flags |= CONFIG_NOCLID;
1082 if (!config->hwaddr)
1083 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
1084 if (config->hwaddr)
1085 {
1086 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
1087 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
1088 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
1089 config->hwaddr->wildcard_mask = 0;
1090 config->hwaddr->next = NULL;
1091 }
Simon Kelley33820b72004-04-03 21:10:00 +01001092 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +01001093
1094 free(host);
1095
Simon Kelley44a2a312004-03-10 20:04:35 +00001096 }
1097
1098 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +01001099
Simon Kelley7622fc02009-06-04 20:32:05 +01001100 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +00001101}
1102
Simon Kelley44a2a312004-03-10 20:04:35 +00001103
Simon Kelleybb01cb92004-12-13 20:56:23 +00001104/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
1105 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +01001106 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001107 so check here that the domain name is legal as a hostname.
1108 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001109char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +00001110{
Simon Kelley824af852008-02-12 20:43:05 +00001111 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +00001112
1113 if (daemon->port == 0)
1114 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +00001115
Simon Kelleycc921df2019-01-02 22:48:59 +00001116 lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001117
Simon Kelleybb01cb92004-12-13 20:56:23 +00001118 if (lookup && (lookup->flags & F_HOSTS))
1119 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001120 char *dot, *hostname = cache_get_name(lookup);
1121 dot = strchr(hostname, '.');
1122
1123 if (dot && strlen(dot+1) != 0)
1124 {
1125 char *d2 = get_domain(addr);
1126 if (!d2 || !hostname_isequal(dot+1, d2))
1127 return NULL; /* wrong domain */
1128 }
1129
1130 if (!legal_hostname(hostname))
1131 return NULL;
1132
Petr Menšík47b45b22018-08-15 18:17:00 +02001133 safe_strncpy(daemon->dhcp_buff, hostname, 256);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001134 strip_hostname(daemon->dhcp_buff);
1135
1136 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001137 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001138
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001139 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001140}
1141
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001142static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001143{
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001144 struct in_addr giaddr = mess->giaddr;
1145 u8 hops = mess->hops;
1146 struct dhcp_relay *relay;
1147
Simon Kelleyff7eea22013-09-04 18:01:38 +01001148 if (mess->op != BOOTREQUEST)
1149 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001150
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001151 for (relay = daemon->relay4; relay; relay = relay->next)
1152 if (relay->iface_index != 0 && relay->iface_index == iface_index)
1153 break;
1154
1155 /* No relay config. */
1156 if (!relay)
1157 return 0;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001158
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001159 for (; relay; relay = relay->next)
1160 if (relay->iface_index != 0 && relay->iface_index == iface_index)
1161 {
1162 union mysockaddr to;
1163 union all_addr from;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001164
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001165 mess->hops = hops;
1166 mess->giaddr = giaddr;
1167
1168 if ((mess->hops++) > 20)
1169 continue;
1170
1171 /* source address == relay address */
1172 from.addr4 = relay->local.addr4;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001173
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001174 /* already gatewayed ? */
1175 if (giaddr.s_addr)
1176 {
1177 /* if so check if by us, to stomp on loops. */
1178 if (giaddr.s_addr == relay->local.addr4.s_addr)
1179 continue;
1180 }
1181 else
1182 {
1183 /* plug in our address */
1184 mess->giaddr.s_addr = relay->local.addr4.s_addr;
1185 }
1186
1187 to.sa.sa_family = AF_INET;
1188 to.in.sin_addr = relay->server.addr4;
1189 to.in.sin_port = htons(relay->port);
1190
1191 /* Broadcasting to server. */
1192 if (relay->server.addr4.s_addr == 0)
1193 {
1194 struct ifreq ifr;
1195
1196 if (relay->interface)
1197 safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
1198
1199 if (!relay->interface || strchr(relay->interface, '*') ||
1200 ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
1201 {
1202 my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
1203 continue;
1204 }
1205
1206 to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
1207 }
1208
1209#ifdef HAVE_DUMPFILE
Simon Kelleyff7eea22013-09-04 18:01:38 +01001210 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001211 union mysockaddr fromsock;
1212 fromsock.in.sin_port = htons(daemon->dhcp_server_port);
1213 fromsock.in.sin_addr = from.addr4;
1214 fromsock.sa.sa_family = AF_INET;
1215
1216 dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
Simon Kelleyff7eea22013-09-04 18:01:38 +01001217 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001218#endif
1219
1220 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1221
1222 if (option_bool(OPT_LOG_OPTS))
1223 {
1224 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1225 if (relay->server.addr4.s_addr == 0)
1226 snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
1227 else
1228 inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
1229 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
1230 }
1231 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01001232
1233 return 1;
1234}
1235
1236
1237static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1238{
1239 struct dhcp_relay *relay;
1240
1241 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1242 return NULL;
1243
1244 for (relay = daemon->relay4; relay; relay = relay->next)
1245 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001246 if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001247 {
1248 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1249 return relay->iface_index != 0 ? relay : NULL;
1250 }
1251 }
1252
1253 return NULL;
1254}
1255
1256#endif