blob: b95a4bab8760f97a163d7d67cffc4a1e7c657a83 [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;
23 int ind;
24};
25
Simon Kelleyc72daea2012-01-05 21:33:27 +000026struct match_param {
27 int ind, matched;
28 struct in_addr netmask, broadcast, addr;
29};
30
Simon Kelley3f2873d2013-05-14 11:28:47 +010031static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010032 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelley3f2873d2013-05-14 11:28:47 +010033static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +000034 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010035
Simon Kelley316e2732010-01-22 20:16:09 +000036static int make_fd(int port)
Simon Kelley44a2a312004-03-10 20:04:35 +000037{
38 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
39 struct sockaddr_in saddr;
Simon Kelley7cebd202006-05-06 14:13:33 +010040 int oneopt = 1;
Simon Kelley824af852008-02-12 20:43:05 +000041#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
42 int mtu = IP_PMTUDISC_DONT;
43#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +000044#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
45 int tos = IPTOS_CLASS_CS6;
46#endif
Simon Kelleydfa666f2004-08-02 18:27:27 +010047
Simon Kelley44a2a312004-03-10 20:04:35 +000048 if (fd == -1)
Simon Kelley7622fc02009-06-04 20:32:05 +010049 die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000050
Simon Kelley824af852008-02-12 20:43:05 +000051 if (!fix_fd(fd) ||
52#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
Simon Kelleyc72daea2012-01-05 21:33:27 +000053 setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
54#endif
55#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
56 setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
Simon Kelley824af852008-02-12 20:43:05 +000057#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010058#if defined(HAVE_LINUX_NETWORK)
Simon Kelleyc72daea2012-01-05 21:33:27 +000059 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley7622fc02009-06-04 20:32:05 +010060#else
Simon Kelley3be34542004-09-11 19:12:13 +010061 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
Simon Kelley44a2a312004-03-10 20:04:35 +000062#endif
Simon Kelley3be34542004-09-11 19:12:13 +010063 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010064 die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000065
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000066 /* When bind-interfaces is set, there might be more than one dnmsasq
Simon Kelley4011c4e2006-10-28 16:26:19 +010067 instance binding port 67. That's OK if they serve different networks.
Simon Kelley56a11422013-04-02 17:02:58 +010068 Need to set REUSEADDR|REUSEPORT to make this posible.
69 Handle the case that REUSEPORT is defined, but the kernel doesn't
70 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley54dd3932012-06-20 11:23:38 +010071 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley4011c4e2006-10-28 16:26:19 +010072 {
Simon Kelleyffbad342013-08-14 15:53:57 +010073 int rc = 0;
Simon Kelley56a11422013-04-02 17:02:58 +010074
Simon Kelley4011c4e2006-10-28 16:26:19 +010075#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010076 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
Simon Kelleyffbad342013-08-14 15:53:57 +010077 errno == ENOPROTOOPT)
78 rc = 0;
Simon Kelley4011c4e2006-10-28 16:26:19 +010079#endif
Simon Kelley56a11422013-04-02 17:02:58 +010080
Simon Kelleyffbad342013-08-14 15:53:57 +010081 if (rc != -1)
Simon Kelley56a11422013-04-02 17:02:58 +010082 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
83
Simon Kelley4011c4e2006-10-28 16:26:19 +010084 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010085 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010086 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000087
Simon Kelley849a8352006-06-09 21:02:31 +010088 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000089 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000090 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000091 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010092#ifdef HAVE_SOCKADDR_SA_LEN
93 saddr.sin_len = sizeof(struct sockaddr_in);
94#endif
95
Simon Kelley44a2a312004-03-10 20:04:35 +000096 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010097 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000098
Simon Kelley316e2732010-01-22 20:16:09 +000099 return fd;
100}
101
102void dhcp_init(void)
103{
104#if defined(HAVE_BSD_NETWORK)
105 int oneopt = 1;
106#endif
107
108 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
109 if (daemon->enable_pxe)
110 daemon->pxefd = make_fd(PXE_PORT);
111 else
112 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100113
Simon Kelley824af852008-02-12 20:43:05 +0000114#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 /* When we're not using capabilities, we need to do this here before
116 we drop root. Also, set buffer size small, to avoid wasting
117 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000118
Simon Kelley28866e92011-02-14 20:19:14 +0000119 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100120 daemon->dhcp_icmp_fd = -1;
121 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
122 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100123 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100124
125 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100126 init_bpf();
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100127#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000128}
129
Simon Kelley316e2732010-01-22 20:16:09 +0000130void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000131{
Simon Kelley316e2732010-01-22 20:16:09 +0000132 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100133 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000134 struct dhcp_context *context;
135 struct iname *tmp;
136 struct ifreq ifr;
137 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100138 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000139 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100140 struct iovec iov;
141 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100142 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000143 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 struct iface_param parm;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100145#ifdef HAVE_LINUX_NETWORK
146 struct arpreq arp_req;
147#endif
148
Simon Kelley44a2a312004-03-10 20:04:35 +0000149 union {
150 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000151#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000152 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000153#elif defined(HAVE_SOLARIS_NETWORK)
154 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100155#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000156 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
157#endif
158 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000159 struct dhcp_bridge *bridge, *alias;
160
161 msg.msg_controllen = sizeof(control_u);
162 msg.msg_control = control_u.control;
163 msg.msg_name = &dest;
164 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100165 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000166 msg.msg_iovlen = 1;
167
Simon Kelleyc72daea2012-01-05 21:33:27 +0000168 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
169 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000170 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000171
172 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100173 if (msg.msg_controllen >= sizeof(struct cmsghdr))
174 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100176 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100177 union {
178 unsigned char *c;
179 struct in_pktinfo *p;
180 } p;
181 p.c = CMSG_DATA(cmptr);
182 iface_index = p.p->ipi_ifindex;
183 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100184 unicast_dest = 1;
185 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100186
187#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100188 if (msg.msg_controllen >= sizeof(struct cmsghdr))
189 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
190 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100191 {
192 union {
193 unsigned char *c;
194 struct sockaddr_dl *s;
195 } p;
196 p.c = CMSG_DATA(cmptr);
197 iface_index = p.s->sdl_index;
198 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100199
Simon Kelley7622fc02009-06-04 20:32:05 +0100200#elif defined(HAVE_SOLARIS_NETWORK)
201 if (msg.msg_controllen >= sizeof(struct cmsghdr))
202 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
203 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100204 {
205 union {
206 unsigned char *c;
207 unsigned int *i;
208 } p;
209 p.c = CMSG_DATA(cmptr);
210 iface_index = *(p.i);
211 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100212#endif
213
214 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
215 return;
216
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100217#ifdef HAVE_LINUX_NETWORK
218 /* ARP fiddling uses original interface even if we pretend to use a different one. */
219 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
220#endif
221
Simon Kelleyc72daea2012-01-05 21:33:27 +0000222 /* One form of bridging on BSD has the property that packets
223 can be recieved on bridge interfaces which do not have an IP address.
224 We allow these to be treated as aliases of another interface which does have
225 an IP address with --dhcp-bridge=interface,alias,alias */
226 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
227 {
228 for (alias = bridge->alias; alias; alias = alias->next)
229 if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
230 {
231 if (!(iface_index = if_nametoindex(bridge->iface)))
232 {
233 my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
234 return;
235 }
236 else
237 {
238 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
239 break;
240 }
241 }
242
243 if (alias)
244 break;
245 }
246
Simon Kelley4011c4e2006-10-28 16:26:19 +0100247#ifdef MSG_BCAST
248 /* OpenBSD tells us when a packet was broadcast */
249 if (!(msg.msg_flags & MSG_BCAST))
250 unicast_dest = 1;
251#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000252
Simon Kelley44a2a312004-03-10 20:04:35 +0000253 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000254 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
Simon Kelleyc72daea2012-01-05 21:33:27 +0000255 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
256 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000257 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000258 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
259 return;
Simon Kelley832af0b2007-01-21 20:01:28 +0000260 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000261
Simon Kelley3d8df262005-08-29 12:19:27 +0100262 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000263 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelley3d8df262005-08-29 12:19:27 +0100264 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000265
Simon Kelley91dccd02005-03-31 17:48:32 +0100266 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100267 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100268 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000269
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100270 parm.current = NULL;
271 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000272
Simon Kelley4f7b3042012-11-28 21:27:02 +0000273 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100274 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000275 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
276 for a secondary */
277 struct match_param match;
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100278
Simon Kelleyc72daea2012-01-05 21:33:27 +0000279 match.matched = 0;
280 match.ind = iface_index;
281
282 if (!daemon->if_addrs ||
283 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
284 !match.matched)
285 return;
286
287 iface_addr = match.addr;
288 /* make sure secondary address gets priority in case
289 there is more than one address on the interface in the same subnet */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100290 complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000291 }
292
Simon Kelley28866e92011-02-14 20:19:14 +0000293 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100294 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000295
Simon Kelley44a2a312004-03-10 20:04:35 +0000296 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000297 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley7de060b2011-08-26 17:24:52 +0100298 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100299 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000300 lease_update_dns(0);
Simon Kelley16972692006-10-16 20:04:18 +0100301
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100302 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000303 return;
304
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100305 msg.msg_name = &dest;
306 msg.msg_namelen = sizeof(dest);
307 msg.msg_control = NULL;
308 msg.msg_controllen = 0;
309 msg.msg_iov = &iov;
310 iov.iov_base = daemon->dhcp_packet.iov_base;
311
312 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000313 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100314
Simon Kelley3be34542004-09-11 19:12:13 +0100315#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100316 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100317#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000318
Simon Kelley316e2732010-01-22 20:16:09 +0000319 if (pxe_fd)
320 {
321 if (mess->ciaddr.s_addr != 0)
322 dest.sin_addr = mess->ciaddr;
323 }
324 else if (mess->giaddr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100325 {
326 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100327 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100328 dest.sin_addr = mess->giaddr;
329 }
330 else if (mess->ciaddr.s_addr)
331 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100332 /* If the client's idea of its own address tallys with
333 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100334 source port too, and send back to that. If we're replying
335 to a DHCPINFORM, trust the source address always. */
336 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
337 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100338 {
Simon Kelley9e038942008-05-30 20:06:34 +0100339 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100340 dest.sin_addr = mess->ciaddr;
341 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342 }
Simon Kelley824af852008-02-12 20:43:05 +0000343#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100344 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
345 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100346 {
Simon Kelley849a8352006-06-09 21:02:31 +0100347 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100348 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100349 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100350 msg.msg_controllen = sizeof(control_u);
351 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100352 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
353 pkt->ipi_ifindex = iface_index;
354 pkt->ipi_spec_dst.s_addr = 0;
355 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000356 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000357 cmptr->cmsg_type = IP_PKTINFO;
358 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100359 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000360 }
361 else
362 {
Simon Kelley849a8352006-06-09 21:02:31 +0100363 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
364 struct sockaddr limits size to 14 bytes. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100365 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100366 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100367 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
368 arp_req.arp_ha.sa_family = mess->htype;
369 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
370 /* interface name already copied in */
371 arp_req.arp_flags = ATF_COM;
372 ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000373 }
Simon Kelley824af852008-02-12 20:43:05 +0000374#elif defined(HAVE_SOLARIS_NETWORK)
375 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
376 {
377 /* broadcast to 255.255.255.255 (or mac address invalid) */
378 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100379 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000380 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100381 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000382 }
383 else
384 {
385 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
386 Note that this only works for ethernet on solaris, because we use SIOCSARP
387 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
388 mysteriously. Bah. Fall back to broadcast for other net types. */
389 struct arpreq req;
390 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100391 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000392 *((struct sockaddr_in *)&req.arp_pa) = dest;
393 req.arp_ha.sa_family = AF_UNSPEC;
394 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
395 req.arp_flags = ATF_COM;
396 ioctl(daemon->dhcpfd, SIOCSARP, &req);
397 }
398#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 else
400 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100401 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100402 return;
403 }
404#endif
405
Simon Kelley824af852008-02-12 20:43:05 +0000406#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000407 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000408#endif
409
Simon Kelley316e2732010-01-22 20:16:09 +0000410 while(sendmsg(fd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000411}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100412
Simon Kelleyc72daea2012-01-05 21:33:27 +0000413/* check against secondary interface addresses */
Simon Kelley3f2873d2013-05-14 11:28:47 +0100414static int check_listen_addrs(struct in_addr local, int if_index, char *label,
Simon Kelleyc72daea2012-01-05 21:33:27 +0000415 struct in_addr netmask, struct in_addr broadcast, void *vparam)
416{
417 struct match_param *param = vparam;
418 struct iname *tmp;
419
Simon Kelley3f2873d2013-05-14 11:28:47 +0100420 (void) label;
421
Simon Kelleyc72daea2012-01-05 21:33:27 +0000422 if (if_index == param->ind)
423 {
424 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
425 if ( tmp->addr.sa.sa_family == AF_INET &&
426 tmp->addr.in.sin_addr.s_addr == local.s_addr)
427 {
428 param->matched = 1;
429 param->addr = local;
430 param->netmask = netmask;
431 param->broadcast = broadcast;
432 break;
433 }
434 }
435
436 return 1;
437}
438
Simon Kelley0a852542005-03-23 20:28:59 +0000439/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100440 of each interface (and any relay address) and does the following things:
441
442 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
443 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
444 3) Fills in local (this host) and router (this host or relay) addresses.
445 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
446
Simon Kelley0a852542005-03-23 20:28:59 +0000447 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100448
Simon Kelley3f2873d2013-05-14 11:28:47 +0100449static int complete_context(struct in_addr local, int if_index, char *label,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100450 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000451{
452 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100453 struct iface_param *param = vparam;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100454
455 (void)label;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100456
Simon Kelley0a852542005-03-23 20:28:59 +0000457 for (context = daemon->dhcp; context; context = context->next)
458 {
459 if (!(context->flags & CONTEXT_NETMASK) &&
460 (is_same_net(local, context->start, netmask) ||
461 is_same_net(local, context->end, netmask)))
462 {
463 if (context->netmask.s_addr != netmask.s_addr &&
464 !(is_same_net(local, context->start, netmask) &&
465 is_same_net(local, context->end, netmask)))
466 {
467 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
468 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100469 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100470 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000471 }
472 context->netmask = netmask;
473 }
474
Simon Kelley7de060b2011-08-26 17:24:52 +0100475 if (context->netmask.s_addr != 0 &&
476 is_same_net(local, context->start, context->netmask) &&
477 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000478 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100479 /* link it onto the current chain if we've not seen it before */
480 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000481 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100482 context->router = local;
483 context->local = local;
484 context->current = param->current;
485 param->current = context;
486 }
487
488 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000489 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100490 if (is_same_net(broadcast, context->start, context->netmask))
491 context->broadcast = broadcast;
492 else
Simon Kelley0a852542005-03-23 20:28:59 +0000493 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
494 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100495 }
Simon Kelley0a852542005-03-23 20:28:59 +0000496 }
497
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100498 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000499}
500
Simon Kelley824af852008-02-12 20:43:05 +0000501struct dhcp_context *address_available(struct dhcp_context *context,
502 struct in_addr taddr,
503 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000504{
Simon Kelley36717ee2004-09-20 19:20:58 +0100505 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100506 possible ranges. Make sure that the address isn't in use
507 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000508
Simon Kelley36717ee2004-09-20 19:20:58 +0100509 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100510 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100511
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100512 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100513 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100514 return NULL;
515
516 for (tmp = context; tmp; tmp = tmp->current)
517 {
518 start = ntohl(tmp->start.s_addr);
519 end = ntohl(tmp->end.s_addr);
520
Simon Kelley7de060b2011-08-26 17:24:52 +0100521 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100522 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000523 addr <= end &&
524 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100525 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100526 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000527
Simon Kelley59353a62004-11-21 19:34:28 +0000528 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000529}
Simon Kelley59353a62004-11-21 19:34:28 +0000530
Simon Kelley824af852008-02-12 20:43:05 +0000531struct dhcp_context *narrow_context(struct dhcp_context *context,
532 struct in_addr taddr,
533 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000534{
Simon Kelleye17fb622006-01-14 20:33:46 +0000535 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000536 These are chained on ->current.
537 Here we have an address, and return the actual context correponding to that
538 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000539 any dhcp-range. In that case we return a static range if possible, or failing that,
540 any context on the correct subnet. (If there's more than one, this is a dodgy
541 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000542
Simon Kelleye17fb622006-01-14 20:33:46 +0000543 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000544
Simon Kelley824af852008-02-12 20:43:05 +0000545 if (!(tmp = address_available(context, taddr, netids)))
546 {
547 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100548 if (match_netid(tmp->filter, netids, 1) &&
549 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100550 (tmp->flags & CONTEXT_STATIC))
551 break;
Simon Kelley824af852008-02-12 20:43:05 +0000552
553 if (!tmp)
554 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100555 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100556 is_same_net(taddr, tmp->start, tmp->netmask) &&
557 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000558 break;
559 }
Simon Kelley59353a62004-11-21 19:34:28 +0000560
Simon Kelley824af852008-02-12 20:43:05 +0000561 /* Only one context allowed now */
562 if (tmp)
563 tmp->current = NULL;
564
565 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000566}
567
Simon Kelleydfa666f2004-08-02 18:27:27 +0100568struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
569{
570 struct dhcp_config *config;
571
572 for (config = configs; config; config = config->next)
573 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
574 return config;
575
576 return NULL;
577}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000578
Simon Kelley5aabfc72007-08-29 11:24:47 +0100579int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000580 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100581 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000582{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100583 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000584 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000585 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000586
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100587 struct in_addr start, addr;
588 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000589 int i, pass;
590 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100591
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100592 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
593 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100594 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100595 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100596
Simon Kelleycdeda282006-03-16 20:16:06 +0000597 for (pass = 0; pass <= 1; pass++)
598 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100599 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000600 continue;
601 else if (!match_netid(c->filter, netids, pass))
602 continue;
603 else
604 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100605 if (option_bool(OPT_CONSEC_ADDR))
606 /* seed is largest extant lease addr in this context */
607 start = lease_find_max_addr(c);
608 else
609 /* pick a seed based on hwaddr */
610 start.s_addr = htonl(ntohl(c->start.s_addr) +
611 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
612
613 /* iterate until we find a free address. */
614 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000615
616 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100617 /* eliminate addresses in use by the server. */
618 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100619 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100620 break;
621
Simon Kelley73a08a22009-02-05 20:28:08 +0000622 /* Addresses which end in .255 and .0 are broken in Windows even when using
623 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
624 then 192.168.0.255 is a valid IP address, but not for Windows as it's
625 in the class C range. See KB281579. We therefore don't allocate these
626 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100627 if (!d &&
628 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000629 !config_find_by_address(daemon->dhcp_conf, addr) &&
630 (!IN_CLASSC(ntohl(addr.s_addr)) ||
631 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000632 {
633 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100634 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
635 ((float)PING_WAIT)));
636
637 *addrp = addr;
638
Simon Kelleycdeda282006-03-16 20:16:06 +0000639 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100640 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000641 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100642 on us repeatedly. As a final check, if we did more
643 than 60% of the possible ping checks in the last
644 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000645 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100646 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000647 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100648 else
649 {
650 count++;
651 if (r->addr.s_addr == addr.s_addr)
652 {
653 /* consec-ip mode: we offered this address for another client
654 (different hash) recently, don't offer it to this one. */
655 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
656 break;
657
658 return 1;
659 }
660 }
661
662 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100663 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100664 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000665 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100666 /* address in use: perturb address selection so that we are
667 less likely to try this address again. */
668 if (!option_bool(OPT_CONSEC_ADDR))
669 c->addr_epoch++;
670 }
671 else
672 {
673 /* at this point victim may hold an expired record */
674 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000675 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100676 if ((victim = whine_malloc(sizeof(struct ping_result))))
677 {
678 victim->next = daemon->ping_results;
679 daemon->ping_results = victim;
680 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000681 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100682
683 /* record that this address is OK for 30s
684 without more ping checks */
685 if (victim)
686 {
687 victim->addr = addr;
688 victim->time = now;
689 victim->hash = j;
690 }
691 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100693 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000694 }
Simon Kelley3be34542004-09-11 19:12:13 +0100695
Simon Kelleycdeda282006-03-16 20:16:06 +0000696 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
697
698 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
699 addr = c->start;
700
701 } while (addr.s_addr != start.s_addr);
702 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100703
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000704 return 0;
705}
706
707static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
708{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000709 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000710 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100711 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000712 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000713 for (; context; context = context->current)
714 if (is_same_net(config->addr, context->start, context->netmask))
715 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716
717 return 0;
718}
719
Simon Kelley9009d742008-11-14 20:04:27 +0000720int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
721{
722 struct hwaddr_config *conf_addr;
723
724 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
725 if (conf_addr->wildcard_mask == 0 &&
726 conf_addr->hwaddr_len == len &&
727 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
728 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
729 return 1;
730
731 return 0;
732}
Simon Kelley0a852542005-03-23 20:28:59 +0000733
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000734struct dhcp_config *find_config(struct dhcp_config *configs,
735 struct dhcp_context *context,
736 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000737 unsigned char *hwaddr, int hw_len,
738 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000739{
Simon Kelley7622fc02009-06-04 20:32:05 +0100740 int count, new;
741 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000742 struct hwaddr_config *conf_addr;
743
Simon Kelley0a852542005-03-23 20:28:59 +0000744 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000745 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100746 if (config->flags & CONFIG_CLID)
747 {
748 if (config->clid_len == clid_len &&
749 memcmp(config->clid, clid, clid_len) == 0 &&
750 is_addr_in_context(context, config))
751 return config;
752
753 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
754 cope with that here */
755 if (*clid == 0 && config->clid_len == clid_len-1 &&
756 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
757 is_addr_in_context(context, config))
758 return config;
759 }
760
Simon Kelley0a852542005-03-23 20:28:59 +0000761
Simon Kelleycdeda282006-03-16 20:16:06 +0000762 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000763 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000764 is_addr_in_context(context, config))
765 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100766
Simon Kelley0a852542005-03-23 20:28:59 +0000767 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000768 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100769 if ((config->flags & CONFIG_NAME) &&
770 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000771 is_addr_in_context(context, config))
772 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100773
Simon Kelley4cb1b322012-02-06 14:30:41 +0000774 /* use match with fewest wildcard octets */
Simon Kelley7622fc02009-06-04 20:32:05 +0100775 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
776 if (is_addr_in_context(context, config))
777 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
778 if (conf_addr->wildcard_mask != 0 &&
779 conf_addr->hwaddr_len == hw_len &&
780 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
781 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
782 {
783 count = new;
784 candidate = config;
785 }
786
787 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000788}
789
Simon Kelley5aabfc72007-08-29 11:24:47 +0100790void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000791{
Simon Kelley44a2a312004-03-10 20:04:35 +0000792 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000793 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100794 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100795 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000796 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100797 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100798 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100799 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000800 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100801
802 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000803
804 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100805 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100806 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100807 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100808 }
809
Simon Kelley849a8352006-06-09 21:02:31 +0100810 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100811 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100812 {
813 tmp = config->next;
814 if (config->flags & CONFIG_FROM_ETHERS)
815 {
816 *up = tmp;
817 /* cannot have a clid */
818 if (config->flags & CONFIG_NAME)
819 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000820 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100821 free(config);
822 }
823 else
824 up = &config->next;
825 }
826
Simon Kelley44a2a312004-03-10 20:04:35 +0000827 while (fgets(buff, MAXDNAME, f))
828 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100829 char *host = NULL;
830
Simon Kelleyb8187c82005-11-26 21:46:27 +0000831 lineno++;
832
Simon Kelley824af852008-02-12 20:43:05 +0000833 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000834 buff[strlen(buff)-1] = 0;
835
Simon Kelley73a08a22009-02-05 20:28:08 +0000836 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000837 continue;
838
Simon Kelley824af852008-02-12 20:43:05 +0000839 for (ip = buff; *ip && !isspace((int)*ip); ip++);
840 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000841 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000842 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000843 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100844 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000845 continue;
846 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000847
848 /* check for name or dotted-quad */
849 for (cp = ip; *cp; cp++)
850 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
851 break;
852
853 if (!*cp)
854 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000855 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000856 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100857 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000858 continue;
859 }
860
Simon Kelley33820b72004-04-03 21:10:00 +0100861 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000862
Simon Kelley16972692006-10-16 20:04:18 +0100863 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100864 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000865 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000866 }
867 else
868 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100869 int nomem;
870 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000871 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100872 if (!nomem)
873 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
874 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000875 continue;
876 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100877
Simon Kelley33820b72004-04-03 21:10:00 +0100878 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000879
Simon Kelley16972692006-10-16 20:04:18 +0100880 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100881 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000882 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000883 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100884
885 if (config && (config->flags & CONFIG_FROM_ETHERS))
886 {
887 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
888 continue;
889 }
890
Simon Kelley1cff1662004-03-12 08:12:58 +0000891 if (!config)
892 {
Simon Kelley16972692006-10-16 20:04:18 +0100893 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000894 {
895 struct hwaddr_config *conf_addr = config->hwaddr;
896 if (conf_addr &&
897 conf_addr->next == NULL &&
898 conf_addr->wildcard_mask == 0 &&
899 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
900 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
901 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
902 break;
903 }
Simon Kelley33820b72004-04-03 21:10:00 +0100904
905 if (!config)
906 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100907 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100908 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100909 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000910 config->hwaddr = NULL;
911 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100912 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100913 config->next = daemon->dhcp_conf;
914 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100915 }
916
917 config->flags |= flags;
918
919 if (flags & CONFIG_NAME)
920 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100921 config->hostname = host;
922 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100923 }
924
925 if (flags & CONFIG_ADDR)
926 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000927 }
Simon Kelley33820b72004-04-03 21:10:00 +0100928
Simon Kelley9009d742008-11-14 20:04:27 +0000929 config->flags |= CONFIG_NOCLID;
930 if (!config->hwaddr)
931 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
932 if (config->hwaddr)
933 {
934 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
935 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
936 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
937 config->hwaddr->wildcard_mask = 0;
938 config->hwaddr->next = NULL;
939 }
Simon Kelley33820b72004-04-03 21:10:00 +0100940 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100941
942 free(host);
943
Simon Kelley44a2a312004-03-10 20:04:35 +0000944 }
945
946 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100947
Simon Kelley7622fc02009-06-04 20:32:05 +0100948 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000949}
950
Simon Kelley44a2a312004-03-10 20:04:35 +0000951
Simon Kelleybb01cb92004-12-13 20:56:23 +0000952/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
953 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100954 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100955 so check here that the domain name is legal as a hostname.
956 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100957char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000958{
Simon Kelley824af852008-02-12 20:43:05 +0000959 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000960
961 if (daemon->port == 0)
962 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000963
Simon Kelley824af852008-02-12 20:43:05 +0000964 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100965
Simon Kelleybb01cb92004-12-13 20:56:23 +0000966 if (lookup && (lookup->flags & F_HOSTS))
967 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100968 char *dot, *hostname = cache_get_name(lookup);
969 dot = strchr(hostname, '.');
970
971 if (dot && strlen(dot+1) != 0)
972 {
973 char *d2 = get_domain(addr);
974 if (!d2 || !hostname_isequal(dot+1, d2))
975 return NULL; /* wrong domain */
976 }
977
978 if (!legal_hostname(hostname))
979 return NULL;
980
981 strncpy(daemon->dhcp_buff, hostname, 256);
982 daemon->dhcp_buff[255] = 0;
983 strip_hostname(daemon->dhcp_buff);
984
985 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000986 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100987
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100988 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000989}
990
Simon Kelley7622fc02009-06-04 20:32:05 +0100991#endif
Simon Kelley9009d742008-11-14 20:04:27 +0000992