blob: 1c85e42691e29e1f72043efb32044001353dcfb8 [file] [log] [blame]
Simon Kelleyaff33962015-01-31 20:13:40 +00001/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021struct iface_param {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010022 struct dhcp_context *current;
Simon Kelleyff7eea22013-09-04 18:01:38 +010023 struct dhcp_relay *relay;
24 struct in_addr relay_local;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010025 int ind;
26};
27
Simon Kelleyc72daea2012-01-05 21:33:27 +000028struct match_param {
29 int ind, matched;
30 struct in_addr netmask, broadcast, addr;
31};
32
Simon Kelley3f2873d2013-05-14 11:28:47 +010033static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010034 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelley3f2873d2013-05-14 11:28:47 +010035static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +000036 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelleyff7eea22013-09-04 18:01:38 +010037static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
38static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010039
Simon Kelley316e2732010-01-22 20:16:09 +000040static int make_fd(int port)
Simon Kelley44a2a312004-03-10 20:04:35 +000041{
42 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
43 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010044 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000045#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
46 int mtu = IP_PMTUDISC_DONT;
47#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000048#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
49 int tos = IPTOS_CLASS_CS6;
50#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010051
Simon Kelley44a2a312004-03-10 20:04:35 +000052 if (fd == -1)
Simon Kelley7622fc02009-06-04 20:32:05 +010053 die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000054
Simon Kelley824af852008-02-12 20:43:05 +000055 if (!fix_fd(fd) ||
56#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +000057 setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
58#endif
59#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
60 setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +000061#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010062#if defined(HAVE_LINUX_NETWORK)
Simon Kelleyc72daea2012-01-05 21:33:27 +000063 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley7622fc02009-06-04 20:32:05 +010064#else
Simon Kelley3be34542004-09-11 19:12:13 +010065 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000066#endif
Simon Kelley3be34542004-09-11 19:12:13 +010067 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010068 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000069
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000070 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010071 instance binding port 67. That's OK if they serve different networks.
Simon Kelley56a11422013-04-02 17:02:58 +010072 Need to set REUSEADDR|REUSEPORT to make this posible.
73 Handle the case that REUSEPORT is defined, but the kernel doesn't
74 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley54dd3932012-06-20 11:23:38 +010075 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley4011c4e2006-10-28 16:26:19 +010076 {
Simon Kelleyffbad342013-08-14 15:53:57 +010077 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010078
Simon Kelley4011c4e2006-10-28 16:26:19 +010079#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010080 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010081 errno == ENOPROTOOPT)
82 rc = 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +010083#endif
Simon Kelley56a11422013-04-02 17:02:58 +010084
Simon Kelleyffbad342013-08-14 15:53:57 +010085 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010086 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
87
Simon Kelley4011c4e2006-10-28 16:26:19 +010088 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010089 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010090 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000091
Simon Kelley849a8352006-06-09 21:02:31 +010092 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000093 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000094 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000095 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010096#ifdef HAVE_SOCKADDR_SA_LEN
97 saddr.sin_len = sizeof(struct sockaddr_in);
98#endif
99
Simon Kelley44a2a312004-03-10 20:04:35 +0000100 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100101 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +0000102
Simon Kelley316e2732010-01-22 20:16:09 +0000103 return fd;
104}
105
106void dhcp_init(void)
107{
108#if defined(HAVE_BSD_NETWORK)
109 int oneopt = 1;
110#endif
111
112 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
113 if (daemon->enable_pxe)
114 daemon->pxefd = make_fd(PXE_PORT);
115 else
116 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100117
Simon Kelley824af852008-02-12 20:43:05 +0000118#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119 /* When we're not using capabilities, we need to do this here before
120 we drop root. Also, set buffer size small, to avoid wasting
121 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000122
Simon Kelley28866e92011-02-14 20:19:14 +0000123 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100124 daemon->dhcp_icmp_fd = -1;
125 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
126 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100127 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100128
129 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100130 init_bpf();
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100131#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000132}
133
Simon Kelley316e2732010-01-22 20:16:09 +0000134void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000135{
Simon Kelley316e2732010-01-22 20:16:09 +0000136 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000138 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100139 struct dhcp_relay *relay;
140 int is_relay_reply = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000141 struct iname *tmp;
142 struct ifreq ifr;
143 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000145 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100146 struct iovec iov;
147 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100148 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000149 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100150 struct iface_param parm;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100151#ifdef HAVE_LINUX_NETWORK
152 struct arpreq arp_req;
153#endif
154
Simon Kelley44a2a312004-03-10 20:04:35 +0000155 union {
156 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000157#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000158 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000159#elif defined(HAVE_SOLARIS_NETWORK)
160 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100161#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000162 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
163#endif
164 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000165 struct dhcp_bridge *bridge, *alias;
166
167 msg.msg_controllen = sizeof(control_u);
168 msg.msg_control = control_u.control;
169 msg.msg_name = &dest;
170 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100171 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000172 msg.msg_iovlen = 1;
173
Simon Kelleyc72daea2012-01-05 21:33:27 +0000174 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
175 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000176 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000177
178 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100179 if (msg.msg_controllen >= sizeof(struct cmsghdr))
180 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000181 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100182 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100183 union {
184 unsigned char *c;
185 struct in_pktinfo *p;
186 } p;
187 p.c = CMSG_DATA(cmptr);
188 iface_index = p.p->ipi_ifindex;
189 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100190 unicast_dest = 1;
191 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100192
193#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100194 if (msg.msg_controllen >= sizeof(struct cmsghdr))
195 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
196 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100197 {
198 union {
199 unsigned char *c;
200 struct sockaddr_dl *s;
201 } p;
202 p.c = CMSG_DATA(cmptr);
203 iface_index = p.s->sdl_index;
204 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100205
Simon Kelley7622fc02009-06-04 20:32:05 +0100206#elif defined(HAVE_SOLARIS_NETWORK)
207 if (msg.msg_controllen >= sizeof(struct cmsghdr))
208 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
209 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100210 {
211 union {
212 unsigned char *c;
213 unsigned int *i;
214 } p;
215 p.c = CMSG_DATA(cmptr);
216 iface_index = *(p.i);
217 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100218#endif
219
220 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
221 return;
222
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100223#ifdef HAVE_LINUX_NETWORK
224 /* ARP fiddling uses original interface even if we pretend to use a different one. */
225 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
226#endif
227
Neil Jerram4918bd52015-06-10 22:23:20 +0100228 /* If the interface on which the DHCP request was received is an
229 alias of some other interface (as specified by the
230 --bridge-interface option), change ifr.ifr_name so that we look
231 for DHCP contexts associated with the aliased interface instead
232 of with the aliasing one. */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000233 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
234 {
235 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100236 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000237 {
238 if (!(iface_index = if_nametoindex(bridge->iface)))
239 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100240 my_syslog(MS_DHCP | LOG_WARNING,
241 _("unknown interface %s in bridge-interface"),
242 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000243 return;
244 }
245 else
246 {
247 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
248 break;
249 }
250 }
251
252 if (alias)
253 break;
254 }
255
Simon Kelley4011c4e2006-10-28 16:26:19 +0100256#ifdef MSG_BCAST
257 /* OpenBSD tells us when a packet was broadcast */
258 if (!(msg.msg_flags & MSG_BCAST))
259 unicast_dest = 1;
260#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000261
Simon Kelleyff7eea22013-09-04 18:01:38 +0100262 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
263 {
264 /* Reply from server, using us as relay. */
265 iface_index = relay->iface_index;
266 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
267 return;
268 is_relay_reply = 1;
269 iov.iov_len = sz;
270#ifdef HAVE_LINUX_NETWORK
271 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
272#endif
273 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000274 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000275 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100276 ifr.ifr_addr.sa_family = AF_INET;
277 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
278 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
279 else
280 {
281 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
282 return;
283 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100284
Simon Kelleyff7eea22013-09-04 18:01:38 +0100285 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
286 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
287 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000288
Simon Kelleyff7eea22013-09-04 18:01:38 +0100289 /* unlinked contexts/relays are marked by context->current == context */
290 for (context = daemon->dhcp; context; context = context->next)
291 context->current = context;
292
293 for (relay = daemon->relay4; relay; relay = relay->next)
294 relay->current = relay;
295
296 parm.current = NULL;
297 parm.relay = NULL;
298 parm.relay_local.s_addr = 0;
299 parm.ind = iface_index;
300
301 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
302 {
303 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
304 for a secondary */
305 struct match_param match;
306
307 match.matched = 0;
308 match.ind = iface_index;
309
310 if (!daemon->if_addrs ||
311 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
312 !match.matched)
313 return;
314
315 iface_addr = match.addr;
316 /* make sure secondary address gets priority in case
317 there is more than one address on the interface in the same subnet */
318 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
319 }
320
321 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000322 return;
323
Simon Kelleyff7eea22013-09-04 18:01:38 +0100324 /* We're relaying this request */
325 if (parm.relay_local.s_addr != 0 &&
326 relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
327 return;
328
329 /* May have configured relay, but not DHCP server */
330 if (!daemon->dhcp)
331 return;
332
333 lease_prune(NULL, now); /* lose any expired leases */
334 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
335 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
336 lease_update_file(now);
337 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000338
Simon Kelleyff7eea22013-09-04 18:01:38 +0100339 if (iov.iov_len == 0)
340 return;
341 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000342
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100343 msg.msg_name = &dest;
344 msg.msg_namelen = sizeof(dest);
345 msg.msg_control = NULL;
346 msg.msg_controllen = 0;
347 msg.msg_iov = &iov;
348 iov.iov_base = daemon->dhcp_packet.iov_base;
349
350 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000351 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100352
Simon Kelley3be34542004-09-11 19:12:13 +0100353#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100354 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100355#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000356
Simon Kelley316e2732010-01-22 20:16:09 +0000357 if (pxe_fd)
358 {
359 if (mess->ciaddr.s_addr != 0)
360 dest.sin_addr = mess->ciaddr;
361 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100362 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100363 {
364 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100365 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100366 dest.sin_addr = mess->giaddr;
367 }
368 else if (mess->ciaddr.s_addr)
369 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100370 /* If the client's idea of its own address tallys with
371 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100372 source port too, and send back to that. If we're replying
373 to a DHCPINFORM, trust the source address always. */
374 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100375 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100376 {
Simon Kelley9e038942008-05-30 20:06:34 +0100377 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100378 dest.sin_addr = mess->ciaddr;
379 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100380 }
Simon Kelley824af852008-02-12 20:43:05 +0000381#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000382 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100383 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000384 /* fill cmsg for outbound interface (both broadcast & unicast) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100385 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100386 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100387 msg.msg_controllen = sizeof(control_u);
388 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100389 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
390 pkt->ipi_ifindex = iface_index;
391 pkt->ipi_spec_dst.s_addr = 0;
392 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000393 cmptr->cmsg_level = IPPROTO_IP;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000394 cmptr->cmsg_type = IP_PKTINFO;
395
396 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
397 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
398 {
399 /* broadcast to 255.255.255.255 (or mac address invalid) */
400 dest.sin_addr.s_addr = INADDR_BROADCAST;
401 dest.sin_port = htons(daemon->dhcp_client_port);
402 }
403 else
404 {
405 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
406 struct sockaddr limits size to 14 bytes. */
407 dest.sin_addr = mess->yiaddr;
408 dest.sin_port = htons(daemon->dhcp_client_port);
409 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
410 arp_req.arp_ha.sa_family = mess->htype;
411 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
412 /* interface name already copied in */
413 arp_req.arp_flags = ATF_COM;
414 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
415 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
416 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000417 }
Simon Kelley824af852008-02-12 20:43:05 +0000418#elif defined(HAVE_SOLARIS_NETWORK)
419 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
420 {
421 /* broadcast to 255.255.255.255 (or mac address invalid) */
422 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100423 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000424 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100425 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000426 }
427 else
428 {
429 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
430 Note that this only works for ethernet on solaris, because we use SIOCSARP
431 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
432 mysteriously. Bah. Fall back to broadcast for other net types. */
433 struct arpreq req;
434 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100435 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000436 *((struct sockaddr_in *)&req.arp_pa) = dest;
437 req.arp_ha.sa_family = AF_UNSPEC;
438 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
439 req.arp_flags = ATF_COM;
440 ioctl(daemon->dhcpfd, SIOCSARP, &req);
441 }
442#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100443 else
444 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100445 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100446 return;
447 }
448#endif
449
Simon Kelley824af852008-02-12 20:43:05 +0000450#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000451 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000452#endif
453
Simon Kelleyff841eb2015-03-11 21:36:30 +0000454 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley98079ea2015-10-13 20:30:32 +0100455
456 /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
457 if (errno != 0)
458 my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
459 inet_ntoa(dest.sin_addr), strerror(errno));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000460}
Simon Kelley98079ea2015-10-13 20:30:32 +0100461
Simon Kelleyc72daea2012-01-05 21:33:27 +0000462/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100463static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000464 struct in_addr netmask, struct in_addr broadcast, void *vparam)
465{
466 struct match_param *param = vparam;
467 struct iname *tmp;
468
Simon Kelley3f2873d2013-05-14 11:28:47 +0100469 (void) label;
470
Simon Kelleyc72daea2012-01-05 21:33:27 +0000471 if (if_index == param->ind)
472 {
473 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
474 if ( tmp->addr.sa.sa_family == AF_INET &&
475 tmp->addr.in.sin_addr.s_addr == local.s_addr)
476 {
477 param->matched = 1;
478 param->addr = local;
479 param->netmask = netmask;
480 param->broadcast = broadcast;
481 break;
482 }
483 }
484
485 return 1;
486}
487
Simon Kelley0a852542005-03-23 20:28:59 +0000488/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100489 of each interface (and any relay address) and does the following things:
490
491 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
492 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
493 3) Fills in local (this host) and router (this host or relay) addresses.
494 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
495
Simon Kelley0a852542005-03-23 20:28:59 +0000496 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100497
Simon Kelley3f2873d2013-05-14 11:28:47 +0100498static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100499 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000500{
501 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100502 struct dhcp_relay *relay;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100503 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100504
505 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100506
Simon Kelley0a852542005-03-23 20:28:59 +0000507 for (context = daemon->dhcp; context; context = context->next)
508 {
509 if (!(context->flags & CONTEXT_NETMASK) &&
510 (is_same_net(local, context->start, netmask) ||
511 is_same_net(local, context->end, netmask)))
512 {
513 if (context->netmask.s_addr != netmask.s_addr &&
514 !(is_same_net(local, context->start, netmask) &&
515 is_same_net(local, context->end, netmask)))
516 {
517 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
518 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100519 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100520 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000521 }
522 context->netmask = netmask;
523 }
524
Simon Kelley7de060b2011-08-26 17:24:52 +0100525 if (context->netmask.s_addr != 0 &&
526 is_same_net(local, context->start, context->netmask) &&
527 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000528 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100529 /* link it onto the current chain if we've not seen it before */
530 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000531 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100532 context->router = local;
533 context->local = local;
534 context->current = param->current;
535 param->current = context;
536 }
537
538 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000539 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100540 if (is_same_net(broadcast, context->start, context->netmask))
541 context->broadcast = broadcast;
542 else
Simon Kelley0a852542005-03-23 20:28:59 +0000543 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
544 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100545 }
Simon Kelley0a852542005-03-23 20:28:59 +0000546 }
547
Simon Kelleyff7eea22013-09-04 18:01:38 +0100548 for (relay = daemon->relay4; relay; relay = relay->next)
549 if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
550 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
551 {
552 relay->current = param->relay;
553 param->relay = relay;
554 param->relay_local = local;
555 }
556
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100557 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000558}
559
Simon Kelley824af852008-02-12 20:43:05 +0000560struct dhcp_context *address_available(struct dhcp_context *context,
561 struct in_addr taddr,
562 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000563{
Simon Kelley36717ee2004-09-20 19:20:58 +0100564 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100565 possible ranges. Make sure that the address isn't in use
566 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000567
Simon Kelley36717ee2004-09-20 19:20:58 +0100568 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100569 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100570
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100571 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100572 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100573 return NULL;
574
575 for (tmp = context; tmp; tmp = tmp->current)
576 {
577 start = ntohl(tmp->start.s_addr);
578 end = ntohl(tmp->end.s_addr);
579
Simon Kelley7de060b2011-08-26 17:24:52 +0100580 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100581 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000582 addr <= end &&
583 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100584 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100585 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000586
Simon Kelley59353a62004-11-21 19:34:28 +0000587 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000588}
Simon Kelley59353a62004-11-21 19:34:28 +0000589
Simon Kelley824af852008-02-12 20:43:05 +0000590struct dhcp_context *narrow_context(struct dhcp_context *context,
591 struct in_addr taddr,
592 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000593{
Simon Kelleye17fb622006-01-14 20:33:46 +0000594 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000595 These are chained on ->current.
596 Here we have an address, and return the actual context correponding to that
597 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000598 any dhcp-range. In that case we return a static range if possible, or failing that,
599 any context on the correct subnet. (If there's more than one, this is a dodgy
600 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000601
Simon Kelleye17fb622006-01-14 20:33:46 +0000602 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000603
Simon Kelley824af852008-02-12 20:43:05 +0000604 if (!(tmp = address_available(context, taddr, netids)))
605 {
606 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100607 if (match_netid(tmp->filter, netids, 1) &&
608 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100609 (tmp->flags & CONTEXT_STATIC))
610 break;
Simon Kelley824af852008-02-12 20:43:05 +0000611
612 if (!tmp)
613 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100614 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100615 is_same_net(taddr, tmp->start, tmp->netmask) &&
616 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000617 break;
618 }
Simon Kelley59353a62004-11-21 19:34:28 +0000619
Simon Kelley824af852008-02-12 20:43:05 +0000620 /* Only one context allowed now */
621 if (tmp)
622 tmp->current = NULL;
623
624 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000625}
626
Simon Kelleydfa666f2004-08-02 18:27:27 +0100627struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
628{
629 struct dhcp_config *config;
630
631 for (config = configs; config; config = config->next)
632 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
633 return config;
634
635 return NULL;
636}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000637
Simon Kelley5aabfc72007-08-29 11:24:47 +0100638int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000639 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100640 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000641{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100642 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000643 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000644 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000645
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100646 struct in_addr start, addr;
647 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000648 int i, pass;
649 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100650
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100651 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
652 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100653 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100654 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100655
Simon Kelleycdeda282006-03-16 20:16:06 +0000656 for (pass = 0; pass <= 1; pass++)
657 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100658 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000659 continue;
660 else if (!match_netid(c->filter, netids, pass))
661 continue;
662 else
663 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100664 if (option_bool(OPT_CONSEC_ADDR))
665 /* seed is largest extant lease addr in this context */
666 start = lease_find_max_addr(c);
667 else
668 /* pick a seed based on hwaddr */
669 start.s_addr = htonl(ntohl(c->start.s_addr) +
670 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
671
672 /* iterate until we find a free address. */
673 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000674
675 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100676 /* eliminate addresses in use by the server. */
677 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100678 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100679 break;
680
Simon Kelley73a08a22009-02-05 20:28:08 +0000681 /* Addresses which end in .255 and .0 are broken in Windows even when using
682 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
683 then 192.168.0.255 is a valid IP address, but not for Windows as it's
684 in the class C range. See KB281579. We therefore don't allocate these
685 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100686 if (!d &&
687 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000688 !config_find_by_address(daemon->dhcp_conf, addr) &&
689 (!IN_CLASSC(ntohl(addr.s_addr)) ||
690 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000691 {
692 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100693 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
694 ((float)PING_WAIT)));
695
696 *addrp = addr;
697
Simon Kelleycdeda282006-03-16 20:16:06 +0000698 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100699 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000700 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100701 on us repeatedly. As a final check, if we did more
702 than 60% of the possible ping checks in the last
703 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000704 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100705 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000706 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100707 else
708 {
709 count++;
710 if (r->addr.s_addr == addr.s_addr)
711 {
712 /* consec-ip mode: we offered this address for another client
713 (different hash) recently, don't offer it to this one. */
714 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
715 break;
716
717 return 1;
718 }
719 }
720
721 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100722 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100723 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000724 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100725 /* address in use: perturb address selection so that we are
726 less likely to try this address again. */
727 if (!option_bool(OPT_CONSEC_ADDR))
728 c->addr_epoch++;
729 }
730 else
731 {
732 /* at this point victim may hold an expired record */
733 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000734 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100735 if ((victim = whine_malloc(sizeof(struct ping_result))))
736 {
737 victim->next = daemon->ping_results;
738 daemon->ping_results = victim;
739 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000740 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100741
742 /* record that this address is OK for 30s
743 without more ping checks */
744 if (victim)
745 {
746 victim->addr = addr;
747 victim->time = now;
748 victim->hash = j;
749 }
750 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000751 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100752 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000753 }
Simon Kelley3be34542004-09-11 19:12:13 +0100754
Simon Kelleycdeda282006-03-16 20:16:06 +0000755 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
756
757 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
758 addr = c->start;
759
760 } while (addr.s_addr != start.s_addr);
761 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100762
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000763 return 0;
764}
765
Simon Kelley5aabfc72007-08-29 11:24:47 +0100766void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000767{
Simon Kelley44a2a312004-03-10 20:04:35 +0000768 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000769 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100770 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100771 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000772 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100773 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100774 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100775 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000776 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100777
778 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000779
780 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100781 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100782 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100783 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100784 }
785
Simon Kelley849a8352006-06-09 21:02:31 +0100786 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100787 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100788 {
789 tmp = config->next;
790 if (config->flags & CONFIG_FROM_ETHERS)
791 {
792 *up = tmp;
793 /* cannot have a clid */
794 if (config->flags & CONFIG_NAME)
795 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000796 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100797 free(config);
798 }
799 else
800 up = &config->next;
801 }
802
Simon Kelley44a2a312004-03-10 20:04:35 +0000803 while (fgets(buff, MAXDNAME, f))
804 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100805 char *host = NULL;
806
Simon Kelleyb8187c82005-11-26 21:46:27 +0000807 lineno++;
808
Simon Kelley824af852008-02-12 20:43:05 +0000809 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000810 buff[strlen(buff)-1] = 0;
811
Simon Kelley73a08a22009-02-05 20:28:08 +0000812 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000813 continue;
814
Simon Kelley824af852008-02-12 20:43:05 +0000815 for (ip = buff; *ip && !isspace((int)*ip); ip++);
816 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000817 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000818 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000819 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100820 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000821 continue;
822 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000823
824 /* check for name or dotted-quad */
825 for (cp = ip; *cp; cp++)
826 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
827 break;
828
829 if (!*cp)
830 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000831 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000832 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000834 continue;
835 }
836
Simon Kelley33820b72004-04-03 21:10:00 +0100837 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000838
Simon Kelley16972692006-10-16 20:04:18 +0100839 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100840 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000841 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000842 }
843 else
844 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100845 int nomem;
846 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000847 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100848 if (!nomem)
849 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
850 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000851 continue;
852 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100853
Simon Kelley33820b72004-04-03 21:10:00 +0100854 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000855
Simon Kelley16972692006-10-16 20:04:18 +0100856 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100857 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000858 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000859 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100860
861 if (config && (config->flags & CONFIG_FROM_ETHERS))
862 {
863 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
864 continue;
865 }
866
Simon Kelley1cff1662004-03-12 08:12:58 +0000867 if (!config)
868 {
Simon Kelley16972692006-10-16 20:04:18 +0100869 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000870 {
871 struct hwaddr_config *conf_addr = config->hwaddr;
872 if (conf_addr &&
873 conf_addr->next == NULL &&
874 conf_addr->wildcard_mask == 0 &&
875 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
876 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
877 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
878 break;
879 }
Simon Kelley33820b72004-04-03 21:10:00 +0100880
881 if (!config)
882 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100883 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100884 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100885 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000886 config->hwaddr = NULL;
887 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100888 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100889 config->next = daemon->dhcp_conf;
890 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100891 }
892
893 config->flags |= flags;
894
895 if (flags & CONFIG_NAME)
896 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100897 config->hostname = host;
898 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100899 }
900
901 if (flags & CONFIG_ADDR)
902 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000903 }
Simon Kelley33820b72004-04-03 21:10:00 +0100904
Simon Kelley9009d742008-11-14 20:04:27 +0000905 config->flags |= CONFIG_NOCLID;
906 if (!config->hwaddr)
907 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
908 if (config->hwaddr)
909 {
910 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
911 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
912 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
913 config->hwaddr->wildcard_mask = 0;
914 config->hwaddr->next = NULL;
915 }
Simon Kelley33820b72004-04-03 21:10:00 +0100916 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100917
918 free(host);
919
Simon Kelley44a2a312004-03-10 20:04:35 +0000920 }
921
922 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100923
Simon Kelley7622fc02009-06-04 20:32:05 +0100924 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000925}
926
Simon Kelley44a2a312004-03-10 20:04:35 +0000927
Simon Kelleybb01cb92004-12-13 20:56:23 +0000928/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
929 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100930 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100931 so check here that the domain name is legal as a hostname.
932 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100933char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000934{
Simon Kelley824af852008-02-12 20:43:05 +0000935 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000936
937 if (daemon->port == 0)
938 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000939
Simon Kelley824af852008-02-12 20:43:05 +0000940 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100941
Simon Kelleybb01cb92004-12-13 20:56:23 +0000942 if (lookup && (lookup->flags & F_HOSTS))
943 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100944 char *dot, *hostname = cache_get_name(lookup);
945 dot = strchr(hostname, '.');
946
947 if (dot && strlen(dot+1) != 0)
948 {
949 char *d2 = get_domain(addr);
950 if (!d2 || !hostname_isequal(dot+1, d2))
951 return NULL; /* wrong domain */
952 }
953
954 if (!legal_hostname(hostname))
955 return NULL;
956
957 strncpy(daemon->dhcp_buff, hostname, 256);
958 daemon->dhcp_buff[255] = 0;
959 strip_hostname(daemon->dhcp_buff);
960
961 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000962 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100963
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100964 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000965}
966
Simon Kelleyff7eea22013-09-04 18:01:38 +0100967static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
968{
969 /* ->local is same value for all relays on ->current chain */
970 struct all_addr from;
971
972 if (mess->op != BOOTREQUEST)
973 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000974
Simon Kelleyff7eea22013-09-04 18:01:38 +0100975 /* source address == relay address */
976 from.addr.addr4 = relay->local.addr.addr4;
977
978 /* already gatewayed ? */
979 if (mess->giaddr.s_addr)
980 {
981 /* if so check if by us, to stomp on loops. */
982 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
983 return 1;
984 }
985 else
986 {
987 /* plug in our address */
988 mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
989 }
990
991 if ((mess->hops++) > 20)
992 return 1;
993
994 for (; relay; relay = relay->current)
995 {
996 union mysockaddr to;
997
998 to.sa.sa_family = AF_INET;
999 to.in.sin_addr = relay->server.addr.addr4;
1000 to.in.sin_port = htons(daemon->dhcp_server_port);
1001
1002 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1003
1004 if (option_bool(OPT_LOG_OPTS))
1005 {
1006 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1007 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
1008 }
1009
1010 /* Save this for replies */
1011 relay->iface_index = iface_index;
1012 }
1013
1014 return 1;
1015}
1016
1017
1018static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1019{
1020 struct dhcp_relay *relay;
1021
1022 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1023 return NULL;
1024
1025 for (relay = daemon->relay4; relay; relay = relay->next)
1026 {
1027 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1028 {
1029 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1030 return relay->iface_index != 0 ? relay : NULL;
1031 }
1032 }
1033
1034 return NULL;
1035}
1036
1037#endif