blob: f667f95bfa8c3c531ffe41a42f2a990986d0b79d [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
2
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
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13/* Author's email: simon@thekelleys.org.uk */
14
15#include "dnsmasq.h"
16
Simon Kelleydfa666f2004-08-02 18:27:27 +010017void dhcp_init(int *fdp, int* rfdp, struct dhcp_config *configs)
Simon Kelley44a2a312004-03-10 20:04:35 +000018{
19 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
20 struct sockaddr_in saddr;
21 int opt = 1;
Simon Kelleydfa666f2004-08-02 18:27:27 +010022 struct dhcp_config *cp;
23
Simon Kelley44a2a312004-03-10 20:04:35 +000024 if (fd == -1)
Simon Kelley1cff1662004-03-12 08:12:58 +000025 die ("cannot create DHCP socket : %s", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000026
27 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
28#if defined(IP_PKTINFO)
29 setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
30#elif defined(IP_RECVIF)
31 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
32#endif
33 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
34 die("failed to set options on DHCP socket: %s", NULL);
35
36 saddr.sin_family = AF_INET;
37 saddr.sin_port = htons(DHCP_SERVER_PORT);
38 saddr.sin_addr.s_addr = INADDR_ANY;
39 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
40 die("failed to bind DHCP server socket: %s", NULL);
41
42 *fdp = fd;
43
44#ifdef HAVE_BPF
45 opt = 0;
46 while (1)
47 {
48 char filename[50];
49 sprintf(filename, "/dev/bpf%d", opt++);
50 if ((fd = open(filename, O_RDWR, 0)) != -1)
51 break;
52 if (errno != EBUSY)
Simon Kelley1cff1662004-03-12 08:12:58 +000053 die("cannot create DHCP BPF socket: %s", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000054 }
55#else
56 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
Simon Kelley1cff1662004-03-12 08:12:58 +000057 die("cannot create DHCP packet socket: %s", NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000058#endif
59
60 *rfdp = fd;
Simon Kelleydfa666f2004-08-02 18:27:27 +010061
62 /* If the same IP appears in more than one host config, then DISCOVER
63 for one of the hosts will get the address, but REQUEST will be NAKed,
64 since the address is reserved by the other one -> protocol loop. */
65 for (; configs; configs = configs->next)
66 for (cp = configs->next; cp; cp = cp->next)
67 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
68 die("Duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
Simon Kelley44a2a312004-03-10 20:04:35 +000069}
70
71void dhcp_packet(struct dhcp_context *contexts, char *packet,
Simon Kelleya84fa1d2004-04-23 22:21:21 +010072 struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
73 struct dhcp_vendor *vendors,
Simon Kelley9e4abcb2004-01-22 19:47:41 +000074 time_t now, char *namebuff, char *domain_suffix,
75 char *dhcp_file, char *dhcp_sname,
Simon Kelley44a2a312004-03-10 20:04:35 +000076 struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
77 struct iname *names, struct iname *addrs, struct iname *except)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000078{
Simon Kelley44a2a312004-03-10 20:04:35 +000079 struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *)packet;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080 struct dhcp_packet *mess = (struct dhcp_packet *)&rawpacket->data;
Simon Kelley44a2a312004-03-10 20:04:35 +000081 struct dhcp_context *context;
82 struct iname *tmp;
83 struct ifreq ifr;
84 struct msghdr msg;
85 struct iovec iov[2];
86 struct cmsghdr *cmptr;
87 int sz, newlen, iface_index = 0;
Simon Kelley8a911cc2004-03-16 18:35:52 +000088 struct in_addr source, iface_netmask, iface_addr, iface_broadcast;
89 struct in_addr netmask_save, broadcast_save, router;
Simon Kelley44a2a312004-03-10 20:04:35 +000090#ifdef HAVE_BPF
91 unsigned char iface_hwaddr[ETHER_ADDR_LEN];
92#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000093
Simon Kelley44a2a312004-03-10 20:04:35 +000094 union {
95 struct cmsghdr align; /* this ensures alignment */
96#ifdef IP_PKTINFO
97 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
98#else
99 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
100#endif
101 } control_u;
102
103 iov[0].iov_base = (char *)&rawpacket->data;
104 iov[0].iov_len = DNSMASQ_PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr));
105
106 msg.msg_control = control_u.control;
107 msg.msg_controllen = sizeof(control_u);
108 msg.msg_flags = 0;
109 msg.msg_name = NULL;
110 msg.msg_namelen = 0;
111 msg.msg_iov = iov;
112 msg.msg_iovlen = 1;
113
114 sz = recvmsg(dhcp_fd, &msg, 0);
115
116 if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
117 return;
118
119#if defined (IP_PKTINFO)
120 if (msg.msg_controllen < sizeof(struct cmsghdr))
121 return;
122 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
123 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
124 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
125
Simon Kelley8a911cc2004-03-16 18:35:52 +0000126 if (!(ifr.ifr_ifindex = iface_index) ||
127 ioctl(dhcp_fd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000128 return;
129
130#elif defined(IP_RECVIF)
131 if (msg.msg_controllen < sizeof(struct cmsghdr))
132 return;
133 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
134 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
135 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
136
137 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
138 return;
139
140#else
Simon Kelleyde379512004-06-22 20:23:33 +0100141 while (names->isloop)
142 names = names->next;
143 strcpy(ifr.ifr_name, names->name);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000144#endif
145
146#ifdef HAVE_BPF
Simon Kelley44a2a312004-03-10 20:04:35 +0000147 ifr.ifr_addr.sa_family = AF_LINK;
148 if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0)
149 return;
150 memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
151#endif
152
153 ifr.ifr_addr.sa_family = AF_INET;
154 if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0 )
155 return;
156 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
157
158 /* enforce available interface configuration */
159 for (tmp = except; tmp; tmp = tmp->next)
160 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
161 return;
162
163 if (names || addrs)
164 {
165 for (tmp = names; tmp; tmp = tmp->next)
166 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
167 break;
168 if (!tmp)
169 for (tmp = addrs; tmp; tmp = tmp->next)
170 if (tmp->addr.sa.sa_family == AF_INET &&
171 tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
172 break;
173 if (!tmp)
174 return;
175 }
176
177 /* If the packet came via a relay, use that address to look up the context,
178 else use the address of the interface is arrived on. */
179 source = mess->giaddr.s_addr ? mess->giaddr : iface_addr;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000180
181 iface_netmask.s_addr = 0;
182 iface_broadcast.s_addr = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000183
Simon Kelley8a911cc2004-03-16 18:35:52 +0000184 if (ioctl(dhcp_fd, SIOCGIFNETMASK, &ifr) != -1)
185 {
186 iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
187 /* we can use the interface netmask if either the packet came direct,
188 or it came via a relay listening on the same network. This sounds unlikely,
189 but it happens with win4lin. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100190 if (!is_same_net(source, iface_addr, iface_netmask))
Simon Kelley8a911cc2004-03-16 18:35:52 +0000191 iface_netmask.s_addr = 0;
192 else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
193 iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
194
195 }
196
Simon Kelley44a2a312004-03-10 20:04:35 +0000197 for (context = contexts; context; context = context->next)
198 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000199 struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask;
200
201 if (netmask.s_addr &&
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100202 is_same_net(source, context->start, netmask) &&
203 is_same_net(source, context->end, netmask))
Simon Kelley44a2a312004-03-10 20:04:35 +0000204 break;
205 }
206
207 if (!context)
208 {
209 syslog(LOG_WARNING, "no address range available for DHCP request via %s", inet_ntoa(source));
210 return;
211 }
212
213 netmask_save = context->netmask;
214 broadcast_save = context->broadcast;
215
Simon Kelley8a911cc2004-03-16 18:35:52 +0000216 if (!context->netmask.s_addr)
217 context->netmask = iface_netmask;
Simon Kelley44a2a312004-03-10 20:04:35 +0000218
219 if (!context->broadcast.s_addr)
220 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000221 if (iface_broadcast.s_addr)
222 context->broadcast = iface_broadcast;
Simon Kelley44a2a312004-03-10 20:04:35 +0000223 else
Simon Kelley8a911cc2004-03-16 18:35:52 +0000224 context->broadcast.s_addr = (source.s_addr & context->netmask.s_addr) | ~context->netmask.s_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000225 }
226
227 if (ioctl(dhcp_fd, SIOCGIFMTU, &ifr) == -1)
228 ifr.ifr_mtu = ETHERMTU;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000229
230 /* Normally, we set the default route to point to the machine which is getting the
231 DHCP broadcast, either this machine or a relay. In the special case that the relay
232 is on the same network as us, we set the default route to us, not the relay.
233 This is the win4lin scenario again. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100234 if (is_same_net(source, iface_addr, context->netmask))
Simon Kelley8a911cc2004-03-16 18:35:52 +0000235 router = iface_addr;
236 else
237 router = source;
Simon Kelley44a2a312004-03-10 20:04:35 +0000238
239 lease_prune(NULL, now); /* lose any expired leases */
240 newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu,
241 rawpacket, sz, now, namebuff,
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100242 dhcp_opts, dhcp_configs, vendors, domain_suffix,
243 dhcp_file, dhcp_sname, dhcp_next_server, router);
Simon Kelley44a2a312004-03-10 20:04:35 +0000244 lease_update_file(0, now);
245 lease_update_dns();
246
247 context->netmask = netmask_save;
248 context->broadcast = broadcast_save;
249
250 if (newlen == 0)
251 return;
252
253 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
254 {
255 /* To send to BOOTP relay or configured client, use
256 the IP packet */
257
258 struct sockaddr_in dest;
259 dest.sin_family = AF_INET;
260
261 if (mess->giaddr.s_addr)
262 {
263 dest.sin_port = htons(DHCP_SERVER_PORT);
264 dest.sin_addr = mess->giaddr;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000265 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000266 else
267 {
268 dest.sin_port = htons(DHCP_CLIENT_PORT);
269 dest.sin_addr = mess->ciaddr;
270 }
271
272 sendto(dhcp_fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
273 }
274 else
275 {
276 /* Hairy stuff, packet either has to go to the
277 net broadcast or the destination can't reply to ARP yet,
278 but we do know the physical address.
279 Build the packet by steam, and send directly, bypassing
280 the kernel IP stack */
281
282 u32 i, sum;
283 unsigned char hwdest[ETHER_ADDR_LEN];
284
285 if (ntohs(mess->flags) & 0x8000)
286 {
287 memset(hwdest, 255, ETHER_ADDR_LEN);
288 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
289 }
290 else
291 {
292 memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
293 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
294 }
295
296 rawpacket->ip.ip_p = IPPROTO_UDP;
297 rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
298 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
299 sizeof(struct udphdr) +
300 newlen) ;
301 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
302 rawpacket->ip.ip_v = IPVERSION;
303 rawpacket->ip.ip_tos = 0;
304 rawpacket->ip.ip_id = htons(0);
305 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
306 rawpacket->ip.ip_ttl = IPDEFTTL;
307 rawpacket->ip.ip_sum = 0;
308 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
309 sum += ((u16 *)&rawpacket->ip)[i];
310 while (sum>>16)
311 sum = (sum & 0xffff) + (sum >> 16);
312 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
313
314 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
315 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
316 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
317 rawpacket->udp.uh_sum = 0;
318 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
319 sum += htons(IPPROTO_UDP);
320 for (i = 0; i < 4; i++)
321 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
322 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
323 sum += ((u16 *)&rawpacket->udp)[i];
324 while (sum>>16)
325 sum = (sum & 0xffff) + (sum >> 16);
326 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
327
328 {
329#ifdef HAVE_BPF
330 struct ether_header header;
331
332 header.ether_type = htons(ETHERTYPE_IP);
333 memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
334 memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
335
336 ioctl(raw_fd, BIOCSETIF, &ifr);
337
338 iov[0].iov_base = (char *)&header;
339 iov[0].iov_len = sizeof(struct ether_header);
340 iov[1].iov_base = (char *)rawpacket;
341 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
342 writev(raw_fd, iov, 2);
343#else
344 struct sockaddr_ll dest;
345
346 dest.sll_family = AF_PACKET;
347 dest.sll_halen = ETHER_ADDR_LEN;
348 dest.sll_ifindex = iface_index;
349 dest.sll_protocol = htons(ETHERTYPE_IP);
350 memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
351 sendto(raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
352 0, (struct sockaddr *)&dest, sizeof(dest));
353
354#endif
355 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356 }
357}
358
359int address_available(struct dhcp_context *context, struct in_addr taddr)
360{
361 /* Check is an address is OK for this network, ie
362 within allowable range and not in an existing lease */
363
364 unsigned int addr, start, end;
365
366 addr = ntohl(taddr.s_addr);
367 start = ntohl(context->start.s_addr);
368 end = ntohl(context->end.s_addr);
369
Simon Kelley33820b72004-04-03 21:10:00 +0100370 /* static leases only. */
371 if (start == end)
372 return 0;
373
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000374 if (addr < start)
375 return 0;
376
377 if (addr > end)
378 return 0;
379
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000380 return 1;
381}
Simon Kelleydfa666f2004-08-02 18:27:27 +0100382
383struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
384{
385 struct dhcp_config *config;
386
387 for (config = configs; config; config = config->next)
388 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
389 return config;
390
391 return NULL;
392}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000393
394int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100395 struct in_addr *addrp, unsigned char *hwaddr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000396{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100397 /* Find a free address: exclude anything in use and anything allocated to
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000398 a particular hwaddr/clientid/hostname in our configuration */
399
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100400 struct in_addr start, addr ;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100401 unsigned int i, j;
Simon Kelley33820b72004-04-03 21:10:00 +0100402
403 /* start == end means no dynamic leases. */
404 if (context->end.s_addr == context->start.s_addr)
405 return 0;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100406
407 /* pick a seed based on hwaddr then iterate until we find a free address. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100408 for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100409 j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
410
411 start.s_addr = addr.s_addr =
412 htonl(ntohl(context->start.s_addr) +
413 (j % (ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
414
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000415 do {
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100416 if (addr.s_addr == context->end.s_addr)
417 addr = context->start;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000418 else
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100419 addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000420
421
Simon Kelleydfa666f2004-08-02 18:27:27 +0100422 if (!lease_find_by_addr(addr) && !config_find_by_address(configs, addr))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000423 {
Simon Kelleydfa666f2004-08-02 18:27:27 +0100424 *addrp = addr;
425 return 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000426 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100427 } while (addr.s_addr != start.s_addr);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000428
429 return 0;
430}
431
432static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
433{
434 if (!context)
435 return 1;
Simon Kelley33820b72004-04-03 21:10:00 +0100436 if (!(config->flags & CONFIG_ADDR))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000437 return 1;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100438 if (is_same_net(config->addr, context->start, context->netmask))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000439 return 1;
440
441 return 0;
442}
443
444struct dhcp_config *find_config(struct dhcp_config *configs,
445 struct dhcp_context *context,
446 unsigned char *clid, int clid_len,
447 unsigned char *hwaddr, char *hostname)
448{
449 struct dhcp_config *config;
450
451 if (clid_len)
452 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100453 if (config->flags & CONFIG_CLID)
454 {
455 if (config->clid_len == clid_len &&
456 memcmp(config->clid, clid, clid_len) == 0 &&
457 is_addr_in_context(context, config))
458 return config;
459
460 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
461 cope with that here */
462 if (*clid == 0 && config->clid_len == clid_len-1 &&
463 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
464 is_addr_in_context(context, config))
465 return config;
466 }
467
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000468 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100469 if ((config->flags & CONFIG_HWADDR) &&
470 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000471 is_addr_in_context(context, config))
472 return config;
473
474 if (hostname)
475 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100476 if ((config->flags & CONFIG_NAME) &&
477 hostname_isequal(config->hostname, hostname) &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000478 is_addr_in_context(context, config))
479 return config;
480
481 return NULL;
482}
483
Simon Kelley44a2a312004-03-10 20:04:35 +0000484struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000485{
Simon Kelley44a2a312004-03-10 20:04:35 +0000486 FILE *f = fopen(ETHERSFILE, "r");
Simon Kelley33820b72004-04-03 21:10:00 +0100487 unsigned int flags, e0, e1, e2, e3, e4, e5;
488 char *ip, *cp;
Simon Kelley44a2a312004-03-10 20:04:35 +0000489 struct in_addr addr;
Simon Kelley33820b72004-04-03 21:10:00 +0100490 unsigned char hwaddr[ETHER_ADDR_LEN];
Simon Kelley1cff1662004-03-12 08:12:58 +0000491 struct dhcp_config *config;
Simon Kelley33820b72004-04-03 21:10:00 +0100492 int count = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000493
494 if (!f)
Simon Kelley33820b72004-04-03 21:10:00 +0100495 {
496 syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
497 return configs;
498 }
499
Simon Kelley44a2a312004-03-10 20:04:35 +0000500 while (fgets(buff, MAXDNAME, f))
501 {
Simon Kelley33820b72004-04-03 21:10:00 +0100502 while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
Simon Kelley44a2a312004-03-10 20:04:35 +0000503 buff[strlen(buff)-1] = 0;
504
505 if ((*buff == '#') || (*buff == '+'))
506 continue;
507
Simon Kelley33820b72004-04-03 21:10:00 +0100508 for (ip = buff; *ip && !isspace(*ip); ip++);
509 for(; *ip && isspace(*ip); ip++)
Simon Kelley44a2a312004-03-10 20:04:35 +0000510 *ip = 0;
511 if (!*ip)
512 continue;
513
514 if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
515 continue;
516
Simon Kelley33820b72004-04-03 21:10:00 +0100517 hwaddr[0] = e0;
518 hwaddr[1] = e1;
519 hwaddr[2] = e2;
520 hwaddr[3] = e3;
521 hwaddr[4] = e4;
522 hwaddr[5] = e5;
523
Simon Kelley44a2a312004-03-10 20:04:35 +0000524 /* check for name or dotted-quad */
525 for (cp = ip; *cp; cp++)
526 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
527 break;
528
529 if (!*cp)
530 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000531 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
Simon Kelley1cff1662004-03-12 08:12:58 +0000532 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100533 flags = CONFIG_ADDR;
Simon Kelley1cff1662004-03-12 08:12:58 +0000534
535 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100536 if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
Simon Kelley1cff1662004-03-12 08:12:58 +0000537 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000538 }
539 else
540 {
Simon Kelley1cff1662004-03-12 08:12:58 +0000541 if (!canonicalise(ip))
542 continue;
Simon Kelley33820b72004-04-03 21:10:00 +0100543 flags = CONFIG_NAME;
Simon Kelley1cff1662004-03-12 08:12:58 +0000544
545 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100546 if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
Simon Kelley1cff1662004-03-12 08:12:58 +0000547 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000548 }
549
Simon Kelley1cff1662004-03-12 08:12:58 +0000550 if (!config)
551 {
Simon Kelley33820b72004-04-03 21:10:00 +0100552 for (config = configs; config; config = config->next)
553 if ((config->flags & CONFIG_HWADDR) &&
554 memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
555 break;
556
557 if (!config)
558 {
559 if (!(config = malloc(sizeof(struct dhcp_config))))
560 continue;
561 config->flags = 0;
562 config->next = configs;
563 configs = config;
564 }
565
566 config->flags |= flags;
567
568 if (flags & CONFIG_NAME)
569 {
570 if ((config->hostname = malloc(strlen(ip)+1)))
571 strcpy(config->hostname, ip);
572 else
573 config->flags &= ~CONFIG_NAME;
574 }
575
576 if (flags & CONFIG_ADDR)
577 config->addr = addr;
Simon Kelley1cff1662004-03-12 08:12:58 +0000578 }
Simon Kelley33820b72004-04-03 21:10:00 +0100579
Simon Kelleyde379512004-06-22 20:23:33 +0100580 config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
Simon Kelley33820b72004-04-03 21:10:00 +0100581 memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
Simon Kelley1cff1662004-03-12 08:12:58 +0000582
Simon Kelley33820b72004-04-03 21:10:00 +0100583 count++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000584 }
585
586 fclose(f);
Simon Kelley33820b72004-04-03 21:10:00 +0100587
588 syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
Simon Kelley44a2a312004-03-10 20:04:35 +0000589 return configs;
590}
591
592void dhcp_update_configs(struct dhcp_config *configs)
593{
594 /* Some people like to keep all static IP addresses in /etc/hosts.
595 This goes through /etc/hosts and sets static addresses for any DHCP config
596 records which don't have an address and whose name matches. */
597
Simon Kelley1ab84e22004-01-29 16:48:35 +0000598 struct dhcp_config *config;
599 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000600
Simon Kelley1ab84e22004-01-29 16:48:35 +0000601 for (config = configs; config; config = config->next)
Simon Kelley33820b72004-04-03 21:10:00 +0100602 if (!(config->flags & CONFIG_ADDR) &&
603 (config->flags & CONFIG_NAME) &&
Simon Kelley1ab84e22004-01-29 16:48:35 +0000604 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
605 (crec->flags & F_HOSTS))
Simon Kelley33820b72004-04-03 21:10:00 +0100606 {
607 config->addr = crec->addr.addr.addr4;
608 config->flags |= CONFIG_ADDR;
609 }
Simon Kelley1ab84e22004-01-29 16:48:35 +0000610}
Simon Kelley44a2a312004-03-10 20:04:35 +0000611