blob: 6b8b803c7e11bc36431bc581f1b3c43e6bc11cec [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 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 Kelley54dd3932012-06-20 11:23:38 +010069 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
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 Kelleyc4a7f902012-07-12 20:52:12 +0100119#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000120}
121
Simon Kelley316e2732010-01-22 20:16:09 +0000122void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000123{
Simon Kelley316e2732010-01-22 20:16:09 +0000124 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100125 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000126 struct dhcp_context *context;
127 struct iname *tmp;
128 struct ifreq ifr;
129 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100130 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000131 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100132 struct iovec iov;
133 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100134 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000135 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100136 struct iface_param parm;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100137#ifdef HAVE_LINUX_NETWORK
138 struct arpreq arp_req;
139#endif
140
Simon Kelley44a2a312004-03-10 20:04:35 +0000141 union {
142 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000143#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000144 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000145#elif defined(HAVE_SOLARIS_NETWORK)
146 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100147#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000148 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
149#endif
150 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000151 struct dhcp_bridge *bridge, *alias;
152
153 msg.msg_controllen = sizeof(control_u);
154 msg.msg_control = control_u.control;
155 msg.msg_name = &dest;
156 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100157 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000158 msg.msg_iovlen = 1;
159
Simon Kelleyc72daea2012-01-05 21:33:27 +0000160 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
161 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000162 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000163
164 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100165 if (msg.msg_controllen >= sizeof(struct cmsghdr))
166 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000167 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100168 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100169 union {
170 unsigned char *c;
171 struct in_pktinfo *p;
172 } p;
173 p.c = CMSG_DATA(cmptr);
174 iface_index = p.p->ipi_ifindex;
175 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100176 unicast_dest = 1;
177 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100178
179#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100180 if (msg.msg_controllen >= sizeof(struct cmsghdr))
181 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
182 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100183 {
184 union {
185 unsigned char *c;
186 struct sockaddr_dl *s;
187 } p;
188 p.c = CMSG_DATA(cmptr);
189 iface_index = p.s->sdl_index;
190 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100191
Simon Kelley7622fc02009-06-04 20:32:05 +0100192#elif defined(HAVE_SOLARIS_NETWORK)
193 if (msg.msg_controllen >= sizeof(struct cmsghdr))
194 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
195 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100196 {
197 union {
198 unsigned char *c;
199 unsigned int *i;
200 } p;
201 p.c = CMSG_DATA(cmptr);
202 iface_index = *(p.i);
203 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100204#endif
205
206 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
207 return;
208
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100209#ifdef HAVE_LINUX_NETWORK
210 /* ARP fiddling uses original interface even if we pretend to use a different one. */
211 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
212#endif
213
Simon Kelleyc72daea2012-01-05 21:33:27 +0000214 /* One form of bridging on BSD has the property that packets
215 can be recieved on bridge interfaces which do not have an IP address.
216 We allow these to be treated as aliases of another interface which does have
217 an IP address with --dhcp-bridge=interface,alias,alias */
218 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
219 {
220 for (alias = bridge->alias; alias; alias = alias->next)
221 if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
222 {
223 if (!(iface_index = if_nametoindex(bridge->iface)))
224 {
225 my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
226 return;
227 }
228 else
229 {
230 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
231 break;
232 }
233 }
234
235 if (alias)
236 break;
237 }
238
Simon Kelley4011c4e2006-10-28 16:26:19 +0100239#ifdef MSG_BCAST
240 /* OpenBSD tells us when a packet was broadcast */
241 if (!(msg.msg_flags & MSG_BCAST))
242 unicast_dest = 1;
243#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000244
Simon Kelley44a2a312004-03-10 20:04:35 +0000245 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000246 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
Simon Kelleyc72daea2012-01-05 21:33:27 +0000247 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
248 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000249 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000250 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
251 return;
Simon Kelley832af0b2007-01-21 20:01:28 +0000252 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000253
Simon Kelley3d8df262005-08-29 12:19:27 +0100254 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000255 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelley3d8df262005-08-29 12:19:27 +0100256 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000257
Simon Kelley91dccd02005-03-31 17:48:32 +0100258 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100259 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100260 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000261
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100262 parm.current = NULL;
263 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000264
Simon Kelley4f7b3042012-11-28 21:27:02 +0000265 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100266 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000267 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
268 for a secondary */
269 struct match_param match;
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100270
Simon Kelleyc72daea2012-01-05 21:33:27 +0000271 match.matched = 0;
272 match.ind = iface_index;
273
274 if (!daemon->if_addrs ||
275 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
276 !match.matched)
277 return;
278
279 iface_addr = match.addr;
280 /* make sure secondary address gets priority in case
281 there is more than one address on the interface in the same subnet */
282 complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
283 }
284
Simon Kelley28866e92011-02-14 20:19:14 +0000285 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100286 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000287
Simon Kelley44a2a312004-03-10 20:04:35 +0000288 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000289 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley7de060b2011-08-26 17:24:52 +0100290 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100291 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000292 lease_update_dns(0);
Simon Kelley16972692006-10-16 20:04:18 +0100293
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100294 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000295 return;
296
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100297 msg.msg_name = &dest;
298 msg.msg_namelen = sizeof(dest);
299 msg.msg_control = NULL;
300 msg.msg_controllen = 0;
301 msg.msg_iov = &iov;
302 iov.iov_base = daemon->dhcp_packet.iov_base;
303
304 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000305 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100306
Simon Kelley3be34542004-09-11 19:12:13 +0100307#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100308 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100309#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000310
Simon Kelley316e2732010-01-22 20:16:09 +0000311 if (pxe_fd)
312 {
313 if (mess->ciaddr.s_addr != 0)
314 dest.sin_addr = mess->ciaddr;
315 }
316 else if (mess->giaddr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100317 {
318 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100319 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100320 dest.sin_addr = mess->giaddr;
321 }
322 else if (mess->ciaddr.s_addr)
323 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100324 /* If the client's idea of its own address tallys with
325 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100326 source port too, and send back to that. If we're replying
327 to a DHCPINFORM, trust the source address always. */
328 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
329 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100330 {
Simon Kelley9e038942008-05-30 20:06:34 +0100331 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100332 dest.sin_addr = mess->ciaddr;
333 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100334 }
Simon Kelley824af852008-02-12 20:43:05 +0000335#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100336 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
337 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100338 {
Simon Kelley849a8352006-06-09 21:02:31 +0100339 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100340 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100341 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342 msg.msg_controllen = sizeof(control_u);
343 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100344 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
345 pkt->ipi_ifindex = iface_index;
346 pkt->ipi_spec_dst.s_addr = 0;
347 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000348 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000349 cmptr->cmsg_type = IP_PKTINFO;
350 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100351 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000352 }
353 else
354 {
Simon Kelley849a8352006-06-09 21:02:31 +0100355 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
356 struct sockaddr limits size to 14 bytes. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100357 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100358 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100359 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
360 arp_req.arp_ha.sa_family = mess->htype;
361 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
362 /* interface name already copied in */
363 arp_req.arp_flags = ATF_COM;
364 ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000365 }
Simon Kelley824af852008-02-12 20:43:05 +0000366#elif defined(HAVE_SOLARIS_NETWORK)
367 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
368 {
369 /* broadcast to 255.255.255.255 (or mac address invalid) */
370 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100371 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000372 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100373 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000374 }
375 else
376 {
377 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
378 Note that this only works for ethernet on solaris, because we use SIOCSARP
379 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
380 mysteriously. Bah. Fall back to broadcast for other net types. */
381 struct arpreq req;
382 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100383 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000384 *((struct sockaddr_in *)&req.arp_pa) = dest;
385 req.arp_ha.sa_family = AF_UNSPEC;
386 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
387 req.arp_flags = ATF_COM;
388 ioctl(daemon->dhcpfd, SIOCSARP, &req);
389 }
390#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100391 else
392 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100393 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100394 return;
395 }
396#endif
397
Simon Kelley824af852008-02-12 20:43:05 +0000398#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000399 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000400#endif
401
Simon Kelley316e2732010-01-22 20:16:09 +0000402 while(sendmsg(fd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000403}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100404
Simon Kelleyc72daea2012-01-05 21:33:27 +0000405/* check against secondary interface addresses */
406static int check_listen_addrs(struct in_addr local, int if_index,
407 struct in_addr netmask, struct in_addr broadcast, void *vparam)
408{
409 struct match_param *param = vparam;
410 struct iname *tmp;
411
412 if (if_index == param->ind)
413 {
414 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
415 if ( tmp->addr.sa.sa_family == AF_INET &&
416 tmp->addr.in.sin_addr.s_addr == local.s_addr)
417 {
418 param->matched = 1;
419 param->addr = local;
420 param->netmask = netmask;
421 param->broadcast = broadcast;
422 break;
423 }
424 }
425
426 return 1;
427}
428
Simon Kelley0a852542005-03-23 20:28:59 +0000429/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100430 of each interface (and any relay address) and does the following things:
431
432 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
433 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
434 3) Fills in local (this host) and router (this host or relay) addresses.
435 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
436
Simon Kelley0a852542005-03-23 20:28:59 +0000437 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100438
Simon Kelley5aabfc72007-08-29 11:24:47 +0100439static int complete_context(struct in_addr local, int if_index,
440 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000441{
442 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100443 struct iface_param *param = vparam;
444
Simon Kelley0a852542005-03-23 20:28:59 +0000445 for (context = daemon->dhcp; context; context = context->next)
446 {
447 if (!(context->flags & CONTEXT_NETMASK) &&
448 (is_same_net(local, context->start, netmask) ||
449 is_same_net(local, context->end, netmask)))
450 {
451 if (context->netmask.s_addr != netmask.s_addr &&
452 !(is_same_net(local, context->start, netmask) &&
453 is_same_net(local, context->end, netmask)))
454 {
455 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
456 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100457 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100458 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000459 }
460 context->netmask = netmask;
461 }
462
Simon Kelley7de060b2011-08-26 17:24:52 +0100463 if (context->netmask.s_addr != 0 &&
464 is_same_net(local, context->start, context->netmask) &&
465 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000466 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100467 /* link it onto the current chain if we've not seen it before */
468 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000469 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100470 context->router = local;
471 context->local = local;
472 context->current = param->current;
473 param->current = context;
474 }
475
476 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000477 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100478 if (is_same_net(broadcast, context->start, context->netmask))
479 context->broadcast = broadcast;
480 else
Simon Kelley0a852542005-03-23 20:28:59 +0000481 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
482 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100483 }
Simon Kelley0a852542005-03-23 20:28:59 +0000484 }
485
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100486 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000487}
488
Simon Kelley824af852008-02-12 20:43:05 +0000489struct dhcp_context *address_available(struct dhcp_context *context,
490 struct in_addr taddr,
491 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000492{
Simon Kelley36717ee2004-09-20 19:20:58 +0100493 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100494 possible ranges. Make sure that the address isn't in use
495 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000496
Simon Kelley36717ee2004-09-20 19:20:58 +0100497 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100498 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100499
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100500 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100501 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100502 return NULL;
503
504 for (tmp = context; tmp; tmp = tmp->current)
505 {
506 start = ntohl(tmp->start.s_addr);
507 end = ntohl(tmp->end.s_addr);
508
Simon Kelley7de060b2011-08-26 17:24:52 +0100509 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100510 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000511 addr <= end &&
512 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100513 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100514 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000515
Simon Kelley59353a62004-11-21 19:34:28 +0000516 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000517}
Simon Kelley59353a62004-11-21 19:34:28 +0000518
Simon Kelley824af852008-02-12 20:43:05 +0000519struct dhcp_context *narrow_context(struct dhcp_context *context,
520 struct in_addr taddr,
521 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000522{
Simon Kelleye17fb622006-01-14 20:33:46 +0000523 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000524 These are chained on ->current.
525 Here we have an address, and return the actual context correponding to that
526 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000527 any dhcp-range. In that case we return a static range if possible, or failing that,
528 any context on the correct subnet. (If there's more than one, this is a dodgy
529 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000530
Simon Kelleye17fb622006-01-14 20:33:46 +0000531 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000532
Simon Kelley824af852008-02-12 20:43:05 +0000533 if (!(tmp = address_available(context, taddr, netids)))
534 {
535 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100536 if (match_netid(tmp->filter, netids, 1) &&
537 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100538 (tmp->flags & CONTEXT_STATIC))
539 break;
Simon Kelley824af852008-02-12 20:43:05 +0000540
541 if (!tmp)
542 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100543 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100544 is_same_net(taddr, tmp->start, tmp->netmask) &&
545 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000546 break;
547 }
Simon Kelley59353a62004-11-21 19:34:28 +0000548
Simon Kelley824af852008-02-12 20:43:05 +0000549 /* Only one context allowed now */
550 if (tmp)
551 tmp->current = NULL;
552
553 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000554}
555
Simon Kelleydfa666f2004-08-02 18:27:27 +0100556struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
557{
558 struct dhcp_config *config;
559
560 for (config = configs; config; config = config->next)
561 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
562 return config;
563
564 return NULL;
565}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000566
Simon Kelley5aabfc72007-08-29 11:24:47 +0100567int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000568 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100569 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000570{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100571 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000572 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000573 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000574
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100575 struct in_addr start, addr;
576 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000577 int i, pass;
578 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100579
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100580 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
581 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100582 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100583 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100584
Simon Kelleycdeda282006-03-16 20:16:06 +0000585 for (pass = 0; pass <= 1; pass++)
586 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100587 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000588 continue;
589 else if (!match_netid(c->filter, netids, pass))
590 continue;
591 else
592 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100593 if (option_bool(OPT_CONSEC_ADDR))
594 /* seed is largest extant lease addr in this context */
595 start = lease_find_max_addr(c);
596 else
597 /* pick a seed based on hwaddr */
598 start.s_addr = htonl(ntohl(c->start.s_addr) +
599 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
600
601 /* iterate until we find a free address. */
602 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000603
604 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100605 /* eliminate addresses in use by the server. */
606 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100607 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100608 break;
609
Simon Kelley73a08a22009-02-05 20:28:08 +0000610 /* Addresses which end in .255 and .0 are broken in Windows even when using
611 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
612 then 192.168.0.255 is a valid IP address, but not for Windows as it's
613 in the class C range. See KB281579. We therefore don't allocate these
614 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100615 if (!d &&
616 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000617 !config_find_by_address(daemon->dhcp_conf, addr) &&
618 (!IN_CLASSC(ntohl(addr.s_addr)) ||
619 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000620 {
621 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100622 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
623 ((float)PING_WAIT)));
624
625 *addrp = addr;
626
Simon Kelleycdeda282006-03-16 20:16:06 +0000627 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100628 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000629 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100630 on us repeatedly. As a final check, if we did more
631 than 60% of the possible ping checks in the last
632 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000633 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100634 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000635 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100636 else
637 {
638 count++;
639 if (r->addr.s_addr == addr.s_addr)
640 {
641 /* consec-ip mode: we offered this address for another client
642 (different hash) recently, don't offer it to this one. */
643 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
644 break;
645
646 return 1;
647 }
648 }
649
650 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100651 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100652 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000653 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100654 /* address in use: perturb address selection so that we are
655 less likely to try this address again. */
656 if (!option_bool(OPT_CONSEC_ADDR))
657 c->addr_epoch++;
658 }
659 else
660 {
661 /* at this point victim may hold an expired record */
662 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000663 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100664 if ((victim = whine_malloc(sizeof(struct ping_result))))
665 {
666 victim->next = daemon->ping_results;
667 daemon->ping_results = victim;
668 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000669 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100670
671 /* record that this address is OK for 30s
672 without more ping checks */
673 if (victim)
674 {
675 victim->addr = addr;
676 victim->time = now;
677 victim->hash = j;
678 }
679 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000680 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100681 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000682 }
Simon Kelley3be34542004-09-11 19:12:13 +0100683
Simon Kelleycdeda282006-03-16 20:16:06 +0000684 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
685
686 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
687 addr = c->start;
688
689 } while (addr.s_addr != start.s_addr);
690 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100691
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000692 return 0;
693}
694
695static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
696{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000697 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000698 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100699 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000700 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000701 for (; context; context = context->current)
702 if (is_same_net(config->addr, context->start, context->netmask))
703 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000704
705 return 0;
706}
707
Simon Kelley9009d742008-11-14 20:04:27 +0000708int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
709{
710 struct hwaddr_config *conf_addr;
711
712 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
713 if (conf_addr->wildcard_mask == 0 &&
714 conf_addr->hwaddr_len == len &&
715 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
716 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
717 return 1;
718
719 return 0;
720}
Simon Kelley0a852542005-03-23 20:28:59 +0000721
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000722struct dhcp_config *find_config(struct dhcp_config *configs,
723 struct dhcp_context *context,
724 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000725 unsigned char *hwaddr, int hw_len,
726 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000727{
Simon Kelley7622fc02009-06-04 20:32:05 +0100728 int count, new;
729 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000730 struct hwaddr_config *conf_addr;
731
Simon Kelley0a852542005-03-23 20:28:59 +0000732 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000733 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100734 if (config->flags & CONFIG_CLID)
735 {
736 if (config->clid_len == clid_len &&
737 memcmp(config->clid, clid, clid_len) == 0 &&
738 is_addr_in_context(context, config))
739 return config;
740
741 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
742 cope with that here */
743 if (*clid == 0 && config->clid_len == clid_len-1 &&
744 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
745 is_addr_in_context(context, config))
746 return config;
747 }
748
Simon Kelley0a852542005-03-23 20:28:59 +0000749
Simon Kelleycdeda282006-03-16 20:16:06 +0000750 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000751 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000752 is_addr_in_context(context, config))
753 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100754
Simon Kelley0a852542005-03-23 20:28:59 +0000755 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000756 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100757 if ((config->flags & CONFIG_NAME) &&
758 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000759 is_addr_in_context(context, config))
760 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100761
Simon Kelley4cb1b322012-02-06 14:30:41 +0000762 /* use match with fewest wildcard octets */
Simon Kelley7622fc02009-06-04 20:32:05 +0100763 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
764 if (is_addr_in_context(context, config))
765 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
766 if (conf_addr->wildcard_mask != 0 &&
767 conf_addr->hwaddr_len == hw_len &&
768 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
769 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
770 {
771 count = new;
772 candidate = config;
773 }
774
775 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000776}
777
Simon Kelley5aabfc72007-08-29 11:24:47 +0100778void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000779{
Simon Kelley44a2a312004-03-10 20:04:35 +0000780 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000781 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100782 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100783 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000784 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100785 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100786 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100787 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000788 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100789
790 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000791
792 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100793 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100794 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100795 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100796 }
797
Simon Kelley849a8352006-06-09 21:02:31 +0100798 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100799 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100800 {
801 tmp = config->next;
802 if (config->flags & CONFIG_FROM_ETHERS)
803 {
804 *up = tmp;
805 /* cannot have a clid */
806 if (config->flags & CONFIG_NAME)
807 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000808 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100809 free(config);
810 }
811 else
812 up = &config->next;
813 }
814
Simon Kelley44a2a312004-03-10 20:04:35 +0000815 while (fgets(buff, MAXDNAME, f))
816 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100817 char *host = NULL;
818
Simon Kelleyb8187c82005-11-26 21:46:27 +0000819 lineno++;
820
Simon Kelley824af852008-02-12 20:43:05 +0000821 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000822 buff[strlen(buff)-1] = 0;
823
Simon Kelley73a08a22009-02-05 20:28:08 +0000824 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000825 continue;
826
Simon Kelley824af852008-02-12 20:43:05 +0000827 for (ip = buff; *ip && !isspace((int)*ip); ip++);
828 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000829 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000830 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000831 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100832 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000833 continue;
834 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000835
836 /* check for name or dotted-quad */
837 for (cp = ip; *cp; cp++)
838 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
839 break;
840
841 if (!*cp)
842 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000843 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000844 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100845 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000846 continue;
847 }
848
Simon Kelley33820b72004-04-03 21:10:00 +0100849 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000850
Simon Kelley16972692006-10-16 20:04:18 +0100851 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100852 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000853 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000854 }
855 else
856 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100857 int nomem;
858 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000859 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100860 if (!nomem)
861 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
862 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000863 continue;
864 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100865
Simon Kelley33820b72004-04-03 21:10:00 +0100866 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000867
Simon Kelley16972692006-10-16 20:04:18 +0100868 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100869 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000870 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000871 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100872
873 if (config && (config->flags & CONFIG_FROM_ETHERS))
874 {
875 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
876 continue;
877 }
878
Simon Kelley1cff1662004-03-12 08:12:58 +0000879 if (!config)
880 {
Simon Kelley16972692006-10-16 20:04:18 +0100881 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000882 {
883 struct hwaddr_config *conf_addr = config->hwaddr;
884 if (conf_addr &&
885 conf_addr->next == NULL &&
886 conf_addr->wildcard_mask == 0 &&
887 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
888 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
889 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
890 break;
891 }
Simon Kelley33820b72004-04-03 21:10:00 +0100892
893 if (!config)
894 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100895 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100896 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100897 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000898 config->hwaddr = NULL;
899 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100900 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100901 config->next = daemon->dhcp_conf;
902 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100903 }
904
905 config->flags |= flags;
906
907 if (flags & CONFIG_NAME)
908 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100909 config->hostname = host;
910 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100911 }
912
913 if (flags & CONFIG_ADDR)
914 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000915 }
Simon Kelley33820b72004-04-03 21:10:00 +0100916
Simon Kelley9009d742008-11-14 20:04:27 +0000917 config->flags |= CONFIG_NOCLID;
918 if (!config->hwaddr)
919 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
920 if (config->hwaddr)
921 {
922 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
923 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
924 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
925 config->hwaddr->wildcard_mask = 0;
926 config->hwaddr->next = NULL;
927 }
Simon Kelley33820b72004-04-03 21:10:00 +0100928 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100929
930 free(host);
931
Simon Kelley44a2a312004-03-10 20:04:35 +0000932 }
933
934 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100935
Simon Kelley7622fc02009-06-04 20:32:05 +0100936 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000937}
938
Simon Kelley44a2a312004-03-10 20:04:35 +0000939
Simon Kelleybb01cb92004-12-13 20:56:23 +0000940/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
941 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100942 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100943 so check here that the domain name is legal as a hostname.
944 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100945char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000946{
Simon Kelley824af852008-02-12 20:43:05 +0000947 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000948
949 if (daemon->port == 0)
950 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000951
Simon Kelley824af852008-02-12 20:43:05 +0000952 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100953
Simon Kelleybb01cb92004-12-13 20:56:23 +0000954 if (lookup && (lookup->flags & F_HOSTS))
955 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100956 char *dot, *hostname = cache_get_name(lookup);
957 dot = strchr(hostname, '.');
958
959 if (dot && strlen(dot+1) != 0)
960 {
961 char *d2 = get_domain(addr);
962 if (!d2 || !hostname_isequal(dot+1, d2))
963 return NULL; /* wrong domain */
964 }
965
966 if (!legal_hostname(hostname))
967 return NULL;
968
969 strncpy(daemon->dhcp_buff, hostname, 256);
970 daemon->dhcp_buff[255] = 0;
971 strip_hostname(daemon->dhcp_buff);
972
973 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000974 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100975
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100976 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000977}
978
Simon Kelley7622fc02009-06-04 20:32:05 +0100979#endif
Simon Kelley9009d742008-11-14 20:04:27 +0000980