blob: eb1ea810b573ca4795aeb5c45402d6419a0d2ab9 [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
Simon Kelleyc72daea2012-01-05 21:33:27 +0000228 /* One form of bridging on BSD has the property that packets
229 can be recieved on bridge interfaces which do not have an IP address.
230 We allow these to be treated as aliases of another interface which does have
231 an IP address with --dhcp-bridge=interface,alias,alias */
232 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
233 {
234 for (alias = bridge->alias; alias; alias = alias->next)
Neil Jerram70772c92014-06-11 21:22:40 +0100235 if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000236 {
237 if (!(iface_index = if_nametoindex(bridge->iface)))
238 {
Neil Jerram654f59e2015-06-10 22:06:33 +0100239 my_syslog(MS_DHCP | LOG_WARNING,
240 _("unknown interface %s in bridge-interface"),
241 bridge->iface);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000242 return;
243 }
244 else
245 {
246 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
247 break;
248 }
249 }
250
251 if (alias)
252 break;
253 }
254
Simon Kelley4011c4e2006-10-28 16:26:19 +0100255#ifdef MSG_BCAST
256 /* OpenBSD tells us when a packet was broadcast */
257 if (!(msg.msg_flags & MSG_BCAST))
258 unicast_dest = 1;
259#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000260
Simon Kelleyff7eea22013-09-04 18:01:38 +0100261 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
262 {
263 /* Reply from server, using us as relay. */
264 iface_index = relay->iface_index;
265 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
266 return;
267 is_relay_reply = 1;
268 iov.iov_len = sz;
269#ifdef HAVE_LINUX_NETWORK
270 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
271#endif
272 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000273 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000274 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100275 ifr.ifr_addr.sa_family = AF_INET;
276 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
277 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
278 else
279 {
280 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
281 return;
282 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100283
Simon Kelleyff7eea22013-09-04 18:01:38 +0100284 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
285 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
286 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000287
Simon Kelleyff7eea22013-09-04 18:01:38 +0100288 /* unlinked contexts/relays are marked by context->current == context */
289 for (context = daemon->dhcp; context; context = context->next)
290 context->current = context;
291
292 for (relay = daemon->relay4; relay; relay = relay->next)
293 relay->current = relay;
294
295 parm.current = NULL;
296 parm.relay = NULL;
297 parm.relay_local.s_addr = 0;
298 parm.ind = iface_index;
299
300 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
301 {
302 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
303 for a secondary */
304 struct match_param match;
305
306 match.matched = 0;
307 match.ind = iface_index;
308
309 if (!daemon->if_addrs ||
310 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
311 !match.matched)
312 return;
313
314 iface_addr = match.addr;
315 /* make sure secondary address gets priority in case
316 there is more than one address on the interface in the same subnet */
317 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
318 }
319
320 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000321 return;
322
Simon Kelleyff7eea22013-09-04 18:01:38 +0100323 /* We're relaying this request */
324 if (parm.relay_local.s_addr != 0 &&
325 relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
326 return;
327
328 /* May have configured relay, but not DHCP server */
329 if (!daemon->dhcp)
330 return;
331
332 lease_prune(NULL, now); /* lose any expired leases */
333 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
334 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
335 lease_update_file(now);
336 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000337
Simon Kelleyff7eea22013-09-04 18:01:38 +0100338 if (iov.iov_len == 0)
339 return;
340 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000341
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342 msg.msg_name = &dest;
343 msg.msg_namelen = sizeof(dest);
344 msg.msg_control = NULL;
345 msg.msg_controllen = 0;
346 msg.msg_iov = &iov;
347 iov.iov_base = daemon->dhcp_packet.iov_base;
348
349 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000350 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100351
Simon Kelley3be34542004-09-11 19:12:13 +0100352#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100353 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100354#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000355
Simon Kelley316e2732010-01-22 20:16:09 +0000356 if (pxe_fd)
357 {
358 if (mess->ciaddr.s_addr != 0)
359 dest.sin_addr = mess->ciaddr;
360 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100361 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100362 {
363 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100364 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100365 dest.sin_addr = mess->giaddr;
366 }
367 else if (mess->ciaddr.s_addr)
368 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100369 /* If the client's idea of its own address tallys with
370 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100371 source port too, and send back to that. If we're replying
372 to a DHCPINFORM, trust the source address always. */
373 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100374 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100375 {
Simon Kelley9e038942008-05-30 20:06:34 +0100376 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100377 dest.sin_addr = mess->ciaddr;
378 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100379 }
Simon Kelley824af852008-02-12 20:43:05 +0000380#if defined(HAVE_LINUX_NETWORK)
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000381 else
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100382 {
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000383 /* fill cmsg for outbound interface (both broadcast & unicast) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100384 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100385 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100386 msg.msg_controllen = sizeof(control_u);
387 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100388 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
389 pkt->ipi_ifindex = iface_index;
390 pkt->ipi_spec_dst.s_addr = 0;
391 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000392 cmptr->cmsg_level = IPPROTO_IP;
Lung-Pin Chang65c72122015-03-19 23:22:21 +0000393 cmptr->cmsg_type = IP_PKTINFO;
394
395 if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
396 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
397 {
398 /* broadcast to 255.255.255.255 (or mac address invalid) */
399 dest.sin_addr.s_addr = INADDR_BROADCAST;
400 dest.sin_port = htons(daemon->dhcp_client_port);
401 }
402 else
403 {
404 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
405 struct sockaddr limits size to 14 bytes. */
406 dest.sin_addr = mess->yiaddr;
407 dest.sin_port = htons(daemon->dhcp_client_port);
408 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
409 arp_req.arp_ha.sa_family = mess->htype;
410 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
411 /* interface name already copied in */
412 arp_req.arp_flags = ATF_COM;
413 if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
414 my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
415 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 }
Simon Kelley824af852008-02-12 20:43:05 +0000417#elif defined(HAVE_SOLARIS_NETWORK)
418 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
419 {
420 /* broadcast to 255.255.255.255 (or mac address invalid) */
421 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100422 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000423 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100424 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000425 }
426 else
427 {
428 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
429 Note that this only works for ethernet on solaris, because we use SIOCSARP
430 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
431 mysteriously. Bah. Fall back to broadcast for other net types. */
432 struct arpreq req;
433 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100434 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000435 *((struct sockaddr_in *)&req.arp_pa) = dest;
436 req.arp_ha.sa_family = AF_UNSPEC;
437 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
438 req.arp_flags = ATF_COM;
439 ioctl(daemon->dhcpfd, SIOCSARP, &req);
440 }
441#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100442 else
443 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100444 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100445 return;
446 }
447#endif
448
Simon Kelley824af852008-02-12 20:43:05 +0000449#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000450 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000451#endif
452
Simon Kelleyff841eb2015-03-11 21:36:30 +0000453 while(retry_send(sendmsg(fd, &msg, 0)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000454}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100455
Simon Kelleyc72daea2012-01-05 21:33:27 +0000456/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100457static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000458 struct in_addr netmask, struct in_addr broadcast, void *vparam)
459{
460 struct match_param *param = vparam;
461 struct iname *tmp;
462
Simon Kelley3f2873d2013-05-14 11:28:47 +0100463 (void) label;
464
Simon Kelleyc72daea2012-01-05 21:33:27 +0000465 if (if_index == param->ind)
466 {
467 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
468 if ( tmp->addr.sa.sa_family == AF_INET &&
469 tmp->addr.in.sin_addr.s_addr == local.s_addr)
470 {
471 param->matched = 1;
472 param->addr = local;
473 param->netmask = netmask;
474 param->broadcast = broadcast;
475 break;
476 }
477 }
478
479 return 1;
480}
481
Simon Kelley0a852542005-03-23 20:28:59 +0000482/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100483 of each interface (and any relay address) and does the following things:
484
485 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
486 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
487 3) Fills in local (this host) and router (this host or relay) addresses.
488 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
489
Simon Kelley0a852542005-03-23 20:28:59 +0000490 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100491
Simon Kelley3f2873d2013-05-14 11:28:47 +0100492static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100493 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000494{
495 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100496 struct dhcp_relay *relay;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100497 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100498
499 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100500
Simon Kelley0a852542005-03-23 20:28:59 +0000501 for (context = daemon->dhcp; context; context = context->next)
502 {
503 if (!(context->flags & CONTEXT_NETMASK) &&
504 (is_same_net(local, context->start, netmask) ||
505 is_same_net(local, context->end, netmask)))
506 {
507 if (context->netmask.s_addr != netmask.s_addr &&
508 !(is_same_net(local, context->start, netmask) &&
509 is_same_net(local, context->end, netmask)))
510 {
511 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
512 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100513 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100514 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000515 }
516 context->netmask = netmask;
517 }
518
Simon Kelley7de060b2011-08-26 17:24:52 +0100519 if (context->netmask.s_addr != 0 &&
520 is_same_net(local, context->start, context->netmask) &&
521 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000522 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100523 /* link it onto the current chain if we've not seen it before */
524 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000525 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100526 context->router = local;
527 context->local = local;
528 context->current = param->current;
529 param->current = context;
530 }
531
532 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000533 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100534 if (is_same_net(broadcast, context->start, context->netmask))
535 context->broadcast = broadcast;
536 else
Simon Kelley0a852542005-03-23 20:28:59 +0000537 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
538 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100539 }
Simon Kelley0a852542005-03-23 20:28:59 +0000540 }
541
Simon Kelleyff7eea22013-09-04 18:01:38 +0100542 for (relay = daemon->relay4; relay; relay = relay->next)
543 if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
544 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
545 {
546 relay->current = param->relay;
547 param->relay = relay;
548 param->relay_local = local;
549 }
550
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100551 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000552}
553
Simon Kelley824af852008-02-12 20:43:05 +0000554struct dhcp_context *address_available(struct dhcp_context *context,
555 struct in_addr taddr,
556 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000557{
Simon Kelley36717ee2004-09-20 19:20:58 +0100558 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100559 possible ranges. Make sure that the address isn't in use
560 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561
Simon Kelley36717ee2004-09-20 19:20:58 +0100562 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100563 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100564
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100565 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100566 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100567 return NULL;
568
569 for (tmp = context; tmp; tmp = tmp->current)
570 {
571 start = ntohl(tmp->start.s_addr);
572 end = ntohl(tmp->end.s_addr);
573
Simon Kelley7de060b2011-08-26 17:24:52 +0100574 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100575 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000576 addr <= end &&
577 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100578 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100579 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000580
Simon Kelley59353a62004-11-21 19:34:28 +0000581 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000582}
Simon Kelley59353a62004-11-21 19:34:28 +0000583
Simon Kelley824af852008-02-12 20:43:05 +0000584struct dhcp_context *narrow_context(struct dhcp_context *context,
585 struct in_addr taddr,
586 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000587{
Simon Kelleye17fb622006-01-14 20:33:46 +0000588 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000589 These are chained on ->current.
590 Here we have an address, and return the actual context correponding to that
591 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000592 any dhcp-range. In that case we return a static range if possible, or failing that,
593 any context on the correct subnet. (If there's more than one, this is a dodgy
594 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000595
Simon Kelleye17fb622006-01-14 20:33:46 +0000596 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000597
Simon Kelley824af852008-02-12 20:43:05 +0000598 if (!(tmp = address_available(context, taddr, netids)))
599 {
600 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100601 if (match_netid(tmp->filter, netids, 1) &&
602 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100603 (tmp->flags & CONTEXT_STATIC))
604 break;
Simon Kelley824af852008-02-12 20:43:05 +0000605
606 if (!tmp)
607 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100608 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100609 is_same_net(taddr, tmp->start, tmp->netmask) &&
610 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000611 break;
612 }
Simon Kelley59353a62004-11-21 19:34:28 +0000613
Simon Kelley824af852008-02-12 20:43:05 +0000614 /* Only one context allowed now */
615 if (tmp)
616 tmp->current = NULL;
617
618 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000619}
620
Simon Kelleydfa666f2004-08-02 18:27:27 +0100621struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
622{
623 struct dhcp_config *config;
624
625 for (config = configs; config; config = config->next)
626 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
627 return config;
628
629 return NULL;
630}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000631
Simon Kelley5aabfc72007-08-29 11:24:47 +0100632int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000633 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100634 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000635{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100636 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000637 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000638 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000639
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100640 struct in_addr start, addr;
641 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000642 int i, pass;
643 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100644
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100645 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
646 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100647 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100648 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100649
Simon Kelleycdeda282006-03-16 20:16:06 +0000650 for (pass = 0; pass <= 1; pass++)
651 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100652 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000653 continue;
654 else if (!match_netid(c->filter, netids, pass))
655 continue;
656 else
657 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100658 if (option_bool(OPT_CONSEC_ADDR))
659 /* seed is largest extant lease addr in this context */
660 start = lease_find_max_addr(c);
661 else
662 /* pick a seed based on hwaddr */
663 start.s_addr = htonl(ntohl(c->start.s_addr) +
664 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
665
666 /* iterate until we find a free address. */
667 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000668
669 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100670 /* eliminate addresses in use by the server. */
671 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100672 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100673 break;
674
Simon Kelley73a08a22009-02-05 20:28:08 +0000675 /* Addresses which end in .255 and .0 are broken in Windows even when using
676 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
677 then 192.168.0.255 is a valid IP address, but not for Windows as it's
678 in the class C range. See KB281579. We therefore don't allocate these
679 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100680 if (!d &&
681 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000682 !config_find_by_address(daemon->dhcp_conf, addr) &&
683 (!IN_CLASSC(ntohl(addr.s_addr)) ||
684 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000685 {
686 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100687 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
688 ((float)PING_WAIT)));
689
690 *addrp = addr;
691
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100693 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000694 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100695 on us repeatedly. As a final check, if we did more
696 than 60% of the possible ping checks in the last
697 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000698 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100699 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000700 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100701 else
702 {
703 count++;
704 if (r->addr.s_addr == addr.s_addr)
705 {
706 /* consec-ip mode: we offered this address for another client
707 (different hash) recently, don't offer it to this one. */
708 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
709 break;
710
711 return 1;
712 }
713 }
714
715 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100716 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100717 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000718 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100719 /* address in use: perturb address selection so that we are
720 less likely to try this address again. */
721 if (!option_bool(OPT_CONSEC_ADDR))
722 c->addr_epoch++;
723 }
724 else
725 {
726 /* at this point victim may hold an expired record */
727 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000728 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100729 if ((victim = whine_malloc(sizeof(struct ping_result))))
730 {
731 victim->next = daemon->ping_results;
732 daemon->ping_results = victim;
733 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000734 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100735
736 /* record that this address is OK for 30s
737 without more ping checks */
738 if (victim)
739 {
740 victim->addr = addr;
741 victim->time = now;
742 victim->hash = j;
743 }
744 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000745 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100746 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000747 }
Simon Kelley3be34542004-09-11 19:12:13 +0100748
Simon Kelleycdeda282006-03-16 20:16:06 +0000749 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
750
751 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
752 addr = c->start;
753
754 } while (addr.s_addr != start.s_addr);
755 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100756
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000757 return 0;
758}
759
Simon Kelley5aabfc72007-08-29 11:24:47 +0100760void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000761{
Simon Kelley44a2a312004-03-10 20:04:35 +0000762 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000763 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100764 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100765 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000766 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100767 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100768 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100769 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000770 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100771
772 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000773
774 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100775 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100776 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100777 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100778 }
779
Simon Kelley849a8352006-06-09 21:02:31 +0100780 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100781 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100782 {
783 tmp = config->next;
784 if (config->flags & CONFIG_FROM_ETHERS)
785 {
786 *up = tmp;
787 /* cannot have a clid */
788 if (config->flags & CONFIG_NAME)
789 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000790 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100791 free(config);
792 }
793 else
794 up = &config->next;
795 }
796
Simon Kelley44a2a312004-03-10 20:04:35 +0000797 while (fgets(buff, MAXDNAME, f))
798 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100799 char *host = NULL;
800
Simon Kelleyb8187c82005-11-26 21:46:27 +0000801 lineno++;
802
Simon Kelley824af852008-02-12 20:43:05 +0000803 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000804 buff[strlen(buff)-1] = 0;
805
Simon Kelley73a08a22009-02-05 20:28:08 +0000806 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000807 continue;
808
Simon Kelley824af852008-02-12 20:43:05 +0000809 for (ip = buff; *ip && !isspace((int)*ip); ip++);
810 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000811 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000812 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000813 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100814 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000815 continue;
816 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000817
818 /* check for name or dotted-quad */
819 for (cp = ip; *cp; cp++)
820 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
821 break;
822
823 if (!*cp)
824 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000825 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000826 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100827 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000828 continue;
829 }
830
Simon Kelley33820b72004-04-03 21:10:00 +0100831 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000832
Simon Kelley16972692006-10-16 20:04:18 +0100833 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100834 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000835 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000836 }
837 else
838 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100839 int nomem;
840 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000841 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100842 if (!nomem)
843 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
844 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000845 continue;
846 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100847
Simon Kelley33820b72004-04-03 21:10:00 +0100848 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000849
Simon Kelley16972692006-10-16 20:04:18 +0100850 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100851 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000852 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000853 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100854
855 if (config && (config->flags & CONFIG_FROM_ETHERS))
856 {
857 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
858 continue;
859 }
860
Simon Kelley1cff1662004-03-12 08:12:58 +0000861 if (!config)
862 {
Simon Kelley16972692006-10-16 20:04:18 +0100863 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000864 {
865 struct hwaddr_config *conf_addr = config->hwaddr;
866 if (conf_addr &&
867 conf_addr->next == NULL &&
868 conf_addr->wildcard_mask == 0 &&
869 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
870 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
871 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
872 break;
873 }
Simon Kelley33820b72004-04-03 21:10:00 +0100874
875 if (!config)
876 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100877 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100878 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100879 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000880 config->hwaddr = NULL;
881 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100882 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100883 config->next = daemon->dhcp_conf;
884 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100885 }
886
887 config->flags |= flags;
888
889 if (flags & CONFIG_NAME)
890 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100891 config->hostname = host;
892 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100893 }
894
895 if (flags & CONFIG_ADDR)
896 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000897 }
Simon Kelley33820b72004-04-03 21:10:00 +0100898
Simon Kelley9009d742008-11-14 20:04:27 +0000899 config->flags |= CONFIG_NOCLID;
900 if (!config->hwaddr)
901 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
902 if (config->hwaddr)
903 {
904 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
905 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
906 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
907 config->hwaddr->wildcard_mask = 0;
908 config->hwaddr->next = NULL;
909 }
Simon Kelley33820b72004-04-03 21:10:00 +0100910 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100911
912 free(host);
913
Simon Kelley44a2a312004-03-10 20:04:35 +0000914 }
915
916 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100917
Simon Kelley7622fc02009-06-04 20:32:05 +0100918 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000919}
920
Simon Kelley44a2a312004-03-10 20:04:35 +0000921
Simon Kelleybb01cb92004-12-13 20:56:23 +0000922/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
923 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100924 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100925 so check here that the domain name is legal as a hostname.
926 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100927char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000928{
Simon Kelley824af852008-02-12 20:43:05 +0000929 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000930
931 if (daemon->port == 0)
932 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000933
Simon Kelley824af852008-02-12 20:43:05 +0000934 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100935
Simon Kelleybb01cb92004-12-13 20:56:23 +0000936 if (lookup && (lookup->flags & F_HOSTS))
937 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100938 char *dot, *hostname = cache_get_name(lookup);
939 dot = strchr(hostname, '.');
940
941 if (dot && strlen(dot+1) != 0)
942 {
943 char *d2 = get_domain(addr);
944 if (!d2 || !hostname_isequal(dot+1, d2))
945 return NULL; /* wrong domain */
946 }
947
948 if (!legal_hostname(hostname))
949 return NULL;
950
951 strncpy(daemon->dhcp_buff, hostname, 256);
952 daemon->dhcp_buff[255] = 0;
953 strip_hostname(daemon->dhcp_buff);
954
955 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000956 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100957
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100958 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000959}
960
Simon Kelleyff7eea22013-09-04 18:01:38 +0100961static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
962{
963 /* ->local is same value for all relays on ->current chain */
964 struct all_addr from;
965
966 if (mess->op != BOOTREQUEST)
967 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000968
Simon Kelleyff7eea22013-09-04 18:01:38 +0100969 /* source address == relay address */
970 from.addr.addr4 = relay->local.addr.addr4;
971
972 /* already gatewayed ? */
973 if (mess->giaddr.s_addr)
974 {
975 /* if so check if by us, to stomp on loops. */
976 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
977 return 1;
978 }
979 else
980 {
981 /* plug in our address */
982 mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
983 }
984
985 if ((mess->hops++) > 20)
986 return 1;
987
988 for (; relay; relay = relay->current)
989 {
990 union mysockaddr to;
991
992 to.sa.sa_family = AF_INET;
993 to.in.sin_addr = relay->server.addr.addr4;
994 to.in.sin_port = htons(daemon->dhcp_server_port);
995
996 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
997
998 if (option_bool(OPT_LOG_OPTS))
999 {
1000 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1001 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
1002 }
1003
1004 /* Save this for replies */
1005 relay->iface_index = iface_index;
1006 }
1007
1008 return 1;
1009}
1010
1011
1012static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1013{
1014 struct dhcp_relay *relay;
1015
1016 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1017 return NULL;
1018
1019 for (relay = daemon->relay4; relay; relay = relay->next)
1020 {
1021 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1022 {
1023 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1024 return relay->iface_index != 0 ? relay : NULL;
1025 }
1026 }
1027
1028 return NULL;
1029}
1030
1031#endif