blob: 94f6bd9b9b17e69d03e09de38cb7c80807d7e7dc [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 Kelley44a2a312004-03-10 20:04:35 +000017void dhcp_init(int *fdp, int* rfdp)
18{
19 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
20 struct sockaddr_in saddr;
21 int opt = 1;
22
23 if (fd == -1)
24 die ("Cannot create DHCP socket : %s", NULL);
25
26 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
27#if defined(IP_PKTINFO)
28 setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
29#elif defined(IP_RECVIF)
30 setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
31#endif
32 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
33 die("failed to set options on DHCP socket: %s", NULL);
34
35 saddr.sin_family = AF_INET;
36 saddr.sin_port = htons(DHCP_SERVER_PORT);
37 saddr.sin_addr.s_addr = INADDR_ANY;
38 if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
39 die("failed to bind DHCP server socket: %s", NULL);
40
41 *fdp = fd;
42
43#ifdef HAVE_BPF
44 opt = 0;
45 while (1)
46 {
47 char filename[50];
48 sprintf(filename, "/dev/bpf%d", opt++);
49 if ((fd = open(filename, O_RDWR, 0)) != -1)
50 break;
51 if (errno != EBUSY)
52 die("Cannot create DHCP BPF socket: %s", NULL);
53 }
54#else
55 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
56 die("Cannot create DHCP packet socket: %s", NULL);
57#endif
58
59 *rfdp = fd;
60}
61
62void dhcp_packet(struct dhcp_context *contexts, char *packet,
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063 struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
64 time_t now, char *namebuff, char *domain_suffix,
65 char *dhcp_file, char *dhcp_sname,
Simon Kelley44a2a312004-03-10 20:04:35 +000066 struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
67 struct iname *names, struct iname *addrs, struct iname *except)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000068{
Simon Kelley44a2a312004-03-10 20:04:35 +000069 struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *)packet;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000070 struct dhcp_packet *mess = (struct dhcp_packet *)&rawpacket->data;
Simon Kelley44a2a312004-03-10 20:04:35 +000071 struct dhcp_context *context;
72 struct iname *tmp;
73 struct ifreq ifr;
74 struct msghdr msg;
75 struct iovec iov[2];
76 struct cmsghdr *cmptr;
77 int sz, newlen, iface_index = 0;
78 struct in_addr source, real_netmask, iface_addr, netmask_save, broadcast_save;
79#ifdef HAVE_BPF
80 unsigned char iface_hwaddr[ETHER_ADDR_LEN];
81#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000082
Simon Kelley44a2a312004-03-10 20:04:35 +000083 union {
84 struct cmsghdr align; /* this ensures alignment */
85#ifdef IP_PKTINFO
86 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
87#else
88 char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
89#endif
90 } control_u;
91
92 iov[0].iov_base = (char *)&rawpacket->data;
93 iov[0].iov_len = DNSMASQ_PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr));
94
95 msg.msg_control = control_u.control;
96 msg.msg_controllen = sizeof(control_u);
97 msg.msg_flags = 0;
98 msg.msg_name = NULL;
99 msg.msg_namelen = 0;
100 msg.msg_iov = iov;
101 msg.msg_iovlen = 1;
102
103 sz = recvmsg(dhcp_fd, &msg, 0);
104
105 if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
106 return;
107
108#if defined (IP_PKTINFO)
109 if (msg.msg_controllen < sizeof(struct cmsghdr))
110 return;
111 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
112 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
113 iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
114
115 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
116 return;
117
118#elif defined(IP_RECVIF)
119 if (msg.msg_controllen < sizeof(struct cmsghdr))
120 return;
121 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
122 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
123 iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
124
125 if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
126 return;
127
128#else
129 if (!names || !names->name || names->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000130 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000131 syslog(LOG_ERR, "must set exactly one interface on broken systems without IP_RECVIF");
132 return;
133 }
134 else
135 strcpy(ifr.ifr_name, names->name);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000136#endif
137
138#ifdef HAVE_BPF
Simon Kelley44a2a312004-03-10 20:04:35 +0000139 ifr.ifr_addr.sa_family = AF_LINK;
140 if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0)
141 return;
142 memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
143#endif
144
145 ifr.ifr_addr.sa_family = AF_INET;
146 if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0 )
147 return;
148 iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
149
150 /* enforce available interface configuration */
151 for (tmp = except; tmp; tmp = tmp->next)
152 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
153 return;
154
155 if (names || addrs)
156 {
157 for (tmp = names; tmp; tmp = tmp->next)
158 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
159 break;
160 if (!tmp)
161 for (tmp = addrs; tmp; tmp = tmp->next)
162 if (tmp->addr.sa.sa_family == AF_INET &&
163 tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
164 break;
165 if (!tmp)
166 return;
167 }
168
169 /* If the packet came via a relay, use that address to look up the context,
170 else use the address of the interface is arrived on. */
171 source = mess->giaddr.s_addr ? mess->giaddr : iface_addr;
172
173 for (context = contexts; context; context = context->next)
174 {
175 if (!context->netmask.s_addr && !mess->giaddr.s_addr && ioctl(dhcp_fd, SIOCGIFNETMASK, &ifr) != -1)
176 real_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
177 else
178 real_netmask = context->netmask;
179
180 if (real_netmask.s_addr &&
181 (source.s_addr & real_netmask.s_addr) == (context->start.s_addr & real_netmask.s_addr) &&
182 (source.s_addr & real_netmask.s_addr) == (context->end.s_addr & real_netmask.s_addr))
183 break;
184 }
185
186 if (!context)
187 {
188 syslog(LOG_WARNING, "no address range available for DHCP request via %s", inet_ntoa(source));
189 return;
190 }
191
192 netmask_save = context->netmask;
193 broadcast_save = context->broadcast;
194
195 context->netmask = real_netmask;
196
197 if (!context->broadcast.s_addr)
198 {
199 if (mess->giaddr.s_addr)
200 context->broadcast.s_addr = (mess->giaddr.s_addr & real_netmask.s_addr) | ~real_netmask.s_addr;
201 else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
202 context->broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
203 else
204 context->broadcast.s_addr = (iface_addr.s_addr & real_netmask.s_addr) | ~real_netmask.s_addr;
205 }
206
207 if (ioctl(dhcp_fd, SIOCGIFMTU, &ifr) == -1)
208 ifr.ifr_mtu = ETHERMTU;
209
210 lease_prune(NULL, now); /* lose any expired leases */
211 newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu,
212 rawpacket, sz, now, namebuff,
213 dhcp_opts, dhcp_configs, domain_suffix, dhcp_file,
214 dhcp_sname, dhcp_next_server);
215 lease_update_file(0, now);
216 lease_update_dns();
217
218 context->netmask = netmask_save;
219 context->broadcast = broadcast_save;
220
221 if (newlen == 0)
222 return;
223
224 if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
225 {
226 /* To send to BOOTP relay or configured client, use
227 the IP packet */
228
229 struct sockaddr_in dest;
230 dest.sin_family = AF_INET;
231
232 if (mess->giaddr.s_addr)
233 {
234 dest.sin_port = htons(DHCP_SERVER_PORT);
235 dest.sin_addr = mess->giaddr;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000236 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000237 else
238 {
239 dest.sin_port = htons(DHCP_CLIENT_PORT);
240 dest.sin_addr = mess->ciaddr;
241 }
242
243 sendto(dhcp_fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
244 }
245 else
246 {
247 /* Hairy stuff, packet either has to go to the
248 net broadcast or the destination can't reply to ARP yet,
249 but we do know the physical address.
250 Build the packet by steam, and send directly, bypassing
251 the kernel IP stack */
252
253 u32 i, sum;
254 unsigned char hwdest[ETHER_ADDR_LEN];
255
256 if (ntohs(mess->flags) & 0x8000)
257 {
258 memset(hwdest, 255, ETHER_ADDR_LEN);
259 rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
260 }
261 else
262 {
263 memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
264 rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
265 }
266
267 rawpacket->ip.ip_p = IPPROTO_UDP;
268 rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
269 rawpacket->ip.ip_len = htons(sizeof(struct ip) +
270 sizeof(struct udphdr) +
271 newlen) ;
272 rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
273 rawpacket->ip.ip_v = IPVERSION;
274 rawpacket->ip.ip_tos = 0;
275 rawpacket->ip.ip_id = htons(0);
276 rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
277 rawpacket->ip.ip_ttl = IPDEFTTL;
278 rawpacket->ip.ip_sum = 0;
279 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
280 sum += ((u16 *)&rawpacket->ip)[i];
281 while (sum>>16)
282 sum = (sum & 0xffff) + (sum >> 16);
283 rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
284
285 rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
286 rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
287 ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
288 rawpacket->udp.uh_sum = 0;
289 rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
290 sum += htons(IPPROTO_UDP);
291 for (i = 0; i < 4; i++)
292 sum += ((u16 *)&rawpacket->ip.ip_src)[i];
293 for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
294 sum += ((u16 *)&rawpacket->udp)[i];
295 while (sum>>16)
296 sum = (sum & 0xffff) + (sum >> 16);
297 rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
298
299 {
300#ifdef HAVE_BPF
301 struct ether_header header;
302
303 header.ether_type = htons(ETHERTYPE_IP);
304 memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
305 memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
306
307 ioctl(raw_fd, BIOCSETIF, &ifr);
308
309 iov[0].iov_base = (char *)&header;
310 iov[0].iov_len = sizeof(struct ether_header);
311 iov[1].iov_base = (char *)rawpacket;
312 iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
313 writev(raw_fd, iov, 2);
314#else
315 struct sockaddr_ll dest;
316
317 dest.sll_family = AF_PACKET;
318 dest.sll_halen = ETHER_ADDR_LEN;
319 dest.sll_ifindex = iface_index;
320 dest.sll_protocol = htons(ETHERTYPE_IP);
321 memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
322 sendto(raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
323 0, (struct sockaddr *)&dest, sizeof(dest));
324
325#endif
326 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000327 }
328}
329
Simon Kelley44a2a312004-03-10 20:04:35 +0000330
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000331int address_available(struct dhcp_context *context, struct in_addr taddr)
332{
333 /* Check is an address is OK for this network, ie
334 within allowable range and not in an existing lease */
335
336 unsigned int addr, start, end;
337
338 addr = ntohl(taddr.s_addr);
339 start = ntohl(context->start.s_addr);
340 end = ntohl(context->end.s_addr);
341
342 if (addr < start)
343 return 0;
344
345 if (addr > end)
346 return 0;
347
348 if (lease_find_by_addr(taddr))
349 return 0;
350
351 return 1;
352}
353
354int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
355 struct in_addr *addrp)
356{
357 /* Find a free address: exlude anything in use and anything allocated to
358 a particular hwaddr/clientid/hostname in our configuration */
359
360 struct dhcp_config *config;
361 struct in_addr start = context->last;
362
363 do {
364 if (context->last.s_addr == context->end.s_addr)
365 context->last = context->start;
366 else
367 context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
368
369
370 if (!lease_find_by_addr(context->last))
371 {
372 for (config = configs; config; config = config->next)
373 if (config->addr.s_addr == context->last.s_addr)
374 break;
375
376 if (!config)
377 {
378 *addrp = context->last;
379 return 1;
380 }
381 }
382 } while (context->last.s_addr != start.s_addr);
383
384 return 0;
385}
386
387static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
388{
389 if (!context)
390 return 1;
391 if (config->addr.s_addr == 0)
392 return 1;
393 if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
394 return 1;
395
396 return 0;
397}
398
399struct dhcp_config *find_config(struct dhcp_config *configs,
400 struct dhcp_context *context,
401 unsigned char *clid, int clid_len,
402 unsigned char *hwaddr, char *hostname)
403{
404 struct dhcp_config *config;
405
406 if (clid_len)
407 for (config = configs; config; config = config->next)
408 {
409 if (config->clid_len == clid_len &&
410 memcmp(config->clid, clid, clid_len) == 0 &&
411 is_addr_in_context(context, config))
412 return config;
413
414 /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
415 cope with that here */
416 if (*clid == 0 && config->clid_len == clid_len-1 &&
417 memcmp(config->clid, clid+1, clid_len-1) == 0 &&
418 is_addr_in_context(context, config))
419 return config;
420 }
421
422 for (config = configs; config; config = config->next)
423 if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
424 is_addr_in_context(context, config))
425 return config;
426
427 if (hostname)
428 for (config = configs; config; config = config->next)
429 if (config->hostname && strcmp(config->hostname, hostname) == 0 &&
430 is_addr_in_context(context, config))
431 return config;
432
433 return NULL;
434}
435
Simon Kelley44a2a312004-03-10 20:04:35 +0000436struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000437{
Simon Kelley44a2a312004-03-10 20:04:35 +0000438 FILE *f = fopen(ETHERSFILE, "r");
439 unsigned int e0, e1, e2, e3, e4, e5;
440 char *ip, *cp, *name;
441 struct in_addr addr;
442 struct dhcp_config *new;
443
444 if (!f)
445 die("Failed to open " ETHERSFILE ":%s", NULL);
446
447 while (fgets(buff, MAXDNAME, f))
448 {
449 while (strlen(buff) > 0 &&
450 (buff[strlen(buff)-1] == '\n' ||
451 buff[strlen(buff)-1] == ' ' ||
452 buff[strlen(buff)-1] == '\t'))
453 buff[strlen(buff)-1] = 0;
454
455 if ((*buff == '#') || (*buff == '+'))
456 continue;
457
458 for (ip = buff; *ip && *ip != ' '; ip++);
459 for(; *ip && *ip == ' '; ip++)
460 *ip = 0;
461 if (!*ip)
462 continue;
463
464 if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
465 continue;
466
467 /* check for name or dotted-quad */
468 for (cp = ip; *cp; cp++)
469 if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
470 break;
471
472 if (!*cp)
473 {
474 name = NULL;
475 if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
476 continue;
477 }
478 else
479 {
480 name = safe_string_alloc(ip);
481 addr.s_addr = 0;
482 }
483
484 new = safe_malloc(sizeof(struct dhcp_config));
485 new->clid_len = 0;
486 new->clid = NULL;
487 new->hwaddr[0] = e0;
488 new->hwaddr[1] = e1;
489 new->hwaddr[2] = e2;
490 new->hwaddr[3] = e3;
491 new->hwaddr[4] = e4;
492 new->hwaddr[5] = e5;
493 new->hostname = name;
494 new->addr = addr;
495 new->lease_time = 0;
496 new->next = configs;
497
498 configs = new;
499 }
500
501 fclose(f);
502 return configs;
503}
504
505void dhcp_update_configs(struct dhcp_config *configs)
506{
507 /* Some people like to keep all static IP addresses in /etc/hosts.
508 This goes through /etc/hosts and sets static addresses for any DHCP config
509 records which don't have an address and whose name matches. */
510
Simon Kelley1ab84e22004-01-29 16:48:35 +0000511 struct dhcp_config *config;
512 struct crec *crec;
Simon Kelley44a2a312004-03-10 20:04:35 +0000513
Simon Kelley1ab84e22004-01-29 16:48:35 +0000514 for (config = configs; config; config = config->next)
515 if (config->addr.s_addr == 0 && config->hostname &&
516 (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
517 (crec->flags & F_HOSTS))
518 config->addr = crec->addr.addr.addr4;
Simon Kelley44a2a312004-03-10 20:04:35 +0000519
Simon Kelley1ab84e22004-01-29 16:48:35 +0000520}
Simon Kelley44a2a312004-03-10 20:04:35 +0000521