blob: 9a77426743a1586069003c1df64e598505bc682d [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
38int arp_enumerate(void *parm, int (*callback)())
39{
40 int mib[6];
41 size_t needed;
42 char *next;
43 struct rt_msghdr *rtm;
44 struct sockaddr_inarp *sin2;
45 struct sockaddr_dl *sdl;
46 int rc;
47
48 mib[0] = CTL_NET;
49 mib[1] = PF_ROUTE;
50 mib[2] = 0;
51 mib[3] = AF_INET;
52 mib[4] = NET_RT_FLAGS;
53#ifdef RTF_LLINFO
54 mib[5] = RTF_LLINFO;
55#else
56 mib[5] = 0;
57#endif
58 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
59 return 0;
60
61 while (1)
62 {
63 if (!expand_buf(&ifconf, needed))
64 return 0;
65 if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
66 errno != ENOMEM)
67 break;
68 needed += needed / 8;
69 }
70 if (rc == -1)
71 return 0;
72
73 for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
74 {
75 rtm = (struct rt_msghdr *)next;
76 sin2 = (struct sockaddr_inarp *)(rtm + 1);
77 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
78 if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
79 return 0;
80 }
81
82 return 1;
83}
84
85#endif
86
87
88int iface_enumerate(int family, void *parm, int (*callback)())
Simon Kelley824af852008-02-12 20:43:05 +000089{
90 char *ptr;
91 struct ifreq *ifr;
92 struct ifconf ifc;
93 int fd, errsav, ret = 0;
94 int lastlen = 0;
95 size_t len = 0;
96
Simon Kelley28866e92011-02-14 20:19:14 +000097 if (family == AF_UNSPEC)
98#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
99 return arp_enumerate(parm, callback);
100#else
101 return 0; /* need code for Solaris and MacOS*/
102#endif
103
Simon Kelley824af852008-02-12 20:43:05 +0000104 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
105 return 0;
106
107 while(1)
108 {
109 len += 10*sizeof(struct ifreq);
110
111 if (!expand_buf(&ifconf, len))
112 goto err;
113
114 ifc.ifc_len = len;
115 ifc.ifc_buf = ifconf.iov_base;
116
117 if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
118 {
119 if (errno != EINVAL || lastlen != 0)
120 goto err;
121 }
122 else
123 {
124 if (ifc.ifc_len == lastlen)
125 break; /* got a big enough buffer now */
126 lastlen = ifc.ifc_len;
127 }
128 }
129
Simon Kelley73a08a22009-02-05 20:28:08 +0000130 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
Simon Kelley824af852008-02-12 20:43:05 +0000131 {
132 /* subsequent entries may not be aligned, so copy into
133 an aligned buffer to avoid nasty complaints about
134 unaligned accesses. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000135
Simon Kelley824af852008-02-12 20:43:05 +0000136 len = sizeof(struct ifreq);
Simon Kelley73a08a22009-02-05 20:28:08 +0000137
138#ifdef HAVE_SOCKADDR_SA_LEN
139 ifr = (struct ifreq *)ptr;
140 if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
141 len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
Simon Kelley824af852008-02-12 20:43:05 +0000142#endif
Simon Kelley73a08a22009-02-05 20:28:08 +0000143
Simon Kelley824af852008-02-12 20:43:05 +0000144 if (!expand_buf(&ifreq, len))
145 goto err;
146
147 ifr = (struct ifreq *)ifreq.iov_base;
148 memcpy(ifr, ptr, len);
149
Simon Kelley28866e92011-02-14 20:19:14 +0000150 if (ifr->ifr_addr.sa_family == family)
Simon Kelley824af852008-02-12 20:43:05 +0000151 {
Simon Kelley28866e92011-02-14 20:19:14 +0000152 if (family == AF_INET)
Simon Kelley824af852008-02-12 20:43:05 +0000153 {
Simon Kelley28866e92011-02-14 20:19:14 +0000154 struct in_addr addr, netmask, broadcast;
155 broadcast.s_addr = 0;
156 addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
157 if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
158 continue;
159 netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
160 if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
161 broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
162 if (!((*callback)(addr,
163 (int)if_nametoindex(ifr->ifr_name),
164 netmask, broadcast,
165 parm)))
166 goto err;
Simon Kelley824af852008-02-12 20:43:05 +0000167 }
Simon Kelley28866e92011-02-14 20:19:14 +0000168#ifdef HAVE_IPV6
169 else if (family == AF_INET6)
170 {
171 struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
172 /* voodoo to clear interface field in address */
173 if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
174 {
175 addr->s6_addr[2] = 0;
176 addr->s6_addr[3] = 0;
177 }
178 if (!((*callback)(addr,
179 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
180 (int)if_nametoindex(ifr->ifr_name),
181 parm)))
182 goto err;
183 }
Simon Kelley824af852008-02-12 20:43:05 +0000184#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000185 }
Simon Kelley824af852008-02-12 20:43:05 +0000186 }
187
188 ret = 1;
189
190 err:
191 errsav = errno;
192 close(fd);
193 errno = errsav;
194
195 return ret;
196}
197#endif
198
199
Simon Kelley7622fc02009-06-04 20:32:05 +0100200#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000201#include <net/bpf.h>
202
Simon Kelley5aabfc72007-08-29 11:24:47 +0100203void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100204{
205 int i = 0;
206
207 while (1)
208 {
209 /* useful size which happens to be sufficient */
210 if (expand_buf(&ifreq, sizeof(struct ifreq)))
211 {
212 sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
213 if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100214 return;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100215 }
216 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100217 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100218 }
219}
220
Simon Kelley5aabfc72007-08-29 11:24:47 +0100221void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100222 struct in_addr iface_addr, struct ifreq *ifr)
223{
224 /* Hairy stuff, packet either has to go to the
225 net broadcast or the destination can't reply to ARP yet,
226 but we do know the physical address.
227 Build the packet by steam, and send directly, bypassing
228 the kernel IP stack */
229
Simon Kelley849a8352006-06-09 21:02:31 +0100230 struct ether_header ether;
231 struct ip ip;
232 struct udphdr {
233 u16 uh_sport; /* source port */
234 u16 uh_dport; /* destination port */
235 u16 uh_ulen; /* udp length */
236 u16 uh_sum; /* udp checksum */
237 } udp;
238
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100239 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100240 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100241
242 /* Only know how to do ethernet on *BSD */
243 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
244 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100245 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100246 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100247 return;
248 }
249
250 ifr->ifr_addr.sa_family = AF_LINK;
251 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
252 return;
253
Simon Kelley849a8352006-06-09 21:02:31 +0100254 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
255 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100256
257 if (ntohs(mess->flags) & 0x8000)
258 {
Simon Kelley849a8352006-06-09 21:02:31 +0100259 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
260 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100261 }
262 else
263 {
Simon Kelley849a8352006-06-09 21:02:31 +0100264 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
265 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100266 }
267
Simon Kelley849a8352006-06-09 21:02:31 +0100268 ip.ip_p = IPPROTO_UDP;
269 ip.ip_src.s_addr = iface_addr.s_addr;
270 ip.ip_len = htons(sizeof(struct ip) +
271 sizeof(struct udphdr) +
272 len) ;
273 ip.ip_hl = sizeof(struct ip) / 4;
274 ip.ip_v = IPVERSION;
275 ip.ip_tos = 0;
276 ip.ip_id = htons(0);
277 ip.ip_off = htons(0x4000); /* don't fragment */
278 ip.ip_ttl = IPDEFTTL;
279 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100280 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100281 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100282 while (sum>>16)
283 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100284 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100285
Simon Kelley9e038942008-05-30 20:06:34 +0100286 udp.uh_sport = htons(daemon->dhcp_server_port);
287 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100288 if (len & 1)
289 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100290 udp.uh_sum = 0;
291 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100292 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000293 sum += ip.ip_src.s_addr & 0xffff;
294 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
295 sum += ip.ip_dst.s_addr & 0xffff;
296 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100297 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100298 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100299 for (i = 0; i < (len + 1) / 2; i++)
300 sum += ((u16 *)mess)[i];
301 while (sum>>16)
302 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100303 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100304
305 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
306
Simon Kelley849a8352006-06-09 21:02:31 +0100307 iov[0].iov_base = &ether;
308 iov[0].iov_len = sizeof(ether);
309 iov[1].iov_base = &ip;
310 iov[1].iov_len = sizeof(ip);
311 iov[2].iov_base = &udp;
312 iov[2].iov_len = sizeof(udp);
313 iov[3].iov_base = mess;
314 iov[3].iov_len = len;
315
316 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100317}
318
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100319#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100320
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100321