blob: cffcf6740402a36a28f6b65e9aa98f825446ba08 [file] [log] [blame]
Simon Kelley28866e92011-02-14 20:19:14 +00001/* dnsmasq is Copyright (c) 2000-2011 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 Kelley5e9e0ef2006-04-17 14:24:29 +010020
21static struct iovec ifconf = {
22 .iov_base = NULL,
23 .iov_len = 0
24};
25
26static struct iovec ifreq = {
27 .iov_base = NULL,
28 .iov_len = 0
29};
30
Simon Kelley28866e92011-02-14 20:19:14 +000031#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
32
33#include <sys/sysctl.h>
34#include <net/route.h>
35#include <net/if_dl.h>
36#include <netinet/if_ether.h>
37
Simon Kelley7de060b2011-08-26 17:24:52 +010038#ifndef SA_SIZE
39#define SA_SIZE(sa) \
40 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
41 sizeof(long) : \
42 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
43#endif
44
Simon Kelley28866e92011-02-14 20:19:14 +000045int arp_enumerate(void *parm, int (*callback)())
46{
47 int mib[6];
48 size_t needed;
49 char *next;
50 struct rt_msghdr *rtm;
51 struct sockaddr_inarp *sin2;
52 struct sockaddr_dl *sdl;
53 int rc;
54
55 mib[0] = CTL_NET;
56 mib[1] = PF_ROUTE;
57 mib[2] = 0;
58 mib[3] = AF_INET;
59 mib[4] = NET_RT_FLAGS;
60#ifdef RTF_LLINFO
61 mib[5] = RTF_LLINFO;
62#else
63 mib[5] = 0;
64#endif
65 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
66 return 0;
67
68 while (1)
69 {
70 if (!expand_buf(&ifconf, needed))
71 return 0;
72 if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
73 errno != ENOMEM)
74 break;
75 needed += needed / 8;
76 }
77 if (rc == -1)
78 return 0;
79
80 for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
81 {
82 rtm = (struct rt_msghdr *)next;
83 sin2 = (struct sockaddr_inarp *)(rtm + 1);
84 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
85 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
86 return 0;
87 }
88
89 return 1;
90}
91
92#endif
93
94
95int iface_enumerate(int family, void *parm, int (*callback)())
Simon Kelley824af852008-02-12 20:43:05 +000096{
97 char *ptr;
98 struct ifreq *ifr;
99 struct ifconf ifc;
100 int fd, errsav, ret = 0;
101 int lastlen = 0;
102 size_t len = 0;
103
Simon Kelley28866e92011-02-14 20:19:14 +0000104 if (family == AF_UNSPEC)
105#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
106 return arp_enumerate(parm, callback);
107#else
108 return 0; /* need code for Solaris and MacOS*/
109#endif
110
Simon Kelley824af852008-02-12 20:43:05 +0000111 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
112 return 0;
113
114 while(1)
115 {
116 len += 10*sizeof(struct ifreq);
117
118 if (!expand_buf(&ifconf, len))
119 goto err;
120
121 ifc.ifc_len = len;
122 ifc.ifc_buf = ifconf.iov_base;
123
124 if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
125 {
126 if (errno != EINVAL || lastlen != 0)
127 goto err;
128 }
129 else
130 {
131 if (ifc.ifc_len == lastlen)
132 break; /* got a big enough buffer now */
133 lastlen = ifc.ifc_len;
134 }
135 }
136
Simon Kelley73a08a22009-02-05 20:28:08 +0000137 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
Simon Kelley824af852008-02-12 20:43:05 +0000138 {
139 /* subsequent entries may not be aligned, so copy into
140 an aligned buffer to avoid nasty complaints about
141 unaligned accesses. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000142
Simon Kelley824af852008-02-12 20:43:05 +0000143 len = sizeof(struct ifreq);
Simon Kelley73a08a22009-02-05 20:28:08 +0000144
145#ifdef HAVE_SOCKADDR_SA_LEN
146 ifr = (struct ifreq *)ptr;
147 if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
148 len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
Simon Kelley824af852008-02-12 20:43:05 +0000149#endif
Simon Kelley73a08a22009-02-05 20:28:08 +0000150
Simon Kelley824af852008-02-12 20:43:05 +0000151 if (!expand_buf(&ifreq, len))
152 goto err;
153
154 ifr = (struct ifreq *)ifreq.iov_base;
155 memcpy(ifr, ptr, len);
156
Simon Kelley28866e92011-02-14 20:19:14 +0000157 if (ifr->ifr_addr.sa_family == family)
Simon Kelley824af852008-02-12 20:43:05 +0000158 {
Simon Kelley28866e92011-02-14 20:19:14 +0000159 if (family == AF_INET)
Simon Kelley824af852008-02-12 20:43:05 +0000160 {
Simon Kelley28866e92011-02-14 20:19:14 +0000161 struct in_addr addr, netmask, broadcast;
162 broadcast.s_addr = 0;
163 addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
164 if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
165 continue;
166 netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
167 if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
168 broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
169 if (!((*callback)(addr,
170 (int)if_nametoindex(ifr->ifr_name),
171 netmask, broadcast,
172 parm)))
173 goto err;
Simon Kelley824af852008-02-12 20:43:05 +0000174 }
Simon Kelley28866e92011-02-14 20:19:14 +0000175#ifdef HAVE_IPV6
176 else if (family == AF_INET6)
177 {
178 struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
179 /* voodoo to clear interface field in address */
180 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
181 {
182 addr->s6_addr[2] = 0;
183 addr->s6_addr[3] = 0;
184 }
185 if (!((*callback)(addr,
186 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
187 (int)if_nametoindex(ifr->ifr_name),
188 parm)))
189 goto err;
190 }
Simon Kelley824af852008-02-12 20:43:05 +0000191#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000192 }
Simon Kelley824af852008-02-12 20:43:05 +0000193 }
194
195 ret = 1;
196
197 err:
198 errsav = errno;
199 close(fd);
200 errno = errsav;
201
202 return ret;
203}
204#endif
205
206
Simon Kelley7622fc02009-06-04 20:32:05 +0100207#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000208#include <net/bpf.h>
209
Simon Kelley5aabfc72007-08-29 11:24:47 +0100210void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100211{
212 int i = 0;
213
214 while (1)
215 {
216 /* useful size which happens to be sufficient */
217 if (expand_buf(&ifreq, sizeof(struct ifreq)))
218 {
219 sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
220 if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100221 return;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100222 }
223 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100224 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100225 }
226}
227
Simon Kelley5aabfc72007-08-29 11:24:47 +0100228void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100229 struct in_addr iface_addr, struct ifreq *ifr)
230{
231 /* Hairy stuff, packet either has to go to the
232 net broadcast or the destination can't reply to ARP yet,
233 but we do know the physical address.
234 Build the packet by steam, and send directly, bypassing
235 the kernel IP stack */
236
Simon Kelley849a8352006-06-09 21:02:31 +0100237 struct ether_header ether;
238 struct ip ip;
239 struct udphdr {
240 u16 uh_sport; /* source port */
241 u16 uh_dport; /* destination port */
242 u16 uh_ulen; /* udp length */
243 u16 uh_sum; /* udp checksum */
244 } udp;
245
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100246 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100247 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100248
249 /* Only know how to do ethernet on *BSD */
250 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
251 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100252 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100253 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100254 return;
255 }
256
257 ifr->ifr_addr.sa_family = AF_LINK;
258 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
259 return;
260
Simon Kelley849a8352006-06-09 21:02:31 +0100261 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
262 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263
264 if (ntohs(mess->flags) & 0x8000)
265 {
Simon Kelley849a8352006-06-09 21:02:31 +0100266 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
267 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100268 }
269 else
270 {
Simon Kelley849a8352006-06-09 21:02:31 +0100271 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
272 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100273 }
274
Simon Kelley849a8352006-06-09 21:02:31 +0100275 ip.ip_p = IPPROTO_UDP;
276 ip.ip_src.s_addr = iface_addr.s_addr;
277 ip.ip_len = htons(sizeof(struct ip) +
278 sizeof(struct udphdr) +
279 len) ;
280 ip.ip_hl = sizeof(struct ip) / 4;
281 ip.ip_v = IPVERSION;
282 ip.ip_tos = 0;
283 ip.ip_id = htons(0);
284 ip.ip_off = htons(0x4000); /* don't fragment */
285 ip.ip_ttl = IPDEFTTL;
286 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100287 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100288 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100289 while (sum>>16)
290 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100291 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100292
Simon Kelley9e038942008-05-30 20:06:34 +0100293 udp.uh_sport = htons(daemon->dhcp_server_port);
294 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100295 if (len & 1)
296 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100297 udp.uh_sum = 0;
298 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100299 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000300 sum += ip.ip_src.s_addr & 0xffff;
301 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
302 sum += ip.ip_dst.s_addr & 0xffff;
303 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100304 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100305 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100306 for (i = 0; i < (len + 1) / 2; i++)
307 sum += ((u16 *)mess)[i];
308 while (sum>>16)
309 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100310 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100311
312 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
313
Simon Kelley849a8352006-06-09 21:02:31 +0100314 iov[0].iov_base = &ether;
315 iov[0].iov_len = sizeof(ether);
316 iov[1].iov_base = &ip;
317 iov[1].iov_len = sizeof(ip);
318 iov[2].iov_base = &udp;
319 iov[2].iov_len = sizeof(udp);
320 iov[3].iov_base = mess;
321 iov[3].iov_len = len;
322
323 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100324}
325
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100326#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100327
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100328