blob: b6fbd90f20d2d789864616f2fd0031d69d2484ec [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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>
28
Simon Kelley7de060b2011-08-26 17:24:52 +010029#ifndef SA_SIZE
30#define SA_SIZE(sa) \
31 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
32 sizeof(long) : \
33 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
34#endif
35
Simon Kelley28866e92011-02-14 20:19:14 +000036int arp_enumerate(void *parm, int (*callback)())
37{
38 int mib[6];
39 size_t needed;
40 char *next;
41 struct rt_msghdr *rtm;
42 struct sockaddr_inarp *sin2;
43 struct sockaddr_dl *sdl;
Simon Kelley08456c62012-03-07 19:08:11 +000044 struct iovec buff;
Simon Kelley28866e92011-02-14 20:19:14 +000045 int rc;
Simon Kelley08456c62012-03-07 19:08:11 +000046
47 buff.iov_base = NULL;
Simon Kelley96fafe22012-03-07 20:25:39 +000048 buff.iov_len = 0;
Simon Kelley08456c62012-03-07 19:08:11 +000049
Simon Kelley28866e92011-02-14 20:19:14 +000050 mib[0] = CTL_NET;
51 mib[1] = PF_ROUTE;
52 mib[2] = 0;
53 mib[3] = AF_INET;
54 mib[4] = NET_RT_FLAGS;
55#ifdef RTF_LLINFO
56 mib[5] = RTF_LLINFO;
57#else
58 mib[5] = 0;
59#endif
60 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
61 return 0;
62
63 while (1)
64 {
Simon Kelley08456c62012-03-07 19:08:11 +000065 if (!expand_buf(&buff, needed))
Simon Kelley28866e92011-02-14 20:19:14 +000066 return 0;
Simon Kelley08456c62012-03-07 19:08:11 +000067 if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
Simon Kelley28866e92011-02-14 20:19:14 +000068 errno != ENOMEM)
69 break;
70 needed += needed / 8;
71 }
72 if (rc == -1)
73 return 0;
74
Simon Kelley08456c62012-03-07 19:08:11 +000075 for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
Simon Kelley28866e92011-02-14 20:19:14 +000076 {
77 rtm = (struct rt_msghdr *)next;
78 sin2 = (struct sockaddr_inarp *)(rtm + 1);
79 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
80 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
81 return 0;
82 }
83
84 return 1;
85}
Simon Kelley28866e92011-02-14 20:19:14 +000086#endif
87
88
89int iface_enumerate(int family, void *parm, int (*callback)())
Simon Kelley824af852008-02-12 20:43:05 +000090{
Simon Kelley08456c62012-03-07 19:08:11 +000091 struct ifaddrs *head, *addrs;
Simon Kelley96fafe22012-03-07 20:25:39 +000092 int errsav, ret = 0;
93
Simon Kelley28866e92011-02-14 20:19:14 +000094 if (family == AF_UNSPEC)
95#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
96 return arp_enumerate(parm, callback);
97#else
98 return 0; /* need code for Solaris and MacOS*/
99#endif
100
Simon Kelley6aef6002012-02-11 22:01:50 +0000101 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
102 if (family == AF_LOCAL)
103 family = AF_LINK;
104
Simon Kelley08456c62012-03-07 19:08:11 +0000105 if (getifaddrs(&head) == -1)
Simon Kelley824af852008-02-12 20:43:05 +0000106 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +0000107
Simon Kelley08456c62012-03-07 19:08:11 +0000108 for (addrs = head; addrs; addrs = addrs->ifa_next)
109 {
Simon Kelley96fafe22012-03-07 20:25:39 +0000110 if (addrs->ifa_addr->sa_family == family)
Simon Kelley824af852008-02-12 20:43:05 +0000111 {
Simon Kelley08456c62012-03-07 19:08:11 +0000112 int iface_index = if_nametoindex(addrs->ifa_name);
113
114 if (iface_index == 0)
115 continue;
116
Simon Kelley28866e92011-02-14 20:19:14 +0000117 if (family == AF_INET)
Simon Kelley824af852008-02-12 20:43:05 +0000118 {
Simon Kelley28866e92011-02-14 20:19:14 +0000119 struct in_addr addr, netmask, broadcast;
Simon Kelley08456c62012-03-07 19:08:11 +0000120 addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
121 netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
122 broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
123 if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
Simon Kelley28866e92011-02-14 20:19:14 +0000124 goto err;
Simon Kelley824af852008-02-12 20:43:05 +0000125 }
Simon Kelley28866e92011-02-14 20:19:14 +0000126#ifdef HAVE_IPV6
127 else if (family == AF_INET6)
128 {
Simon Kelley08456c62012-03-07 19:08:11 +0000129 struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
Simon Kelley96fafe22012-03-07 20:25:39 +0000130 unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
131 int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
Simon Kelley08456c62012-03-07 19:08:11 +0000132 int i, j, prefix = 0;
133
Simon Kelley96fafe22012-03-07 20:25:39 +0000134 for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
Simon Kelley08456c62012-03-07 19:08:11 +0000135 if (netmask[i] != 0xff)
136 break;
137
Simon Kelley96fafe22012-03-07 20:25:39 +0000138 if (i != IN6ADDRSZ && netmask[i])
Simon Kelley08456c62012-03-07 19:08:11 +0000139 for (j = 7; j > 0; j--, prefix++)
140 if ((netmask[i] & (1 << j)) == 0)
141 break;
142
Simon Kelley28866e92011-02-14 20:19:14 +0000143 /* voodoo to clear interface field in address */
144 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
145 {
146 addr->s6_addr[2] = 0;
147 addr->s6_addr[3] = 0;
148 }
Simon Kelley08456c62012-03-07 19:08:11 +0000149
150 if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm)))
Simon Kelley28866e92011-02-14 20:19:14 +0000151 goto err;
Simon Kelley08456c62012-03-07 19:08:11 +0000152 }
Simon Kelley824af852008-02-12 20:43:05 +0000153#endif
Simon Kelley6aef6002012-02-11 22:01:50 +0000154#ifdef HAVE_DHCP6
155 else if (family == AF_LINK)
156 {
157 /* Assume ethernet again here */
Simon Kelley96fafe22012-03-07 20:25:39 +0000158 struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
Simon Kelley08456c62012-03-07 19:08:11 +0000159 if (sdl->sdl_alen != 0 &&
160 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
Simon Kelley6aef6002012-02-11 22:01:50 +0000161 goto err;
162 }
163#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000164 }
Simon Kelley824af852008-02-12 20:43:05 +0000165 }
166
167 ret = 1;
168
169 err:
170 errsav = errno;
Simon Kelley08456c62012-03-07 19:08:11 +0000171 freeifaddrs(head);
Simon Kelley824af852008-02-12 20:43:05 +0000172 errno = errsav;
173
174 return ret;
175}
176#endif
177
178
Simon Kelley7622fc02009-06-04 20:32:05 +0100179#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000180#include <net/bpf.h>
181
Simon Kelley5aabfc72007-08-29 11:24:47 +0100182void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100183{
184 int i = 0;
185
186 while (1)
187 {
Simon Kelley08456c62012-03-07 19:08:11 +0000188 sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
189 if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
190 return;
191
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100192 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100193 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100194 }
195}
196
Simon Kelley5aabfc72007-08-29 11:24:47 +0100197void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100198 struct in_addr iface_addr, struct ifreq *ifr)
199{
200 /* Hairy stuff, packet either has to go to the
201 net broadcast or the destination can't reply to ARP yet,
202 but we do know the physical address.
203 Build the packet by steam, and send directly, bypassing
204 the kernel IP stack */
205
Simon Kelley849a8352006-06-09 21:02:31 +0100206 struct ether_header ether;
207 struct ip ip;
208 struct udphdr {
209 u16 uh_sport; /* source port */
210 u16 uh_dport; /* destination port */
211 u16 uh_ulen; /* udp length */
212 u16 uh_sum; /* udp checksum */
213 } udp;
214
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100215 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100216 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100217
218 /* Only know how to do ethernet on *BSD */
219 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
220 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100221 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100222 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100223 return;
224 }
225
226 ifr->ifr_addr.sa_family = AF_LINK;
227 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
228 return;
229
Simon Kelley849a8352006-06-09 21:02:31 +0100230 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
231 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100232
233 if (ntohs(mess->flags) & 0x8000)
234 {
Simon Kelley849a8352006-06-09 21:02:31 +0100235 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
236 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100237 }
238 else
239 {
Simon Kelley849a8352006-06-09 21:02:31 +0100240 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
241 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100242 }
243
Simon Kelley849a8352006-06-09 21:02:31 +0100244 ip.ip_p = IPPROTO_UDP;
245 ip.ip_src.s_addr = iface_addr.s_addr;
246 ip.ip_len = htons(sizeof(struct ip) +
247 sizeof(struct udphdr) +
248 len) ;
249 ip.ip_hl = sizeof(struct ip) / 4;
250 ip.ip_v = IPVERSION;
251 ip.ip_tos = 0;
252 ip.ip_id = htons(0);
253 ip.ip_off = htons(0x4000); /* don't fragment */
254 ip.ip_ttl = IPDEFTTL;
255 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100256 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100257 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100258 while (sum>>16)
259 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100260 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100261
Simon Kelley9e038942008-05-30 20:06:34 +0100262 udp.uh_sport = htons(daemon->dhcp_server_port);
263 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100264 if (len & 1)
265 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100266 udp.uh_sum = 0;
267 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100268 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000269 sum += ip.ip_src.s_addr & 0xffff;
270 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
271 sum += ip.ip_dst.s_addr & 0xffff;
272 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100273 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100274 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100275 for (i = 0; i < (len + 1) / 2; i++)
276 sum += ((u16 *)mess)[i];
277 while (sum>>16)
278 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100279 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100280
281 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
282
Simon Kelley849a8352006-06-09 21:02:31 +0100283 iov[0].iov_base = &ether;
284 iov[0].iov_len = sizeof(ether);
285 iov[1].iov_base = &ip;
286 iov[1].iov_len = sizeof(ip);
287 iov[2].iov_base = &udp;
288 iov[2].iov_len = sizeof(udp);
289 iov[3].iov_base = mess;
290 iov[3].iov_len = len;
291
292 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100293}
294
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100295#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100296
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100297