blob: 619a662ac5ea41fd5cc481dbc7414891e934c568 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01002
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 Kelley5e9e0ef2006-04-17 14:24:29 +01008 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 Kelley5e9e0ef2006-04-17 14:24:29 +010015*/
16
17#include "dnsmasq.h"
18
Simon Kelley824af852008-02-12 20:43:05 +000019#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
Simon Kelley70969c12012-03-07 20:46:28 +000020#include <ifaddrs.h>
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021
Simon Kelley28866e92011-02-14 20:19:14 +000022#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
Simon Kelley132255b2012-08-06 20:12:04 +010023#include <sys/param.h>
Simon Kelley28866e92011-02-14 20:19:14 +000024#include <sys/sysctl.h>
25#include <net/route.h>
26#include <net/if_dl.h>
27#include <netinet/if_ether.h>
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +010028#if defined(__FreeBSD__)
29# include <net/if_var.h>
30#endif
31#include <netinet/in_var.h>
Simon Kelley28866e92011-02-14 20:19:14 +000032
Simon Kelley7de060b2011-08-26 17:24:52 +010033#ifndef SA_SIZE
34#define SA_SIZE(sa) \
35 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
36 sizeof(long) : \
37 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
38#endif
39
Simon Kelley28866e92011-02-14 20:19:14 +000040int arp_enumerate(void *parm, int (*callback)())
41{
42 int mib[6];
43 size_t needed;
44 char *next;
45 struct rt_msghdr *rtm;
46 struct sockaddr_inarp *sin2;
47 struct sockaddr_dl *sdl;
Simon Kelley08456c62012-03-07 19:08:11 +000048 struct iovec buff;
Simon Kelley28866e92011-02-14 20:19:14 +000049 int rc;
Simon Kelley08456c62012-03-07 19:08:11 +000050
51 buff.iov_base = NULL;
Simon Kelley96fafe22012-03-07 20:25:39 +000052 buff.iov_len = 0;
Simon Kelley08456c62012-03-07 19:08:11 +000053
Simon Kelley28866e92011-02-14 20:19:14 +000054 mib[0] = CTL_NET;
55 mib[1] = PF_ROUTE;
56 mib[2] = 0;
57 mib[3] = AF_INET;
58 mib[4] = NET_RT_FLAGS;
59#ifdef RTF_LLINFO
60 mib[5] = RTF_LLINFO;
61#else
62 mib[5] = 0;
63#endif
64 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
65 return 0;
66
67 while (1)
68 {
Simon Kelley08456c62012-03-07 19:08:11 +000069 if (!expand_buf(&buff, needed))
Simon Kelley28866e92011-02-14 20:19:14 +000070 return 0;
Simon Kelley08456c62012-03-07 19:08:11 +000071 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
Simon Kelley28866e92011-02-14 20:19:14 +000072 errno != ENOMEM)
73 break;
74 needed += needed / 8;
75 }
76 if (rc == -1)
77 return 0;
78
Simon Kelley08456c62012-03-07 19:08:11 +000079 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
Simon Kelley28866e92011-02-14 20:19:14 +000080 {
81 rtm = (struct rt_msghdr *)next;
82 sin2 = (struct sockaddr_inarp *)(rtm + 1);
83 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
84 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
85 return 0;
86 }
87
88 return 1;
89}
Simon Kelley28866e92011-02-14 20:19:14 +000090#endif
91
92
93int iface_enumerate(int family, void *parm, int (*callback)())
Simon Kelley824af852008-02-12 20:43:05 +000094{
Simon Kelley08456c62012-03-07 19:08:11 +000095 struct ifaddrs *head, *addrs;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +010096 int errsav, fd = -1, ret = 0;
Simon Kelley96fafe22012-03-07 20:25:39 +000097
Simon Kelley28866e92011-02-14 20:19:14 +000098 if (family == AF_UNSPEC)
99#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
100 return arp_enumerate(parm, callback);
101#else
102 return 0; /* need code for Solaris and MacOS*/
103#endif
104
Simon Kelley6aef6002012-02-11 22:01:50 +0000105 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
106 if (family == AF_LOCAL)
107 family = AF_LINK;
108
Simon Kelley08456c62012-03-07 19:08:11 +0000109 if (getifaddrs(&head) == -1)
Simon Kelley824af852008-02-12 20:43:05 +0000110 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +0000111
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100112#if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
113 if (family == AF_INET6)
114 fd = socket(PF_INET6, SOCK_DGRAM, 0);
115#endif
116
Simon Kelley08456c62012-03-07 19:08:11 +0000117 for (addrs = head; addrs; addrs = addrs->ifa_next)
118 {
Simon Kelley96fafe22012-03-07 20:25:39 +0000119 if (addrs->ifa_addr->sa_family == family)
Simon Kelley824af852008-02-12 20:43:05 +0000120 {
Simon Kelley08456c62012-03-07 19:08:11 +0000121 int iface_index = if_nametoindex(addrs->ifa_name);
122
Simon Kelleyfc4c4fd2013-07-26 15:38:59 +0100123 if (iface_index == 0 || !addrs->ifa_addr ||
124 (!addrs->ifa_netmask && family != AF_LINK))
Simon Kelley08456c62012-03-07 19:08:11 +0000125 continue;
126
Simon Kelley28866e92011-02-14 20:19:14 +0000127 if (family == AF_INET)
Simon Kelley824af852008-02-12 20:43:05 +0000128 {
Simon Kelley28866e92011-02-14 20:19:14 +0000129 struct in_addr addr, netmask, broadcast;
Simon Kelley08456c62012-03-07 19:08:11 +0000130 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
131 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
Simon Kelley39f6a042013-01-09 19:57:47 +0000132 if (addrs->ifa_broadaddr)
133 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
134 else
135 broadcast.s_addr = 0;
Simon Kelley3f2873d2013-05-14 11:28:47 +0100136 if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
Simon Kelley28866e92011-02-14 20:19:14 +0000137 goto err;
Simon Kelley824af852008-02-12 20:43:05 +0000138 }
Simon Kelley28866e92011-02-14 20:19:14 +0000139#ifdef HAVE_IPV6
140 else if (family == AF_INET6)
141 {
Simon Kelley08456c62012-03-07 19:08:11 +0000142 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
Simon Kelley96fafe22012-03-07 20:25:39 +0000143 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
144 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
Simon Kelley08456c62012-03-07 19:08:11 +0000145 int i, j, prefix = 0;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100146 u32 valid = 0xffffffff, preferred = 0xffffffff;
147 int flags = 0;
148#ifdef HAVE_BSD_NETWORK
149 struct in6_ifreq ifr6;
150
151 memset(&ifr6, 0, sizeof(ifr6));
152 strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
Simon Kelley08456c62012-03-07 19:08:11 +0000153
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100154 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
155 if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
156 {
157 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
158 flags |= IFACE_TENTATIVE;
159
160 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
161 flags |= IFACE_DEPRECATED;
Vladislav Grishenko4568a6f2013-08-19 16:07:07 +0100162
163 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
164 flags |= IFACE_PERMANENT;
165
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100166 }
167
168 ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
169 if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
170 {
171 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
172 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
173 }
174#endif
175
Simon Kelley96fafe22012-03-07 20:25:39 +0000176 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
Simon Kelley08456c62012-03-07 19:08:11 +0000177 if (netmask[i] != 0xff)
178 break;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100179
Simon Kelley96fafe22012-03-07 20:25:39 +0000180 if (i != IN6ADDRSZ && netmask[i])
Simon Kelley08456c62012-03-07 19:08:11 +0000181 for (j = 7; j > 0; j--, prefix++)
182 if ((netmask[i] & (1 << j)) == 0)
183 break;
184
Simon Kelley28866e92011-02-14 20:19:14 +0000185 /* voodoo to clear interface field in address */
186 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
187 {
188 addr->s6_addr[2] = 0;
189 addr->s6_addr[3] = 0;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100190 }
191
192 if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
193 (int) preferred, (int)valid, parm)))
194 goto err;
195 }
196#endif /* HAVE_IPV6 */
197
Simon Kelley6aef6002012-02-11 22:01:50 +0000198#ifdef HAVE_DHCP6
199 else if (family == AF_LINK)
200 {
201 /* Assume ethernet again here */
Simon Kelley96fafe22012-03-07 20:25:39 +0000202 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
Simon Kelley08456c62012-03-07 19:08:11 +0000203 if (sdl->sdl_alen != 0 &&
204 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
Simon Kelley6aef6002012-02-11 22:01:50 +0000205 goto err;
206 }
207#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000208 }
Simon Kelley824af852008-02-12 20:43:05 +0000209 }
210
211 ret = 1;
212
213 err:
214 errsav = errno;
Vladislav Grishenko8c3bdb42013-08-19 14:04:38 +0100215 freeifaddrs(head);
216 if (fd != -1)
217 close(fd);
Simon Kelley824af852008-02-12 20:43:05 +0000218 errno = errsav;
219
220 return ret;
221}
222#endif
223
224
Simon Kelley7622fc02009-06-04 20:32:05 +0100225#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000226#include <net/bpf.h>
227
Simon Kelley5aabfc72007-08-29 11:24:47 +0100228void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100229{
230 int i = 0;
231
232 while (1)
233 {
Simon Kelley08456c62012-03-07 19:08:11 +0000234 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
235 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
236 return;
237
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100238 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100239 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100240 }
241}
242
Simon Kelley5aabfc72007-08-29 11:24:47 +0100243void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100244 struct in_addr iface_addr, struct ifreq *ifr)
245{
246 /* Hairy stuff, packet either has to go to the
247 net broadcast or the destination can't reply to ARP yet,
248 but we do know the physical address.
249 Build the packet by steam, and send directly, bypassing
250 the kernel IP stack */
251
Simon Kelley849a8352006-06-09 21:02:31 +0100252 struct ether_header ether;
253 struct ip ip;
254 struct udphdr {
255 u16 uh_sport; /* source port */
256 u16 uh_dport; /* destination port */
257 u16 uh_ulen; /* udp length */
258 u16 uh_sum; /* udp checksum */
259 } udp;
260
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100261 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100262 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263
264 /* Only know how to do ethernet on *BSD */
265 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
266 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100267 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100268 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100269 return;
270 }
271
272 ifr->ifr_addr.sa_family = AF_LINK;
273 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
274 return;
275
Simon Kelley849a8352006-06-09 21:02:31 +0100276 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
277 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100278
279 if (ntohs(mess->flags) & 0x8000)
280 {
Simon Kelley849a8352006-06-09 21:02:31 +0100281 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
282 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100283 }
284 else
285 {
Simon Kelley849a8352006-06-09 21:02:31 +0100286 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
287 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100288 }
289
Simon Kelley849a8352006-06-09 21:02:31 +0100290 ip.ip_p = IPPROTO_UDP;
291 ip.ip_src.s_addr = iface_addr.s_addr;
292 ip.ip_len = htons(sizeof(struct ip) +
293 sizeof(struct udphdr) +
294 len) ;
295 ip.ip_hl = sizeof(struct ip) / 4;
296 ip.ip_v = IPVERSION;
297 ip.ip_tos = 0;
298 ip.ip_id = htons(0);
299 ip.ip_off = htons(0x4000); /* don't fragment */
300 ip.ip_ttl = IPDEFTTL;
301 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100302 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100303 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100304 while (sum>>16)
305 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100306 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100307
Simon Kelley9e038942008-05-30 20:06:34 +0100308 udp.uh_sport = htons(daemon->dhcp_server_port);
309 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100310 if (len & 1)
311 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100312 udp.uh_sum = 0;
313 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100314 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000315 sum += ip.ip_src.s_addr & 0xffff;
316 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
317 sum += ip.ip_dst.s_addr & 0xffff;
318 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100319 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100320 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100321 for (i = 0; i < (len + 1) / 2; i++)
322 sum += ((u16 *)mess)[i];
323 while (sum>>16)
324 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100325 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100326
327 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
328
Simon Kelley849a8352006-06-09 21:02:31 +0100329 iov[0].iov_base = &ether;
330 iov[0].iov_len = sizeof(ether);
331 iov[1].iov_base = &ip;
332 iov[1].iov_len = sizeof(ip);
333 iov[2].iov_base = &udp;
334 iov[2].iov_len = sizeof(udp);
335 iov[3].iov_base = mess;
336 iov[3].iov_len = len;
337
338 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100339}
340
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100341#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100342
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100343