blob: b95752c2c009455ef75501c3c5e0c6daffef7c96 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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 Kelley5aabfc72007-08-29 11:24:47 +010031static int complete_context(struct in_addr local, int if_index,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010032 struct in_addr netmask, struct in_addr broadcast, void *vparam);
Simon Kelleyc72daea2012-01-05 21:33:27 +000033static int check_listen_addrs(struct in_addr local, int if_index,
34 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 Kelley7622fc02009-06-04 20:32:05 +010068 Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
Simon Kelley28866e92011-02-14 20:19:14 +000069 if (option_bool(OPT_NOWILD))
Simon Kelley4011c4e2006-10-28 16:26:19 +010070 {
71#ifdef SO_REUSEPORT
72 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
73#else
74 int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
75#endif
76 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010077 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010078 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000079
Simon Kelley849a8352006-06-09 21:02:31 +010080 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000081 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000082 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000083 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010084#ifdef HAVE_SOCKADDR_SA_LEN
85 saddr.sin_len = sizeof(struct sockaddr_in);
86#endif
87
Simon Kelley44a2a312004-03-10 20:04:35 +000088 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010089 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000090
Simon Kelley316e2732010-01-22 20:16:09 +000091 return fd;
92}
93
94void dhcp_init(void)
95{
96#if defined(HAVE_BSD_NETWORK)
97 int oneopt = 1;
98#endif
99
100 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
101 if (daemon->enable_pxe)
102 daemon->pxefd = make_fd(PXE_PORT);
103 else
104 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100105
Simon Kelley824af852008-02-12 20:43:05 +0000106#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100107 /* When we're not using capabilities, we need to do this here before
108 we drop root. Also, set buffer size small, to avoid wasting
109 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000110
Simon Kelley28866e92011-02-14 20:19:14 +0000111 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100112 daemon->dhcp_icmp_fd = -1;
113 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
114 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100115 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100116
117 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100118 init_bpf();
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100120
Simon Kelley824af852008-02-12 20:43:05 +0000121 check_dhcp_hosts(1);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000122}
123
Simon Kelley316e2732010-01-22 20:16:09 +0000124void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000125{
Simon Kelley316e2732010-01-22 20:16:09 +0000126 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100127 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000128 struct dhcp_context *context;
129 struct iname *tmp;
130 struct ifreq ifr;
131 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100132 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000133 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100134 struct iovec iov;
135 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100136 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000137 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100138 struct iface_param parm;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100139#ifdef HAVE_LINUX_NETWORK
140 struct arpreq arp_req;
141#endif
142
Simon Kelley44a2a312004-03-10 20:04:35 +0000143 union {
144 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000145#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000146 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000147#elif defined(HAVE_SOLARIS_NETWORK)
148 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100149#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000150 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
151#endif
152 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000153 struct dhcp_bridge *bridge, *alias;
154
155 msg.msg_controllen = sizeof(control_u);
156 msg.msg_control = control_u.control;
157 msg.msg_name = &dest;
158 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100159 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000160 msg.msg_iovlen = 1;
161
Simon Kelleyc72daea2012-01-05 21:33:27 +0000162 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
163 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000164 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000165
166 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100167 if (msg.msg_controllen >= sizeof(struct cmsghdr))
168 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000169 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100170 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100171 union {
172 unsigned char *c;
173 struct in_pktinfo *p;
174 } p;
175 p.c = CMSG_DATA(cmptr);
176 iface_index = p.p->ipi_ifindex;
177 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100178 unicast_dest = 1;
179 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100180
181#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100182 if (msg.msg_controllen >= sizeof(struct cmsghdr))
183 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
184 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100185 {
186 union {
187 unsigned char *c;
188 struct sockaddr_dl *s;
189 } p;
190 p.c = CMSG_DATA(cmptr);
191 iface_index = p.s->sdl_index;
192 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100193
Simon Kelley7622fc02009-06-04 20:32:05 +0100194#elif defined(HAVE_SOLARIS_NETWORK)
195 if (msg.msg_controllen >= sizeof(struct cmsghdr))
196 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
197 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100198 {
199 union {
200 unsigned char *c;
201 unsigned int *i;
202 } p;
203 p.c = CMSG_DATA(cmptr);
204 iface_index = *(p.i);
205 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100206#endif
207
208 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
209 return;
210
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100211#ifdef HAVE_LINUX_NETWORK
212 /* ARP fiddling uses original interface even if we pretend to use a different one. */
213 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
214#endif
215
Simon Kelleyc72daea2012-01-05 21:33:27 +0000216 /* One form of bridging on BSD has the property that packets
217 can be recieved on bridge interfaces which do not have an IP address.
218 We allow these to be treated as aliases of another interface which does have
219 an IP address with --dhcp-bridge=interface,alias,alias */
220 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
221 {
222 for (alias = bridge->alias; alias; alias = alias->next)
223 if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
224 {
225 if (!(iface_index = if_nametoindex(bridge->iface)))
226 {
227 my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
228 return;
229 }
230 else
231 {
232 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
233 break;
234 }
235 }
236
237 if (alias)
238 break;
239 }
240
Simon Kelley4011c4e2006-10-28 16:26:19 +0100241#ifdef MSG_BCAST
242 /* OpenBSD tells us when a packet was broadcast */
243 if (!(msg.msg_flags & MSG_BCAST))
244 unicast_dest = 1;
245#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000246
Simon Kelley44a2a312004-03-10 20:04:35 +0000247 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000248 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
Simon Kelleyc72daea2012-01-05 21:33:27 +0000249 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
250 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000251 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000252 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
253 return;
Simon Kelley832af0b2007-01-21 20:01:28 +0000254 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000255
Simon Kelley3d8df262005-08-29 12:19:27 +0100256 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
257 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
258 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000259
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100260 /* weird libvirt-inspired access control */
261 for (context = daemon->dhcp; context; context = context->next)
262 if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
263 break;
264
265 if (!context)
266 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000267
Simon Kelley91dccd02005-03-31 17:48:32 +0100268 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100269 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100270 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000271
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100272 parm.current = NULL;
273 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000274
Simon Kelleyc72daea2012-01-05 21:33:27 +0000275 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100276 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000277 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
278 for a secondary */
279 struct match_param match;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100280
Simon Kelleyc72daea2012-01-05 21:33:27 +0000281 match.matched = 0;
282 match.ind = iface_index;
283
284 if (!daemon->if_addrs ||
285 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
286 !match.matched)
287 return;
288
289 iface_addr = match.addr;
290 /* make sure secondary address gets priority in case
291 there is more than one address on the interface in the same subnet */
292 complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
293 }
294
Simon Kelley28866e92011-02-14 20:19:14 +0000295 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100296 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000297
Simon Kelley44a2a312004-03-10 20:04:35 +0000298 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000299 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley7de060b2011-08-26 17:24:52 +0100300 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100301 lease_update_file(now);
302 lease_update_dns();
Simon Kelley16972692006-10-16 20:04:18 +0100303
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100304 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000305 return;
306
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100307 msg.msg_name = &dest;
308 msg.msg_namelen = sizeof(dest);
309 msg.msg_control = NULL;
310 msg.msg_controllen = 0;
311 msg.msg_iov = &iov;
312 iov.iov_base = daemon->dhcp_packet.iov_base;
313
314 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000315 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100316
Simon Kelley3be34542004-09-11 19:12:13 +0100317#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100318 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100319#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000320
Simon Kelley316e2732010-01-22 20:16:09 +0000321 if (pxe_fd)
322 {
323 if (mess->ciaddr.s_addr != 0)
324 dest.sin_addr = mess->ciaddr;
325 }
326 else if (mess->giaddr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100327 {
328 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100329 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100330 dest.sin_addr = mess->giaddr;
331 }
332 else if (mess->ciaddr.s_addr)
333 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100334 /* If the client's idea of its own address tallys with
335 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100336 source port too, and send back to that. If we're replying
337 to a DHCPINFORM, trust the source address always. */
338 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
339 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100340 {
Simon Kelley9e038942008-05-30 20:06:34 +0100341 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100342 dest.sin_addr = mess->ciaddr;
343 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100344 }
Simon Kelley824af852008-02-12 20:43:05 +0000345#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100346 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
347 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100348 {
Simon Kelley849a8352006-06-09 21:02:31 +0100349 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100350 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100351 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100352 msg.msg_controllen = sizeof(control_u);
353 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100354 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
355 pkt->ipi_ifindex = iface_index;
356 pkt->ipi_spec_dst.s_addr = 0;
357 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000358 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000359 cmptr->cmsg_type = IP_PKTINFO;
360 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100361 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000362 }
363 else
364 {
Simon Kelley849a8352006-06-09 21:02:31 +0100365 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
366 struct sockaddr limits size to 14 bytes. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100367 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100368 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100369 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
370 arp_req.arp_ha.sa_family = mess->htype;
371 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
372 /* interface name already copied in */
373 arp_req.arp_flags = ATF_COM;
374 ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000375 }
Simon Kelley824af852008-02-12 20:43:05 +0000376#elif defined(HAVE_SOLARIS_NETWORK)
377 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
378 {
379 /* broadcast to 255.255.255.255 (or mac address invalid) */
380 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100381 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000382 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100383 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000384 }
385 else
386 {
387 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
388 Note that this only works for ethernet on solaris, because we use SIOCSARP
389 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
390 mysteriously. Bah. Fall back to broadcast for other net types. */
391 struct arpreq req;
392 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100393 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000394 *((struct sockaddr_in *)&req.arp_pa) = dest;
395 req.arp_ha.sa_family = AF_UNSPEC;
396 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
397 req.arp_flags = ATF_COM;
398 ioctl(daemon->dhcpfd, SIOCSARP, &req);
399 }
400#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100401 else
402 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100403 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100404 return;
405 }
406#endif
407
Simon Kelley824af852008-02-12 20:43:05 +0000408#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000409 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000410#endif
411
Simon Kelley316e2732010-01-22 20:16:09 +0000412 while(sendmsg(fd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000413}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100414
Simon Kelleyc72daea2012-01-05 21:33:27 +0000415/* check against secondary interface addresses */
416static int check_listen_addrs(struct in_addr local, int if_index,
417 struct in_addr netmask, struct in_addr broadcast, void *vparam)
418{
419 struct match_param *param = vparam;
420 struct iname *tmp;
421
422 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 Kelley5aabfc72007-08-29 11:24:47 +0100449static int complete_context(struct in_addr local, int if_index,
450 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;
454
Simon Kelley0a852542005-03-23 20:28:59 +0000455 for (context = daemon->dhcp; context; context = context->next)
456 {
457 if (!(context->flags & CONTEXT_NETMASK) &&
458 (is_same_net(local, context->start, netmask) ||
459 is_same_net(local, context->end, netmask)))
460 {
461 if (context->netmask.s_addr != netmask.s_addr &&
462 !(is_same_net(local, context->start, netmask) &&
463 is_same_net(local, context->end, netmask)))
464 {
465 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
466 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100467 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100468 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000469 }
470 context->netmask = netmask;
471 }
472
Simon Kelley7de060b2011-08-26 17:24:52 +0100473 if (context->netmask.s_addr != 0 &&
474 is_same_net(local, context->start, context->netmask) &&
475 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000476 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100477 /* link it onto the current chain if we've not seen it before */
478 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000479 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100480 context->router = local;
481 context->local = local;
482 context->current = param->current;
483 param->current = context;
484 }
485
486 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000487 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100488 if (is_same_net(broadcast, context->start, context->netmask))
489 context->broadcast = broadcast;
490 else
Simon Kelley0a852542005-03-23 20:28:59 +0000491 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
492 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100493 }
Simon Kelley0a852542005-03-23 20:28:59 +0000494 }
495
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100496 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000497}
498
Simon Kelley824af852008-02-12 20:43:05 +0000499struct dhcp_context *address_available(struct dhcp_context *context,
500 struct in_addr taddr,
501 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000502{
Simon Kelley36717ee2004-09-20 19:20:58 +0100503 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100504 possible ranges. Make sure that the address isn't in use
505 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000506
Simon Kelley36717ee2004-09-20 19:20:58 +0100507 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100508 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100509
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100510 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100511 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100512 return NULL;
513
514 for (tmp = context; tmp; tmp = tmp->current)
515 {
516 start = ntohl(tmp->start.s_addr);
517 end = ntohl(tmp->end.s_addr);
518
Simon Kelley7de060b2011-08-26 17:24:52 +0100519 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100520 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000521 addr <= end &&
522 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100523 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100524 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000525
Simon Kelley59353a62004-11-21 19:34:28 +0000526 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000527}
Simon Kelley59353a62004-11-21 19:34:28 +0000528
Simon Kelley824af852008-02-12 20:43:05 +0000529struct dhcp_context *narrow_context(struct dhcp_context *context,
530 struct in_addr taddr,
531 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000532{
Simon Kelleye17fb622006-01-14 20:33:46 +0000533 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000534 These are chained on ->current.
535 Here we have an address, and return the actual context correponding to that
536 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000537 any dhcp-range. In that case we return a static range if possible, or failing that,
538 any context on the correct subnet. (If there's more than one, this is a dodgy
539 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000540
Simon Kelleye17fb622006-01-14 20:33:46 +0000541 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000542
Simon Kelley824af852008-02-12 20:43:05 +0000543 if (!(tmp = address_available(context, taddr, netids)))
544 {
545 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100546 if (match_netid(tmp->filter, netids, 1) &&
547 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100548 (tmp->flags & CONTEXT_STATIC))
549 break;
Simon Kelley824af852008-02-12 20:43:05 +0000550
551 if (!tmp)
552 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100553 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100554 is_same_net(taddr, tmp->start, tmp->netmask) &&
555 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000556 break;
557 }
Simon Kelley59353a62004-11-21 19:34:28 +0000558
Simon Kelley824af852008-02-12 20:43:05 +0000559 /* Only one context allowed now */
560 if (tmp)
561 tmp->current = NULL;
562
563 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000564}
565
Simon Kelleydfa666f2004-08-02 18:27:27 +0100566struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
567{
568 struct dhcp_config *config;
569
570 for (config = configs; config; config = config->next)
571 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
572 return config;
573
574 return NULL;
575}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000576
Simon Kelley5aabfc72007-08-29 11:24:47 +0100577int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000578 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100579 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000580{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100581 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000582 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000583 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000584
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100585 struct in_addr start, addr;
586 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000587 int i, pass;
588 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100589
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100590 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
591 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100592 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100593 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100594
Simon Kelleycdeda282006-03-16 20:16:06 +0000595 for (pass = 0; pass <= 1; pass++)
596 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100597 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000598 continue;
599 else if (!match_netid(c->filter, netids, pass))
600 continue;
601 else
602 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100603 if (option_bool(OPT_CONSEC_ADDR))
604 /* seed is largest extant lease addr in this context */
605 start = lease_find_max_addr(c);
606 else
607 /* pick a seed based on hwaddr */
608 start.s_addr = htonl(ntohl(c->start.s_addr) +
609 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
610
611 /* iterate until we find a free address. */
612 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000613
614 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100615 /* eliminate addresses in use by the server. */
616 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100617 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100618 break;
619
Simon Kelley73a08a22009-02-05 20:28:08 +0000620 /* Addresses which end in .255 and .0 are broken in Windows even when using
621 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
622 then 192.168.0.255 is a valid IP address, but not for Windows as it's
623 in the class C range. See KB281579. We therefore don't allocate these
624 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100625 if (!d &&
626 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000627 !config_find_by_address(daemon->dhcp_conf, addr) &&
628 (!IN_CLASSC(ntohl(addr.s_addr)) ||
629 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000630 {
631 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100632 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
633 ((float)PING_WAIT)));
634
635 *addrp = addr;
636
Simon Kelleycdeda282006-03-16 20:16:06 +0000637 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100638 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000639 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100640 on us repeatedly. As a final check, if we did more
641 than 60% of the possible ping checks in the last
642 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000643 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100644 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000645 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100646 else
647 {
648 count++;
649 if (r->addr.s_addr == addr.s_addr)
650 {
651 /* consec-ip mode: we offered this address for another client
652 (different hash) recently, don't offer it to this one. */
653 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
654 break;
655
656 return 1;
657 }
658 }
659
660 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100661 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100662 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000663 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100664 /* address in use: perturb address selection so that we are
665 less likely to try this address again. */
666 if (!option_bool(OPT_CONSEC_ADDR))
667 c->addr_epoch++;
668 }
669 else
670 {
671 /* at this point victim may hold an expired record */
672 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000673 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100674 if ((victim = whine_malloc(sizeof(struct ping_result))))
675 {
676 victim->next = daemon->ping_results;
677 daemon->ping_results = victim;
678 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000679 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100680
681 /* record that this address is OK for 30s
682 without more ping checks */
683 if (victim)
684 {
685 victim->addr = addr;
686 victim->time = now;
687 victim->hash = j;
688 }
689 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000690 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100691 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 }
Simon Kelley3be34542004-09-11 19:12:13 +0100693
Simon Kelleycdeda282006-03-16 20:16:06 +0000694 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
695
696 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
697 addr = c->start;
698
699 } while (addr.s_addr != start.s_addr);
700 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100701
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000702 return 0;
703}
704
705static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
706{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000707 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000708 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100709 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000710 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000711 for (; context; context = context->current)
712 if (is_same_net(config->addr, context->start, context->netmask))
713 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000714
715 return 0;
716}
717
Simon Kelley9009d742008-11-14 20:04:27 +0000718int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
719{
720 struct hwaddr_config *conf_addr;
721
722 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
723 if (conf_addr->wildcard_mask == 0 &&
724 conf_addr->hwaddr_len == len &&
725 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
726 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
727 return 1;
728
729 return 0;
730}
Simon Kelley0a852542005-03-23 20:28:59 +0000731
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000732struct dhcp_config *find_config(struct dhcp_config *configs,
733 struct dhcp_context *context,
734 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000735 unsigned char *hwaddr, int hw_len,
736 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000737{
Simon Kelley7622fc02009-06-04 20:32:05 +0100738 int count, new;
739 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000740 struct hwaddr_config *conf_addr;
741
Simon Kelley0a852542005-03-23 20:28:59 +0000742 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000743 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100744 if (config->flags & CONFIG_CLID)
745 {
746 if (config->clid_len == clid_len &&
747 memcmp(config->clid, clid, clid_len) == 0 &&
748 is_addr_in_context(context, config))
749 return config;
750
751 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
752 cope with that here */
753 if (*clid == 0 && config->clid_len == clid_len-1 &&
754 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
755 is_addr_in_context(context, config))
756 return config;
757 }
758
Simon Kelley0a852542005-03-23 20:28:59 +0000759
Simon Kelleycdeda282006-03-16 20:16:06 +0000760 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000761 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000762 is_addr_in_context(context, config))
763 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100764
Simon Kelley0a852542005-03-23 20:28:59 +0000765 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000766 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100767 if ((config->flags & CONFIG_NAME) &&
768 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000769 is_addr_in_context(context, config))
770 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100771
Simon Kelley4cb1b322012-02-06 14:30:41 +0000772 /* use match with fewest wildcard octets */
Simon Kelley7622fc02009-06-04 20:32:05 +0100773 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
774 if (is_addr_in_context(context, config))
775 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
776 if (conf_addr->wildcard_mask != 0 &&
777 conf_addr->hwaddr_len == hw_len &&
778 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
779 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
780 {
781 count = new;
782 candidate = config;
783 }
784
785 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000786}
787
Simon Kelley5aabfc72007-08-29 11:24:47 +0100788void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000789{
Simon Kelley44a2a312004-03-10 20:04:35 +0000790 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000791 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100792 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100793 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000794 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100795 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100796 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100797 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000798 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100799
800 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000801
802 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100803 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100804 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100805 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100806 }
807
Simon Kelley849a8352006-06-09 21:02:31 +0100808 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100809 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100810 {
811 tmp = config->next;
812 if (config->flags & CONFIG_FROM_ETHERS)
813 {
814 *up = tmp;
815 /* cannot have a clid */
816 if (config->flags & CONFIG_NAME)
817 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000818 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100819 free(config);
820 }
821 else
822 up = &config->next;
823 }
824
Simon Kelley44a2a312004-03-10 20:04:35 +0000825 while (fgets(buff, MAXDNAME, f))
826 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100827 char *host = NULL;
828
Simon Kelleyb8187c82005-11-26 21:46:27 +0000829 lineno++;
830
Simon Kelley824af852008-02-12 20:43:05 +0000831 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000832 buff[strlen(buff)-1] = 0;
833
Simon Kelley73a08a22009-02-05 20:28:08 +0000834 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000835 continue;
836
Simon Kelley824af852008-02-12 20:43:05 +0000837 for (ip = buff; *ip && !isspace((int)*ip); ip++);
838 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000839 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000840 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000841 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100842 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000843 continue;
844 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000845
846 /* check for name or dotted-quad */
847 for (cp = ip; *cp; cp++)
848 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
849 break;
850
851 if (!*cp)
852 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000853 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000854 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100855 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000856 continue;
857 }
858
Simon Kelley33820b72004-04-03 21:10:00 +0100859 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000860
Simon Kelley16972692006-10-16 20:04:18 +0100861 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100862 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000863 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000864 }
865 else
866 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100867 int nomem;
868 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000869 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100870 if (!nomem)
871 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
872 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000873 continue;
874 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100875
Simon Kelley33820b72004-04-03 21:10:00 +0100876 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000877
Simon Kelley16972692006-10-16 20:04:18 +0100878 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100879 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000880 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000881 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100882
883 if (config && (config->flags & CONFIG_FROM_ETHERS))
884 {
885 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
886 continue;
887 }
888
Simon Kelley1cff1662004-03-12 08:12:58 +0000889 if (!config)
890 {
Simon Kelley16972692006-10-16 20:04:18 +0100891 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000892 {
893 struct hwaddr_config *conf_addr = config->hwaddr;
894 if (conf_addr &&
895 conf_addr->next == NULL &&
896 conf_addr->wildcard_mask == 0 &&
897 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
898 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
899 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
900 break;
901 }
Simon Kelley33820b72004-04-03 21:10:00 +0100902
903 if (!config)
904 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100905 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100906 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100907 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000908 config->hwaddr = NULL;
909 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100910 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100911 config->next = daemon->dhcp_conf;
912 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100913 }
914
915 config->flags |= flags;
916
917 if (flags & CONFIG_NAME)
918 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100919 config->hostname = host;
920 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100921 }
922
923 if (flags & CONFIG_ADDR)
924 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000925 }
Simon Kelley33820b72004-04-03 21:10:00 +0100926
Simon Kelley9009d742008-11-14 20:04:27 +0000927 config->flags |= CONFIG_NOCLID;
928 if (!config->hwaddr)
929 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
930 if (config->hwaddr)
931 {
932 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
933 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
934 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
935 config->hwaddr->wildcard_mask = 0;
936 config->hwaddr->next = NULL;
937 }
Simon Kelley33820b72004-04-03 21:10:00 +0100938 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100939
940 free(host);
941
Simon Kelley44a2a312004-03-10 20:04:35 +0000942 }
943
944 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100945
Simon Kelley7622fc02009-06-04 20:32:05 +0100946 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000947}
948
Simon Kelley44a2a312004-03-10 20:04:35 +0000949
Simon Kelleybb01cb92004-12-13 20:56:23 +0000950/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
951 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100952 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100953 so check here that the domain name is legal as a hostname.
954 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100955char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000956{
Simon Kelley824af852008-02-12 20:43:05 +0000957 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000958
959 if (daemon->port == 0)
960 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000961
Simon Kelley824af852008-02-12 20:43:05 +0000962 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100963
Simon Kelleybb01cb92004-12-13 20:56:23 +0000964 if (lookup && (lookup->flags & F_HOSTS))
965 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100966 char *dot, *hostname = cache_get_name(lookup);
967 dot = strchr(hostname, '.');
968
969 if (dot && strlen(dot+1) != 0)
970 {
971 char *d2 = get_domain(addr);
972 if (!d2 || !hostname_isequal(dot+1, d2))
973 return NULL; /* wrong domain */
974 }
975
976 if (!legal_hostname(hostname))
977 return NULL;
978
979 strncpy(daemon->dhcp_buff, hostname, 256);
980 daemon->dhcp_buff[255] = 0;
981 strip_hostname(daemon->dhcp_buff);
982
983 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000984 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100985
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100986 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000987}
988
Simon Kelley7622fc02009-06-04 20:32:05 +0100989#endif
Simon Kelley9009d742008-11-14 20:04:27 +0000990