blob: dd256329bf1031727b96294bd347acd685c997f2 [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 Kelley56a11422013-04-02 17:02:58 +010068 Need to set REUSEADDR|REUSEPORT to make this posible.
69 Handle the case that REUSEPORT is defined, but the kernel doesn't
70 support it. This handles the introduction of REUSEPORT on Linux. */
Simon Kelley54dd3932012-06-20 11:23:38 +010071 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley4011c4e2006-10-28 16:26:19 +010072 {
Simon Kelley56a11422013-04-02 17:02:58 +010073 int rc = -1, porterr = 0;
74
Simon Kelley4011c4e2006-10-28 16:26:19 +010075#ifdef SO_REUSEPORT
Simon Kelley56a11422013-04-02 17:02:58 +010076 if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
77 errno != ENOPROTOOPT)
78 porterr = 1;
Simon Kelley4011c4e2006-10-28 16:26:19 +010079#endif
Simon Kelley56a11422013-04-02 17:02:58 +010080
81 if (rc == -1 && !porterr)
82 rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
83
Simon Kelley4011c4e2006-10-28 16:26:19 +010084 if (rc == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +010085 die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
Simon Kelley4011c4e2006-10-28 16:26:19 +010086 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +000087
Simon Kelley849a8352006-06-09 21:02:31 +010088 memset(&saddr, 0, sizeof(saddr));
Simon Kelley44a2a312004-03-10 20:04:35 +000089 saddr.sin_family = AF_INET;
Simon Kelley316e2732010-01-22 20:16:09 +000090 saddr.sin_port = htons(port);
Simon Kelley44a2a312004-03-10 20:04:35 +000091 saddr.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +010092#ifdef HAVE_SOCKADDR_SA_LEN
93 saddr.sin_len = sizeof(struct sockaddr_in);
94#endif
95
Simon Kelley44a2a312004-03-10 20:04:35 +000096 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
Simon Kelley5aabfc72007-08-29 11:24:47 +010097 die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
Simon Kelley44a2a312004-03-10 20:04:35 +000098
Simon Kelley316e2732010-01-22 20:16:09 +000099 return fd;
100}
101
102void dhcp_init(void)
103{
104#if defined(HAVE_BSD_NETWORK)
105 int oneopt = 1;
106#endif
107
108 daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
109 if (daemon->enable_pxe)
110 daemon->pxefd = make_fd(PXE_PORT);
111 else
112 daemon->pxefd = -1;
Simon Kelley3be34542004-09-11 19:12:13 +0100113
Simon Kelley824af852008-02-12 20:43:05 +0000114#if defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 /* When we're not using capabilities, we need to do this here before
116 we drop root. Also, set buffer size small, to avoid wasting
117 kernel buffers */
Simon Kelley44a2a312004-03-10 20:04:35 +0000118
Simon Kelley28866e92011-02-14 20:19:14 +0000119 if (option_bool(OPT_NO_PING))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100120 daemon->dhcp_icmp_fd = -1;
121 else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
122 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
Simon Kelley5aabfc72007-08-29 11:24:47 +0100123 die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100124
125 /* Make BPF raw send socket */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100126 init_bpf();
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100127#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000128}
129
Simon Kelley316e2732010-01-22 20:16:09 +0000130void dhcp_packet(time_t now, int pxe_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000131{
Simon Kelley316e2732010-01-22 20:16:09 +0000132 int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100133 struct dhcp_packet *mess;
Simon Kelley44a2a312004-03-10 20:04:35 +0000134 struct dhcp_context *context;
135 struct iname *tmp;
136 struct ifreq ifr;
137 struct msghdr msg;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100138 struct sockaddr_in dest;
Simon Kelley44a2a312004-03-10 20:04:35 +0000139 struct cmsghdr *cmptr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100140 struct iovec iov;
141 ssize_t sz;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100142 int iface_index = 0, unicast_dest = 0, is_inform = 0;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000143 struct in_addr iface_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 struct iface_param parm;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100145#ifdef HAVE_LINUX_NETWORK
146 struct arpreq arp_req;
147#endif
148
Simon Kelley44a2a312004-03-10 20:04:35 +0000149 union {
150 struct cmsghdr align; /* this ensures alignment */
Simon Kelley824af852008-02-12 20:43:05 +0000151#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000152 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000153#elif defined(HAVE_SOLARIS_NETWORK)
154 char control[CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley7622fc02009-06-04 20:32:05 +0100155#elif defined(HAVE_BSD_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000156 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
157#endif
158 } control_u;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000159 struct dhcp_bridge *bridge, *alias;
160
161 msg.msg_controllen = sizeof(control_u);
162 msg.msg_control = control_u.control;
163 msg.msg_name = &dest;
164 msg.msg_namelen = sizeof(dest);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100165 msg.msg_iov = &daemon->dhcp_packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000166 msg.msg_iovlen = 1;
167
Simon Kelleyc72daea2012-01-05 21:33:27 +0000168 if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
169 (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
Simon Kelley44a2a312004-03-10 20:04:35 +0000170 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000171
172 #if defined (HAVE_LINUX_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100173 if (msg.msg_controllen >= sizeof(struct cmsghdr))
174 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100176 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100177 union {
178 unsigned char *c;
179 struct in_pktinfo *p;
180 } p;
181 p.c = CMSG_DATA(cmptr);
182 iface_index = p.p->ipi_ifindex;
183 if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100184 unicast_dest = 1;
185 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100186
187#elif defined(HAVE_BSD_NETWORK)
Simon Kelley4011c4e2006-10-28 16:26:19 +0100188 if (msg.msg_controllen >= sizeof(struct cmsghdr))
189 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
190 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100191 {
192 union {
193 unsigned char *c;
194 struct sockaddr_dl *s;
195 } p;
196 p.c = CMSG_DATA(cmptr);
197 iface_index = p.s->sdl_index;
198 }
Simon Kelley4011c4e2006-10-28 16:26:19 +0100199
Simon Kelley7622fc02009-06-04 20:32:05 +0100200#elif defined(HAVE_SOLARIS_NETWORK)
201 if (msg.msg_controllen >= sizeof(struct cmsghdr))
202 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
203 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100204 {
205 union {
206 unsigned char *c;
207 unsigned int *i;
208 } p;
209 p.c = CMSG_DATA(cmptr);
210 iface_index = *(p.i);
211 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100212#endif
213
214 if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
215 return;
216
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100217#ifdef HAVE_LINUX_NETWORK
218 /* ARP fiddling uses original interface even if we pretend to use a different one. */
219 strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
220#endif
221
Simon Kelleyc72daea2012-01-05 21:33:27 +0000222 /* One form of bridging on BSD has the property that packets
223 can be recieved on bridge interfaces which do not have an IP address.
224 We allow these to be treated as aliases of another interface which does have
225 an IP address with --dhcp-bridge=interface,alias,alias */
226 for (bridge = daemon->bridges; bridge; bridge = bridge->next)
227 {
228 for (alias = bridge->alias; alias; alias = alias->next)
229 if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
230 {
231 if (!(iface_index = if_nametoindex(bridge->iface)))
232 {
233 my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
234 return;
235 }
236 else
237 {
238 strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
239 break;
240 }
241 }
242
243 if (alias)
244 break;
245 }
246
Simon Kelley4011c4e2006-10-28 16:26:19 +0100247#ifdef MSG_BCAST
248 /* OpenBSD tells us when a packet was broadcast */
249 if (!(msg.msg_flags & MSG_BCAST))
250 unicast_dest = 1;
251#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000252
Simon Kelley44a2a312004-03-10 20:04:35 +0000253 ifr.ifr_addr.sa_family = AF_INET;
Simon Kelley832af0b2007-01-21 20:01:28 +0000254 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
Simon Kelleyc72daea2012-01-05 21:33:27 +0000255 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
256 else
Simon Kelley832af0b2007-01-21 20:01:28 +0000257 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000258 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
259 return;
Simon Kelley832af0b2007-01-21 20:01:28 +0000260 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000261
Simon Kelley3d8df262005-08-29 12:19:27 +0100262 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
Simon Kelley49333cb2013-03-15 20:30:51 +0000263 if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
Simon Kelley3d8df262005-08-29 12:19:27 +0100264 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000265
Simon Kelley91dccd02005-03-31 17:48:32 +0100266 /* unlinked contexts are marked by context->current == context */
Simon Kelley3be34542004-09-11 19:12:13 +0100267 for (context = daemon->dhcp; context; context = context->next)
Simon Kelley91dccd02005-03-31 17:48:32 +0100268 context->current = context;
Simon Kelley44a2a312004-03-10 20:04:35 +0000269
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100270 parm.current = NULL;
271 parm.ind = iface_index;
Simon Kelleye17fb622006-01-14 20:33:46 +0000272
Simon Kelley4f7b3042012-11-28 21:27:02 +0000273 if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100274 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000275 /* If we failed to match the primary address of the interface, see if we've got a --listen-address
276 for a secondary */
277 struct match_param match;
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100278
Simon Kelleyc72daea2012-01-05 21:33:27 +0000279 match.matched = 0;
280 match.ind = iface_index;
281
282 if (!daemon->if_addrs ||
283 !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
284 !match.matched)
285 return;
286
287 iface_addr = match.addr;
288 /* make sure secondary address gets priority in case
289 there is more than one address on the interface in the same subnet */
290 complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
291 }
292
Simon Kelley28866e92011-02-14 20:19:14 +0000293 if (!iface_enumerate(AF_INET, &parm, complete_context))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100294 return;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000295
Simon Kelley44a2a312004-03-10 20:04:35 +0000296 lease_prune(NULL, now); /* lose any expired leases */
Simon Kelley824af852008-02-12 20:43:05 +0000297 iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
Simon Kelley7de060b2011-08-26 17:24:52 +0100298 now, unicast_dest, &is_inform, pxe_fd, iface_addr);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100299 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000300 lease_update_dns(0);
Simon Kelley16972692006-10-16 20:04:18 +0100301
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100302 if (iov.iov_len == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000303 return;
304
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100305 msg.msg_name = &dest;
306 msg.msg_namelen = sizeof(dest);
307 msg.msg_control = NULL;
308 msg.msg_controllen = 0;
309 msg.msg_iov = &iov;
310 iov.iov_base = daemon->dhcp_packet.iov_base;
311
312 /* packet buffer may have moved */
Simon Kelley824af852008-02-12 20:43:05 +0000313 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100314
Simon Kelley3be34542004-09-11 19:12:13 +0100315#ifdef HAVE_SOCKADDR_SA_LEN
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100316 dest.sin_len = sizeof(struct sockaddr_in);
Simon Kelley3be34542004-09-11 19:12:13 +0100317#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000318
Simon Kelley316e2732010-01-22 20:16:09 +0000319 if (pxe_fd)
320 {
321 if (mess->ciaddr.s_addr != 0)
322 dest.sin_addr = mess->ciaddr;
323 }
324 else if (mess->giaddr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100325 {
326 /* Send to BOOTP relay */
Simon Kelley9e038942008-05-30 20:06:34 +0100327 dest.sin_port = htons(daemon->dhcp_server_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100328 dest.sin_addr = mess->giaddr;
329 }
330 else if (mess->ciaddr.s_addr)
331 {
Simon Kelley208b65c2006-08-05 21:41:37 +0100332 /* If the client's idea of its own address tallys with
333 the source address in the request packet, we believe the
Simon Kelley5aabfc72007-08-29 11:24:47 +0100334 source port too, and send back to that. If we're replying
335 to a DHCPINFORM, trust the source address always. */
336 if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
337 dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
Simon Kelley208b65c2006-08-05 21:41:37 +0100338 {
Simon Kelley9e038942008-05-30 20:06:34 +0100339 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley208b65c2006-08-05 21:41:37 +0100340 dest.sin_addr = mess->ciaddr;
341 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342 }
Simon Kelley824af852008-02-12 20:43:05 +0000343#if defined(HAVE_LINUX_NETWORK)
Simon Kelley849a8352006-06-09 21:02:31 +0100344 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
345 mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100346 {
Simon Kelley849a8352006-06-09 21:02:31 +0100347 /* broadcast to 255.255.255.255 (or mac address invalid) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100348 struct in_pktinfo *pkt;
Simon Kelley26d0dba2006-04-23 20:00:42 +0100349 msg.msg_control = control_u.control;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100350 msg.msg_controllen = sizeof(control_u);
351 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100352 pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
353 pkt->ipi_ifindex = iface_index;
354 pkt->ipi_spec_dst.s_addr = 0;
355 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
Simon Kelleyc72daea2012-01-05 21:33:27 +0000356 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley824af852008-02-12 20:43:05 +0000357 cmptr->cmsg_type = IP_PKTINFO;
358 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100359 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley44a2a312004-03-10 20:04:35 +0000360 }
361 else
362 {
Simon Kelley849a8352006-06-09 21:02:31 +0100363 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
364 struct sockaddr limits size to 14 bytes. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100365 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100366 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100367 memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
368 arp_req.arp_ha.sa_family = mess->htype;
369 memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
370 /* interface name already copied in */
371 arp_req.arp_flags = ATF_COM;
372 ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000373 }
Simon Kelley824af852008-02-12 20:43:05 +0000374#elif defined(HAVE_SOLARIS_NETWORK)
375 else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
376 {
377 /* broadcast to 255.255.255.255 (or mac address invalid) */
378 dest.sin_addr.s_addr = INADDR_BROADCAST;
Simon Kelley9e038942008-05-30 20:06:34 +0100379 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000380 /* note that we don't specify the interface here: that's done by the
Simon Kelley7622fc02009-06-04 20:32:05 +0100381 IP_BOUND_IF sockopt lower down. */
Simon Kelley824af852008-02-12 20:43:05 +0000382 }
383 else
384 {
385 /* unicast to unconfigured client. Inject mac address direct into ARP cache.
386 Note that this only works for ethernet on solaris, because we use SIOCSARP
387 and not SIOCSXARP, which would be perfect, except that it returns ENXIO
388 mysteriously. Bah. Fall back to broadcast for other net types. */
389 struct arpreq req;
390 dest.sin_addr = mess->yiaddr;
Simon Kelley9e038942008-05-30 20:06:34 +0100391 dest.sin_port = htons(daemon->dhcp_client_port);
Simon Kelley824af852008-02-12 20:43:05 +0000392 *((struct sockaddr_in *)&req.arp_pa) = dest;
393 req.arp_ha.sa_family = AF_UNSPEC;
394 memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
395 req.arp_flags = ATF_COM;
396 ioctl(daemon->dhcpfd, SIOCSARP, &req);
397 }
398#elif defined(HAVE_BSD_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 else
400 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100401 send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100402 return;
403 }
404#endif
405
Simon Kelley824af852008-02-12 20:43:05 +0000406#ifdef HAVE_SOLARIS_NETWORK
Simon Kelley316e2732010-01-22 20:16:09 +0000407 setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
Simon Kelley824af852008-02-12 20:43:05 +0000408#endif
409
Simon Kelley316e2732010-01-22 20:16:09 +0000410 while(sendmsg(fd, &msg, 0) == -1 && retry_send());
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000411}
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100412
Simon Kelleyc72daea2012-01-05 21:33:27 +0000413/* check against secondary interface addresses */
414static int check_listen_addrs(struct in_addr local, int if_index,
415 struct in_addr netmask, struct in_addr broadcast, void *vparam)
416{
417 struct match_param *param = vparam;
418 struct iname *tmp;
419
420 if (if_index == param->ind)
421 {
422 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
423 if ( tmp->addr.sa.sa_family == AF_INET &&
424 tmp->addr.in.sin_addr.s_addr == local.s_addr)
425 {
426 param->matched = 1;
427 param->addr = local;
428 param->netmask = netmask;
429 param->broadcast = broadcast;
430 break;
431 }
432 }
433
434 return 1;
435}
436
Simon Kelley0a852542005-03-23 20:28:59 +0000437/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100438 of each interface (and any relay address) and does the following things:
439
440 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
441 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
442 3) Fills in local (this host) and router (this host or relay) addresses.
443 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
444
Simon Kelley0a852542005-03-23 20:28:59 +0000445 Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100446
Simon Kelley5aabfc72007-08-29 11:24:47 +0100447static int complete_context(struct in_addr local, int if_index,
448 struct in_addr netmask, struct in_addr broadcast, void *vparam)
Simon Kelley0a852542005-03-23 20:28:59 +0000449{
450 struct dhcp_context *context;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100451 struct iface_param *param = vparam;
452
Simon Kelley0a852542005-03-23 20:28:59 +0000453 for (context = daemon->dhcp; context; context = context->next)
454 {
455 if (!(context->flags & CONTEXT_NETMASK) &&
456 (is_same_net(local, context->start, netmask) ||
457 is_same_net(local, context->end, netmask)))
458 {
459 if (context->netmask.s_addr != netmask.s_addr &&
460 !(is_same_net(local, context->start, netmask) &&
461 is_same_net(local, context->end, netmask)))
462 {
463 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
464 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
Simon Kelley7622fc02009-06-04 20:32:05 +0100465 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100466 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
Simon Kelley0a852542005-03-23 20:28:59 +0000467 }
468 context->netmask = netmask;
469 }
470
Simon Kelley7de060b2011-08-26 17:24:52 +0100471 if (context->netmask.s_addr != 0 &&
472 is_same_net(local, context->start, context->netmask) &&
473 is_same_net(local, context->end, context->netmask))
Simon Kelley0a852542005-03-23 20:28:59 +0000474 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100475 /* link it onto the current chain if we've not seen it before */
476 if (if_index == param->ind && context->current == context)
Simon Kelley0a852542005-03-23 20:28:59 +0000477 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100478 context->router = local;
479 context->local = local;
480 context->current = param->current;
481 param->current = context;
482 }
483
484 if (!(context->flags & CONTEXT_BRDCAST))
Simon Kelley0a852542005-03-23 20:28:59 +0000485 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100486 if (is_same_net(broadcast, context->start, context->netmask))
487 context->broadcast = broadcast;
488 else
Simon Kelley0a852542005-03-23 20:28:59 +0000489 context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
490 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100491 }
Simon Kelley0a852542005-03-23 20:28:59 +0000492 }
493
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100494 return 1;
Simon Kelley0a852542005-03-23 20:28:59 +0000495}
496
Simon Kelley824af852008-02-12 20:43:05 +0000497struct dhcp_context *address_available(struct dhcp_context *context,
498 struct in_addr taddr,
499 struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000500{
Simon Kelley36717ee2004-09-20 19:20:58 +0100501 /* Check is an address is OK for this network, check all
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100502 possible ranges. Make sure that the address isn't in use
503 by the server itself. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000504
Simon Kelley36717ee2004-09-20 19:20:58 +0100505 unsigned int start, end, addr = ntohl(taddr.s_addr);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100506 struct dhcp_context *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100507
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100508 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100509 if (taddr.s_addr == context->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100510 return NULL;
511
512 for (tmp = context; tmp; tmp = tmp->current)
513 {
514 start = ntohl(tmp->start.s_addr);
515 end = ntohl(tmp->end.s_addr);
516
Simon Kelley7de060b2011-08-26 17:24:52 +0100517 if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100518 addr >= start &&
Simon Kelley824af852008-02-12 20:43:05 +0000519 addr <= end &&
520 match_netid(tmp->filter, netids, 1))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100521 return tmp;
Simon Kelley36717ee2004-09-20 19:20:58 +0100522 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000523
Simon Kelley59353a62004-11-21 19:34:28 +0000524 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000525}
Simon Kelley59353a62004-11-21 19:34:28 +0000526
Simon Kelley824af852008-02-12 20:43:05 +0000527struct dhcp_context *narrow_context(struct dhcp_context *context,
528 struct in_addr taddr,
529 struct dhcp_netid *netids)
Simon Kelley59353a62004-11-21 19:34:28 +0000530{
Simon Kelleye17fb622006-01-14 20:33:46 +0000531 /* We start of with a set of possible contexts, all on the current physical interface.
Simon Kelley59353a62004-11-21 19:34:28 +0000532 These are chained on ->current.
533 Here we have an address, and return the actual context correponding to that
534 address. Note that none may fit, if the address came a dhcp-host and is outside
Simon Kelleye17fb622006-01-14 20:33:46 +0000535 any dhcp-range. In that case we return a static range if possible, or failing that,
536 any context on the correct subnet. (If there's more than one, this is a dodgy
537 configuration: maybe there should be a warning.) */
Simon Kelley59353a62004-11-21 19:34:28 +0000538
Simon Kelleye17fb622006-01-14 20:33:46 +0000539 struct dhcp_context *tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000540
Simon Kelley824af852008-02-12 20:43:05 +0000541 if (!(tmp = address_available(context, taddr, netids)))
542 {
543 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100544 if (match_netid(tmp->filter, netids, 1) &&
545 is_same_net(taddr, tmp->start, tmp->netmask) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100546 (tmp->flags & CONTEXT_STATIC))
547 break;
Simon Kelley824af852008-02-12 20:43:05 +0000548
549 if (!tmp)
550 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100551 if (match_netid(tmp->filter, netids, 1) &&
Simon Kelley7de060b2011-08-26 17:24:52 +0100552 is_same_net(taddr, tmp->start, tmp->netmask) &&
553 !(tmp->flags & CONTEXT_PROXY))
Simon Kelley824af852008-02-12 20:43:05 +0000554 break;
555 }
Simon Kelley59353a62004-11-21 19:34:28 +0000556
Simon Kelley824af852008-02-12 20:43:05 +0000557 /* Only one context allowed now */
558 if (tmp)
559 tmp->current = NULL;
560
561 return tmp;
Simon Kelley59353a62004-11-21 19:34:28 +0000562}
563
Simon Kelleydfa666f2004-08-02 18:27:27 +0100564struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
565{
566 struct dhcp_config *config;
567
568 for (config = configs; config; config = config->next)
569 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
570 return config;
571
572 return NULL;
573}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000574
Simon Kelley5aabfc72007-08-29 11:24:47 +0100575int address_allocate(struct dhcp_context *context,
Simon Kelleycdeda282006-03-16 20:16:06 +0000576 struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
Simon Kelley3d8df262005-08-29 12:19:27 +0100577 struct dhcp_netid *netids, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000578{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100579 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000580 a particular hwaddr/clientid/hostname in our configuration.
Simon Kelleycdeda282006-03-16 20:16:06 +0000581 Try to return from contexts which match netids first. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000582
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100583 struct in_addr start, addr;
584 struct dhcp_context *c, *d;
Simon Kelleycdeda282006-03-16 20:16:06 +0000585 int i, pass;
586 unsigned int j;
Simon Kelley3d8df262005-08-29 12:19:27 +0100587
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100588 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
589 dispersal even with similarly-valued "strings". */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100590 for (j = 0, i = 0; i < hw_len; i++)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100591 j += hwaddr[i] + (j << 6) + (j << 16) - j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100592
Simon Kelleycdeda282006-03-16 20:16:06 +0000593 for (pass = 0; pass <= 1; pass++)
594 for (c = context; c; c = c->current)
Simon Kelley7de060b2011-08-26 17:24:52 +0100595 if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelleycdeda282006-03-16 20:16:06 +0000596 continue;
597 else if (!match_netid(c->filter, netids, pass))
598 continue;
599 else
600 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100601 if (option_bool(OPT_CONSEC_ADDR))
602 /* seed is largest extant lease addr in this context */
603 start = lease_find_max_addr(c);
604 else
605 /* pick a seed based on hwaddr */
606 start.s_addr = htonl(ntohl(c->start.s_addr) +
607 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
608
609 /* iterate until we find a free address. */
610 addr = start;
Simon Kelleycdeda282006-03-16 20:16:06 +0000611
612 do {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100613 /* eliminate addresses in use by the server. */
614 for (d = context; d; d = d->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100615 if (addr.s_addr == d->router.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100616 break;
617
Simon Kelley73a08a22009-02-05 20:28:08 +0000618 /* Addresses which end in .255 and .0 are broken in Windows even when using
619 supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
620 then 192.168.0.255 is a valid IP address, but not for Windows as it's
621 in the class C range. See KB281579. We therefore don't allocate these
622 addresses to avoid hard-to-diagnose problems. Thanks Bill. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100623 if (!d &&
624 !lease_find_by_addr(addr) &&
Simon Kelley73a08a22009-02-05 20:28:08 +0000625 !config_find_by_address(daemon->dhcp_conf, addr) &&
626 (!IN_CLASSC(ntohl(addr.s_addr)) ||
627 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
Simon Kelleycdeda282006-03-16 20:16:06 +0000628 {
629 struct ping_result *r, *victim = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100630 int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
631 ((float)PING_WAIT)));
632
633 *addrp = addr;
634
Simon Kelleycdeda282006-03-16 20:16:06 +0000635 /* check if we failed to ping addr sometime in the last
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636 PING_CACHE_TIME seconds. If so, assume the same situation still exists.
Simon Kelleycdeda282006-03-16 20:16:06 +0000637 This avoids problems when a stupid client bangs
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100638 on us repeatedly. As a final check, if we did more
639 than 60% of the possible ping checks in the last
640 PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000641 for (count = 0, r = daemon->ping_results; r; r = r->next)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100642 if (difftime(now, r->time) > (float)PING_CACHE_TIME)
Simon Kelleycdeda282006-03-16 20:16:06 +0000643 victim = r; /* old record */
Simon Kelley7de060b2011-08-26 17:24:52 +0100644 else
645 {
646 count++;
647 if (r->addr.s_addr == addr.s_addr)
648 {
649 /* consec-ip mode: we offered this address for another client
650 (different hash) recently, don't offer it to this one. */
651 if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
652 break;
653
654 return 1;
655 }
656 }
657
658 if (!r)
Simon Kelley3d8df262005-08-29 12:19:27 +0100659 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100660 if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
Simon Kelleycdeda282006-03-16 20:16:06 +0000661 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100662 /* address in use: perturb address selection so that we are
663 less likely to try this address again. */
664 if (!option_bool(OPT_CONSEC_ADDR))
665 c->addr_epoch++;
666 }
667 else
668 {
669 /* at this point victim may hold an expired record */
670 if (!victim)
Simon Kelleycdeda282006-03-16 20:16:06 +0000671 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100672 if ((victim = whine_malloc(sizeof(struct ping_result))))
673 {
674 victim->next = daemon->ping_results;
675 daemon->ping_results = victim;
676 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000677 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100678
679 /* record that this address is OK for 30s
680 without more ping checks */
681 if (victim)
682 {
683 victim->addr = addr;
684 victim->time = now;
685 victim->hash = j;
686 }
687 return 1;
Simon Kelleycdeda282006-03-16 20:16:06 +0000688 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100689 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000690 }
Simon Kelley3be34542004-09-11 19:12:13 +0100691
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
693
694 if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
695 addr = c->start;
696
697 } while (addr.s_addr != start.s_addr);
698 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100699
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000700 return 0;
701}
702
703static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
704{
Simon Kelleyb8187c82005-11-26 21:46:27 +0000705 if (!context) /* called via find_config() from lease_update_from_configs() */
Simon Kelley0a852542005-03-23 20:28:59 +0000706 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100707 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000708 return 1;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000709 for (; context; context = context->current)
710 if (is_same_net(config->addr, context->start, context->netmask))
711 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000712
713 return 0;
714}
715
Simon Kelley9009d742008-11-14 20:04:27 +0000716int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
717{
718 struct hwaddr_config *conf_addr;
719
720 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
721 if (conf_addr->wildcard_mask == 0 &&
722 conf_addr->hwaddr_len == len &&
723 (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
724 memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
725 return 1;
726
727 return 0;
728}
Simon Kelley0a852542005-03-23 20:28:59 +0000729
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000730struct dhcp_config *find_config(struct dhcp_config *configs,
731 struct dhcp_context *context,
732 unsigned char *clid, int clid_len,
Simon Kelleycdeda282006-03-16 20:16:06 +0000733 unsigned char *hwaddr, int hw_len,
734 int hw_type, char *hostname)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000735{
Simon Kelley7622fc02009-06-04 20:32:05 +0100736 int count, new;
737 struct dhcp_config *config, *candidate;
Simon Kelley9009d742008-11-14 20:04:27 +0000738 struct hwaddr_config *conf_addr;
739
Simon Kelley0a852542005-03-23 20:28:59 +0000740 if (clid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000741 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100742 if (config->flags & CONFIG_CLID)
743 {
744 if (config->clid_len == clid_len &&
745 memcmp(config->clid, clid, clid_len) == 0 &&
746 is_addr_in_context(context, config))
747 return config;
748
749 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
750 cope with that here */
751 if (*clid == 0 && config->clid_len == clid_len-1 &&
752 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
753 is_addr_in_context(context, config))
754 return config;
755 }
756
Simon Kelley0a852542005-03-23 20:28:59 +0000757
Simon Kelleycdeda282006-03-16 20:16:06 +0000758 for (config = configs; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000759 if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000760 is_addr_in_context(context, config))
761 return config;
Simon Kelley3d8df262005-08-29 12:19:27 +0100762
Simon Kelley0a852542005-03-23 20:28:59 +0000763 if (hostname && context)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000764 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100765 if ((config->flags & CONFIG_NAME) &&
766 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000767 is_addr_in_context(context, config))
768 return config;
Simon Kelley7622fc02009-06-04 20:32:05 +0100769
Simon Kelley4cb1b322012-02-06 14:30:41 +0000770 /* use match with fewest wildcard octets */
Simon Kelley7622fc02009-06-04 20:32:05 +0100771 for (candidate = NULL, count = 0, config = configs; config; config = config->next)
772 if (is_addr_in_context(context, config))
773 for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
774 if (conf_addr->wildcard_mask != 0 &&
775 conf_addr->hwaddr_len == hw_len &&
776 (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
777 (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
778 {
779 count = new;
780 candidate = config;
781 }
782
783 return candidate;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000784}
785
Simon Kelley5aabfc72007-08-29 11:24:47 +0100786void dhcp_read_ethers(void)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000787{
Simon Kelley44a2a312004-03-10 20:04:35 +0000788 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley0a852542005-03-23 20:28:59 +0000789 unsigned int flags;
Simon Kelley3be34542004-09-11 19:12:13 +0100790 char *buff = daemon->namebuff;
Simon Kelley33820b72004-04-03 21:10:00 +0100791 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000792 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100793 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley849a8352006-06-09 21:02:31 +0100794 struct dhcp_config **up, *tmp;
Simon Kelley16972692006-10-16 20:04:18 +0100795 struct dhcp_config *config;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000796 int count = 0, lineno = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100797
798 addr.s_addr = 0; /* eliminate warning */
Simon Kelley44a2a312004-03-10 20:04:35 +0000799
800 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100801 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100802 my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
Simon Kelley3be34542004-09-11 19:12:13 +0100803 return;
Simon Kelley33820b72004-04-03 21:10:00 +0100804 }
805
Simon Kelley849a8352006-06-09 21:02:31 +0100806 /* This can be called again on SIGHUP, so remove entries created last time round. */
Simon Kelley16972692006-10-16 20:04:18 +0100807 for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100808 {
809 tmp = config->next;
810 if (config->flags & CONFIG_FROM_ETHERS)
811 {
812 *up = tmp;
813 /* cannot have a clid */
814 if (config->flags & CONFIG_NAME)
815 free(config->hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000816 free(config->hwaddr);
Simon Kelley849a8352006-06-09 21:02:31 +0100817 free(config);
818 }
819 else
820 up = &config->next;
821 }
822
Simon Kelley44a2a312004-03-10 20:04:35 +0000823 while (fgets(buff, MAXDNAME, f))
824 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100825 char *host = NULL;
826
Simon Kelleyb8187c82005-11-26 21:46:27 +0000827 lineno++;
828
Simon Kelley824af852008-02-12 20:43:05 +0000829 while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000830 buff[strlen(buff)-1] = 0;
831
Simon Kelley73a08a22009-02-05 20:28:08 +0000832 if ((*buff == '#') || (*buff == '+') || (*buff == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000833 continue;
834
Simon Kelley824af852008-02-12 20:43:05 +0000835 for (ip = buff; *ip && !isspace((int)*ip); ip++);
836 for(; *ip && isspace((int)*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000837 *ip = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000838 if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000839 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100840 my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000841 continue;
842 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000843
844 /* check for name or dotted-quad */
845 for (cp = ip; *cp; cp++)
846 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
847 break;
848
849 if (!*cp)
850 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000851 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000852 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100853 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000854 continue;
855 }
856
Simon Kelley33820b72004-04-03 21:10:00 +0100857 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000858
Simon Kelley16972692006-10-16 20:04:18 +0100859 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100860 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000861 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000862 }
863 else
864 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100865 int nomem;
866 if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000867 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100868 if (!nomem)
869 my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
870 free(host);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000871 continue;
872 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100873
Simon Kelley33820b72004-04-03 21:10:00 +0100874 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000875
Simon Kelley16972692006-10-16 20:04:18 +0100876 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley1f15b812009-10-13 17:49:32 +0100877 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
Simon Kelley1cff1662004-03-12 08:12:58 +0000878 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000879 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100880
881 if (config && (config->flags & CONFIG_FROM_ETHERS))
882 {
883 my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
884 continue;
885 }
886
Simon Kelley1cff1662004-03-12 08:12:58 +0000887 if (!config)
888 {
Simon Kelley16972692006-10-16 20:04:18 +0100889 for (config = daemon->dhcp_conf; config; config = config->next)
Simon Kelley9009d742008-11-14 20:04:27 +0000890 {
891 struct hwaddr_config *conf_addr = config->hwaddr;
892 if (conf_addr &&
893 conf_addr->next == NULL &&
894 conf_addr->wildcard_mask == 0 &&
895 conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
896 (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
897 memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
898 break;
899 }
Simon Kelley33820b72004-04-03 21:10:00 +0100900
901 if (!config)
902 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100903 if (!(config = whine_malloc(sizeof(struct dhcp_config))))
Simon Kelley33820b72004-04-03 21:10:00 +0100904 continue;
Simon Kelley849a8352006-06-09 21:02:31 +0100905 config->flags = CONFIG_FROM_ETHERS;
Simon Kelley9009d742008-11-14 20:04:27 +0000906 config->hwaddr = NULL;
907 config->domain = NULL;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100908 config->netid = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100909 config->next = daemon->dhcp_conf;
910 daemon->dhcp_conf = config;
Simon Kelley33820b72004-04-03 21:10:00 +0100911 }
912
913 config->flags |= flags;
914
915 if (flags & CONFIG_NAME)
916 {
Simon Kelley1f15b812009-10-13 17:49:32 +0100917 config->hostname = host;
918 host = NULL;
Simon Kelley33820b72004-04-03 21:10:00 +0100919 }
920
921 if (flags & CONFIG_ADDR)
922 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000923 }
Simon Kelley33820b72004-04-03 21:10:00 +0100924
Simon Kelley9009d742008-11-14 20:04:27 +0000925 config->flags |= CONFIG_NOCLID;
926 if (!config->hwaddr)
927 config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
928 if (config->hwaddr)
929 {
930 memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
931 config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
932 config->hwaddr->hwaddr_type = ARPHRD_ETHER;
933 config->hwaddr->wildcard_mask = 0;
934 config->hwaddr->next = NULL;
935 }
Simon Kelley33820b72004-04-03 21:10:00 +0100936 count++;
Simon Kelley1f15b812009-10-13 17:49:32 +0100937
938 free(host);
939
Simon Kelley44a2a312004-03-10 20:04:35 +0000940 }
941
942 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100943
Simon Kelley7622fc02009-06-04 20:32:05 +0100944 my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000945}
946
Simon Kelley44a2a312004-03-10 20:04:35 +0000947
Simon Kelleybb01cb92004-12-13 20:56:23 +0000948/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
949 for this address. If it has a domain part, that must match the set domain and
Simon Kelley1f15b812009-10-13 17:49:32 +0100950 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100951 so check here that the domain name is legal as a hostname.
952 NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100953char *host_from_dns(struct in_addr addr)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000954{
Simon Kelley824af852008-02-12 20:43:05 +0000955 struct crec *lookup;
Simon Kelley824af852008-02-12 20:43:05 +0000956
957 if (daemon->port == 0)
958 return NULL; /* DNS disabled. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000959
Simon Kelley824af852008-02-12 20:43:05 +0000960 lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100961
Simon Kelleybb01cb92004-12-13 20:56:23 +0000962 if (lookup && (lookup->flags & F_HOSTS))
963 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100964 char *dot, *hostname = cache_get_name(lookup);
965 dot = strchr(hostname, '.');
966
967 if (dot && strlen(dot+1) != 0)
968 {
969 char *d2 = get_domain(addr);
970 if (!d2 || !hostname_isequal(dot+1, d2))
971 return NULL; /* wrong domain */
972 }
973
974 if (!legal_hostname(hostname))
975 return NULL;
976
977 strncpy(daemon->dhcp_buff, hostname, 256);
978 daemon->dhcp_buff[255] = 0;
979 strip_hostname(daemon->dhcp_buff);
980
981 return daemon->dhcp_buff;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000982 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100983
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100984 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000985}
986
Simon Kelley7622fc02009-06-04 20:32:05 +0100987#endif
Simon Kelley9009d742008-11-14 20:04:27 +0000988