blob: 573de5b08c377800b07b0becce942dc04d0aa5cb [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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)
235 if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
236 {
237 if (!(iface_index = if_nametoindex(bridge->iface)))
238 {
239 my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
240 return;
241 }
242 else
243 {
244 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
245 break;
246 }
247 }
248
249 if (alias)
250 break;
251 }
252
Simon Kelley4011c4e2006-10-28 16:26:19 +0100253#ifdef MSG_BCAST
254 /* OpenBSD tells us when a packet was broadcast */
255 if (!(msg.msg_flags & MSG_BCAST))
256 unicast_dest = 1;
257#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000258
Simon Kelleyff7eea22013-09-04 18:01:38 +0100259 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
260 {
261 /* Reply from server, using us as relay. */
262 iface_index = relay->iface_index;
263 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
264 return;
265 is_relay_reply = 1;
266 iov.iov_len = sz;
267#ifdef HAVE_LINUX_NETWORK
268 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
269#endif
270 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000271 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000272 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100273 ifr.ifr_addr.sa_family = AF_INET;
274 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
275 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
276 else
277 {
278 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
279 return;
280 }
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100281
Simon Kelleyff7eea22013-09-04 18:01:38 +0100282 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
283 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
284 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000285
Simon Kelleyff7eea22013-09-04 18:01:38 +0100286 /* unlinked contexts/relays are marked by context->current == context */
287 for (context = daemon->dhcp; context; context = context->next)
288 context->current = context;
289
290 for (relay = daemon->relay4; relay; relay = relay->next)
291 relay->current = relay;
292
293 parm.current = NULL;
294 parm.relay = NULL;
295 parm.relay_local.s_addr = 0;
296 parm.ind = iface_index;
297
298 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
299 {
300 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
301 for a secondary */
302 struct match_param match;
303
304 match.matched = 0;
305 match.ind = iface_index;
306
307 if (!daemon->if_addrs ||
308 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
309 !match.matched)
310 return;
311
312 iface_addr = match.addr;
313 /* make sure secondary address gets priority in case
314 there is more than one address on the interface in the same subnet */
315 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
316 }
317
318 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000319 return;
320
Simon Kelleyff7eea22013-09-04 18:01:38 +0100321 /* We're relaying this request */
322 if (parm.relay_local.s_addr != 0 &&
323 relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
324 return;
325
326 /* May have configured relay, but not DHCP server */
327 if (!daemon->dhcp)
328 return;
329
330 lease_prune(NULL, now); /* lose any expired leases */
331 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
332 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
333 lease_update_file(now);
334 lease_update_dns(0);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000335
Simon Kelleyff7eea22013-09-04 18:01:38 +0100336 if (iov.iov_len == 0)
337 return;
338 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000339
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100340 msg.msg_name = &dest;
341 msg.msg_namelen = sizeof(dest);
342 msg.msg_control = NULL;
343 msg.msg_controllen = 0;
344 msg.msg_iov = &iov;
345 iov.iov_base = daemon->dhcp_packet.iov_base;
346
347 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000348 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100349
Simon Kelley3be34542004-09-11 19:12:13 +0100350#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100351 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100352#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000353
Simon Kelley316e2732010-01-22 20:16:09 +0000354 if (pxe_fd)
355 {
356 if (mess->ciaddr.s_addr != 0)
357 dest.sin_addr = mess->ciaddr;
358 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100359 else if (mess->giaddr.s_addr && !is_relay_reply)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100360 {
361 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100362 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100363 dest.sin_addr = mess->giaddr;
364 }
365 else if (mess->ciaddr.s_addr)
366 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100367 /* If the client's idea of its own address tallys with
368 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100369 source port too, and send back to that. If we're replying
370 to a DHCPINFORM, trust the source address always. */
371 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
Simon Kelleyff7eea22013-09-04 18:01:38 +0100372 dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
Simon Kelley208b65c2006-08-05 21:41:37 +0100373 {
Simon Kelley9e038942008-05-30 20:06:34 +0100374 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100375 dest.sin_addr = mess->ciaddr;
376 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100377 }
Simon Kelley824af852008-02-12 20:43:05 +0000378#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100379 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
380 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100381 {
Simon Kelley849a8352006-06-09 21:02:31 +0100382 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100383 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100384 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100385 msg.msg_controllen = sizeof(control_u);
386 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100387 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
388 pkt->ipi_ifindex = iface_index;
389 pkt->ipi_spec_dst.s_addr = 0;
390 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000391 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000392 cmptr->cmsg_type = IP_PKTINFO;
393 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100394 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000395 }
396 else
397 {
Simon Kelley849a8352006-06-09 21:02:31 +0100398 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
399 struct sockaddr limits size to 14 bytes. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100400 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100401 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100402 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
403 arp_req.arp_ha.sa_family = mess->htype;
404 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
405 /* interface name already copied in */
406 arp_req.arp_flags = ATF_COM;
407 ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000408 }
Simon Kelley824af852008-02-12 20:43:05 +0000409#elif defined(HAVE_SOLARIS_NETWORK)
410 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
411 {
412 /* broadcast to 255.255.255.255 (or mac address invalid) */
413 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100414 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000415 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100416 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000417 }
418 else
419 {
420 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
421 Note that this only works for ethernet on solaris, because we use SIOCSARP
422 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
423 mysteriously. Bah. Fall back to broadcast for other net types. */
424 struct arpreq req;
425 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100426 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000427 *((struct sockaddr_in *)&req.arp_pa) = dest;
428 req.arp_ha.sa_family = AF_UNSPEC;
429 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
430 req.arp_flags = ATF_COM;
431 ioctl(daemon->dhcpfd, SIOCSARP, &req);
432 }
433#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100434 else
435 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100436 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100437 return;
438 }
439#endif
440
Simon Kelley824af852008-02-12 20:43:05 +0000441#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000442 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000443#endif
444
Simon Kelley316e2732010-01-22 20:16:09 +0000445 while(sendmsg(fd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000446}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100447
Simon Kelleyc72daea2012-01-05 21:33:27 +0000448/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100449static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000450 struct in_addr netmask, struct in_addr broadcast, void *vparam)
451{
452 struct match_param *param = vparam;
453 struct iname *tmp;
454
Simon Kelley3f2873d2013-05-14 11:28:47 +0100455 (void) label;
456
Simon Kelleyc72daea2012-01-05 21:33:27 +0000457 if (if_index == param->ind)
458 {
459 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
460 if ( tmp->addr.sa.sa_family == AF_INET &&
461 tmp->addr.in.sin_addr.s_addr == local.s_addr)
462 {
463 param->matched = 1;
464 param->addr = local;
465 param->netmask = netmask;
466 param->broadcast = broadcast;
467 break;
468 }
469 }
470
471 return 1;
472}
473
Simon Kelley0a852542005-03-23 20:28:59 +0000474/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100475 of each interface (and any relay address) and does the following things:
476
477 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
478 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
479 3) Fills in local (this host) and router (this host or relay) addresses.
480 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
481
Simon Kelley0a852542005-03-23 20:28:59 +0000482 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100483
Simon Kelley3f2873d2013-05-14 11:28:47 +0100484static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100485 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000486{
487 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +0100488 struct dhcp_relay *relay;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100489 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100490
491 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100492
Simon Kelley0a852542005-03-23 20:28:59 +0000493 for (context = daemon->dhcp; context; context = context->next)
494 {
495 if (!(context->flags & CONTEXT_NETMASK) &&
496 (is_same_net(local, context->start, netmask) ||
497 is_same_net(local, context->end, netmask)))
498 {
499 if (context->netmask.s_addr != netmask.s_addr &&
500 !(is_same_net(local, context->start, netmask) &&
501 is_same_net(local, context->end, netmask)))
502 {
503 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
504 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100505 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100506 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000507 }
508 context->netmask = netmask;
509 }
510
Simon Kelley7de060b2011-08-26 17:24:52 +0100511 if (context->netmask.s_addr != 0 &&
512 is_same_net(local, context->start, context->netmask) &&
513 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000514 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100515 /* link it onto the current chain if we've not seen it before */
516 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000517 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100518 context->router = local;
519 context->local = local;
520 context->current = param->current;
521 param->current = context;
522 }
523
524 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000525 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100526 if (is_same_net(broadcast, context->start, context->netmask))
527 context->broadcast = broadcast;
528 else
Simon Kelley0a852542005-03-23 20:28:59 +0000529 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
530 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100531 }
Simon Kelley0a852542005-03-23 20:28:59 +0000532 }
533
Simon Kelleyff7eea22013-09-04 18:01:38 +0100534 for (relay = daemon->relay4; relay; relay = relay->next)
535 if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
536 (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
537 {
538 relay->current = param->relay;
539 param->relay = relay;
540 param->relay_local = local;
541 }
542
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100543 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000544}
545
Simon Kelley824af852008-02-12 20:43:05 +0000546struct dhcp_context *address_available(struct dhcp_context *context,
547 struct in_addr taddr,
548 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000549{
Simon Kelley36717ee2004-09-20 19:20:58 +0100550 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100551 possible ranges. Make sure that the address isn't in use
552 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000553
Simon Kelley36717ee2004-09-20 19:20:58 +0100554 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100555 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100556
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100557 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100558 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100559 return NULL;
560
561 for (tmp = context; tmp; tmp = tmp->current)
562 {
563 start = ntohl(tmp->start.s_addr);
564 end = ntohl(tmp->end.s_addr);
565
Simon Kelley7de060b2011-08-26 17:24:52 +0100566 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100567 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000568 addr <= end &&
569 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100570 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100571 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000572
Simon Kelley59353a62004-11-21 19:34:28 +0000573 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000574}
Simon Kelley59353a62004-11-21 19:34:28 +0000575
Simon Kelley824af852008-02-12 20:43:05 +0000576struct dhcp_context *narrow_context(struct dhcp_context *context,
577 struct in_addr taddr,
578 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000579{
Simon Kelleye17fb622006-01-14 20:33:46 +0000580 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000581 These are chained on ->current.
582 Here we have an address, and return the actual context correponding to that
583 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000584 any dhcp-range. In that case we return a static range if possible, or failing that,
585 any context on the correct subnet. (If there's more than one, this is a dodgy
586 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000587
Simon Kelleye17fb622006-01-14 20:33:46 +0000588 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000589
Simon Kelley824af852008-02-12 20:43:05 +0000590 if (!(tmp = address_available(context, taddr, netids)))
591 {
592 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100593 if (match_netid(tmp->filter, netids, 1) &&
594 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100595 (tmp->flags & CONTEXT_STATIC))
596 break;
Simon Kelley824af852008-02-12 20:43:05 +0000597
598 if (!tmp)
599 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100600 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100601 is_same_net(taddr, tmp->start, tmp->netmask) &&
602 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000603 break;
604 }
Simon Kelley59353a62004-11-21 19:34:28 +0000605
Simon Kelley824af852008-02-12 20:43:05 +0000606 /* Only one context allowed now */
607 if (tmp)
608 tmp->current = NULL;
609
610 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000611}
612
Simon Kelleydfa666f2004-08-02 18:27:27 +0100613struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
614{
615 struct dhcp_config *config;
616
617 for (config = configs; config; config = config->next)
618 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
619 return config;
620
621 return NULL;
622}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000623
Simon Kelley5aabfc72007-08-29 11:24:47 +0100624int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000625 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100626 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000627{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100628 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000629 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000630 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000631
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100632 struct in_addr start, addr;
633 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000634 int i, pass;
635 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100636
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100637 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
638 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100639 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100640 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100641
Simon Kelleycdeda282006-03-16 20:16:06 +0000642 for (pass = 0; pass <= 1; pass++)
643 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100644 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000645 continue;
646 else if (!match_netid(c->filter, netids, pass))
647 continue;
648 else
649 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100650 if (option_bool(OPT_CONSEC_ADDR))
651 /* seed is largest extant lease addr in this context */
652 start = lease_find_max_addr(c);
653 else
654 /* pick a seed based on hwaddr */
655 start.s_addr = htonl(ntohl(c->start.s_addr) +
656 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
657
658 /* iterate until we find a free address. */
659 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000660
661 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100662 /* eliminate addresses in use by the server. */
663 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100664 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100665 break;
666
Simon Kelley73a08a22009-02-05 20:28:08 +0000667 /* Addresses which end in .255 and .0 are broken in Windows even when using
668 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
669 then 192.168.0.255 is a valid IP address, but not for Windows as it's
670 in the class C range. See KB281579. We therefore don't allocate these
671 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100672 if (!d &&
673 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000674 !config_find_by_address(daemon->dhcp_conf, addr) &&
675 (!IN_CLASSC(ntohl(addr.s_addr)) ||
676 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000677 {
678 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100679 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
680 ((float)PING_WAIT)));
681
682 *addrp = addr;
683
Simon Kelleycdeda282006-03-16 20:16:06 +0000684 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100685 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000686 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100687 on us repeatedly. As a final check, if we did more
688 than 60% of the possible ping checks in the last
689 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000690 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100691 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100693 else
694 {
695 count++;
696 if (r->addr.s_addr == addr.s_addr)
697 {
698 /* consec-ip mode: we offered this address for another client
699 (different hash) recently, don't offer it to this one. */
700 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
701 break;
702
703 return 1;
704 }
705 }
706
707 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100708 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100709 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000710 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100711 /* address in use: perturb address selection so that we are
712 less likely to try this address again. */
713 if (!option_bool(OPT_CONSEC_ADDR))
714 c->addr_epoch++;
715 }
716 else
717 {
718 /* at this point victim may hold an expired record */
719 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000720 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100721 if ((victim = whine_malloc(sizeof(struct ping_result))))
722 {
723 victim->next = daemon->ping_results;
724 daemon->ping_results = victim;
725 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000726 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100727
728 /* record that this address is OK for 30s
729 without more ping checks */
730 if (victim)
731 {
732 victim->addr = addr;
733 victim->time = now;
734 victim->hash = j;
735 }
736 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000737 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100738 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000739 }
Simon Kelley3be34542004-09-11 19:12:13 +0100740
Simon Kelleycdeda282006-03-16 20:16:06 +0000741 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
742
743 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
744 addr = c->start;
745
746 } while (addr.s_addr != start.s_addr);
747 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100748
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000749 return 0;
750}
751
752static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
753{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000754 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000755 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100756 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000757 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000758 for (; context; context = context->current)
759 if (is_same_net(config->addr, context->start, context->netmask))
760 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000761
762 return 0;
763}
764
Simon Kelley9009d742008-11-14 20:04:27 +0000765int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
766{
767 struct hwaddr_config *conf_addr;
768
769 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
770 if (conf_addr->wildcard_mask == 0 &&
771 conf_addr->hwaddr_len == len &&
772 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
773 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
774 return 1;
775
776 return 0;
777}
Simon Kelley0a852542005-03-23 20:28:59 +0000778
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000779struct dhcp_config *find_config(struct dhcp_config *configs,
780 struct dhcp_context *context,
781 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000782 unsigned char *hwaddr, int hw_len,
783 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000784{
Simon Kelley7622fc02009-06-04 20:32:05 +0100785 int count, new;
786 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000787 struct hwaddr_config *conf_addr;
788
Simon Kelley0a852542005-03-23 20:28:59 +0000789 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000790 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100791 if (config->flags & CONFIG_CLID)
792 {
793 if (config->clid_len == clid_len &&
794 memcmp(config->clid, clid, clid_len) == 0 &&
795 is_addr_in_context(context, config))
796 return config;
797
798 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
799 cope with that here */
800 if (*clid == 0 && config->clid_len == clid_len-1 &&
801 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
802 is_addr_in_context(context, config))
803 return config;
804 }
805
Simon Kelley0a852542005-03-23 20:28:59 +0000806
Simon Kelleycdeda282006-03-16 20:16:06 +0000807 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000808 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000809 is_addr_in_context(context, config))
810 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100811
Simon Kelley0a852542005-03-23 20:28:59 +0000812 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000813 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100814 if ((config->flags & CONFIG_NAME) &&
815 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000816 is_addr_in_context(context, config))
817 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100818
Simon Kelley4cb1b322012-02-06 14:30:41 +0000819 /* use match with fewest wildcard octets */
Simon Kelley7622fc02009-06-04 20:32:05 +0100820 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
821 if (is_addr_in_context(context, config))
822 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
823 if (conf_addr->wildcard_mask != 0 &&
824 conf_addr->hwaddr_len == hw_len &&
825 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
826 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
827 {
828 count = new;
829 candidate = config;
830 }
831
832 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000833}
834
Simon Kelley5aabfc72007-08-29 11:24:47 +0100835void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000836{
Simon Kelley44a2a312004-03-10 20:04:35 +0000837 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000838 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100839 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100840 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000841 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100842 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100843 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100844 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000845 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100846
847 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000848
849 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100850 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100851 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100852 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100853 }
854
Simon Kelley849a8352006-06-09 21:02:31 +0100855 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100856 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100857 {
858 tmp = config->next;
859 if (config->flags & CONFIG_FROM_ETHERS)
860 {
861 *up = tmp;
862 /* cannot have a clid */
863 if (config->flags & CONFIG_NAME)
864 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000865 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100866 free(config);
867 }
868 else
869 up = &config->next;
870 }
871
Simon Kelley44a2a312004-03-10 20:04:35 +0000872 while (fgets(buff, MAXDNAME, f))
873 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100874 char *host = NULL;
875
Simon Kelleyb8187c82005-11-26 21:46:27 +0000876 lineno++;
877
Simon Kelley824af852008-02-12 20:43:05 +0000878 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000879 buff[strlen(buff)-1] = 0;
880
Simon Kelley73a08a22009-02-05 20:28:08 +0000881 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000882 continue;
883
Simon Kelley824af852008-02-12 20:43:05 +0000884 for (ip = buff; *ip && !isspace((int)*ip); ip++);
885 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000886 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000887 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000888 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100889 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000890 continue;
891 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000892
893 /* check for name or dotted-quad */
894 for (cp = ip; *cp; cp++)
895 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
896 break;
897
898 if (!*cp)
899 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000900 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000901 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100902 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000903 continue;
904 }
905
Simon Kelley33820b72004-04-03 21:10:00 +0100906 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000907
Simon Kelley16972692006-10-16 20:04:18 +0100908 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100909 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000910 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000911 }
912 else
913 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100914 int nomem;
915 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000916 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100917 if (!nomem)
918 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
919 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000920 continue;
921 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100922
Simon Kelley33820b72004-04-03 21:10:00 +0100923 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000924
Simon Kelley16972692006-10-16 20:04:18 +0100925 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100926 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000927 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000928 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100929
930 if (config && (config->flags & CONFIG_FROM_ETHERS))
931 {
932 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
933 continue;
934 }
935
Simon Kelley1cff1662004-03-12 08:12:58 +0000936 if (!config)
937 {
Simon Kelley16972692006-10-16 20:04:18 +0100938 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000939 {
940 struct hwaddr_config *conf_addr = config->hwaddr;
941 if (conf_addr &&
942 conf_addr->next == NULL &&
943 conf_addr->wildcard_mask == 0 &&
944 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
945 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
946 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
947 break;
948 }
Simon Kelley33820b72004-04-03 21:10:00 +0100949
950 if (!config)
951 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100952 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100953 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100954 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000955 config->hwaddr = NULL;
956 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100957 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100958 config->next = daemon->dhcp_conf;
959 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100960 }
961
962 config->flags |= flags;
963
964 if (flags & CONFIG_NAME)
965 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100966 config->hostname = host;
967 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100968 }
969
970 if (flags & CONFIG_ADDR)
971 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000972 }
Simon Kelley33820b72004-04-03 21:10:00 +0100973
Simon Kelley9009d742008-11-14 20:04:27 +0000974 config->flags |= CONFIG_NOCLID;
975 if (!config->hwaddr)
976 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
977 if (config->hwaddr)
978 {
979 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
980 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
981 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
982 config->hwaddr->wildcard_mask = 0;
983 config->hwaddr->next = NULL;
984 }
Simon Kelley33820b72004-04-03 21:10:00 +0100985 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100986
987 free(host);
988
Simon Kelley44a2a312004-03-10 20:04:35 +0000989 }
990
991 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100992
Simon Kelley7622fc02009-06-04 20:32:05 +0100993 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000994}
995
Simon Kelley44a2a312004-03-10 20:04:35 +0000996
Simon Kelleybb01cb92004-12-13 20:56:23 +0000997/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
998 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100999 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001000 so check here that the domain name is legal as a hostname.
1001 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001002char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +00001003{
Simon Kelley824af852008-02-12 20:43:05 +00001004 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +00001005
1006 if (daemon->port == 0)
1007 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +00001008
Simon Kelley824af852008-02-12 20:43:05 +00001009 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001010
Simon Kelleybb01cb92004-12-13 20:56:23 +00001011 if (lookup && (lookup->flags & F_HOSTS))
1012 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001013 char *dot, *hostname = cache_get_name(lookup);
1014 dot = strchr(hostname, '.');
1015
1016 if (dot && strlen(dot+1) != 0)
1017 {
1018 char *d2 = get_domain(addr);
1019 if (!d2 || !hostname_isequal(dot+1, d2))
1020 return NULL; /* wrong domain */
1021 }
1022
1023 if (!legal_hostname(hostname))
1024 return NULL;
1025
1026 strncpy(daemon->dhcp_buff, hostname, 256);
1027 daemon->dhcp_buff[255] = 0;
1028 strip_hostname(daemon->dhcp_buff);
1029
1030 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001031 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001032
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001033 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001034}
1035
Simon Kelleyff7eea22013-09-04 18:01:38 +01001036static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
1037{
1038 /* ->local is same value for all relays on ->current chain */
1039 struct all_addr from;
1040
1041 if (mess->op != BOOTREQUEST)
1042 return 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001043
Simon Kelleyff7eea22013-09-04 18:01:38 +01001044 /* source address == relay address */
1045 from.addr.addr4 = relay->local.addr.addr4;
1046
1047 /* already gatewayed ? */
1048 if (mess->giaddr.s_addr)
1049 {
1050 /* if so check if by us, to stomp on loops. */
1051 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1052 return 1;
1053 }
1054 else
1055 {
1056 /* plug in our address */
1057 mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
1058 }
1059
1060 if ((mess->hops++) > 20)
1061 return 1;
1062
1063 for (; relay; relay = relay->current)
1064 {
1065 union mysockaddr to;
1066
1067 to.sa.sa_family = AF_INET;
1068 to.in.sin_addr = relay->server.addr.addr4;
1069 to.in.sin_port = htons(daemon->dhcp_server_port);
1070
1071 send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1072
1073 if (option_bool(OPT_LOG_OPTS))
1074 {
1075 inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1076 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
1077 }
1078
1079 /* Save this for replies */
1080 relay->iface_index = iface_index;
1081 }
1082
1083 return 1;
1084}
1085
1086
1087static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1088{
1089 struct dhcp_relay *relay;
1090
1091 if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1092 return NULL;
1093
1094 for (relay = daemon->relay4; relay; relay = relay->next)
1095 {
1096 if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
1097 {
1098 if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1099 return relay->iface_index != 0 ? relay : NULL;
1100 }
1101 }
1102
1103 return NULL;
1104}
1105
1106#endif