blob: abde78c795b2db205d4a6596a90ea60ed7d3c6db [file] [log] [blame]
Simon Kelley73a08a22009-02-05 20:28:08 +00001/* dnsmasq is Copyright (c) 2000-2009 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 Kelley824af852008-02-12 20:43:05 +000031int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
32{
33 char *ptr;
34 struct ifreq *ifr;
35 struct ifconf ifc;
36 int fd, errsav, ret = 0;
37 int lastlen = 0;
38 size_t len = 0;
39
40 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
41 return 0;
42
43 while(1)
44 {
45 len += 10*sizeof(struct ifreq);
46
47 if (!expand_buf(&ifconf, len))
48 goto err;
49
50 ifc.ifc_len = len;
51 ifc.ifc_buf = ifconf.iov_base;
52
53 if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
54 {
55 if (errno != EINVAL || lastlen != 0)
56 goto err;
57 }
58 else
59 {
60 if (ifc.ifc_len == lastlen)
61 break; /* got a big enough buffer now */
62 lastlen = ifc.ifc_len;
63 }
64 }
65
Simon Kelley73a08a22009-02-05 20:28:08 +000066 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
Simon Kelley824af852008-02-12 20:43:05 +000067 {
68 /* subsequent entries may not be aligned, so copy into
69 an aligned buffer to avoid nasty complaints about
70 unaligned accesses. */
Simon Kelley73a08a22009-02-05 20:28:08 +000071
Simon Kelley824af852008-02-12 20:43:05 +000072 len = sizeof(struct ifreq);
Simon Kelley73a08a22009-02-05 20:28:08 +000073
74#ifdef HAVE_SOCKADDR_SA_LEN
75 ifr = (struct ifreq *)ptr;
76 if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
77 len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
Simon Kelley824af852008-02-12 20:43:05 +000078#endif
Simon Kelley73a08a22009-02-05 20:28:08 +000079
Simon Kelley824af852008-02-12 20:43:05 +000080 if (!expand_buf(&ifreq, len))
81 goto err;
82
83 ifr = (struct ifreq *)ifreq.iov_base;
84 memcpy(ifr, ptr, len);
85
86 if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
87 {
88 struct in_addr addr, netmask, broadcast;
89 broadcast.s_addr = 0;
90 addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
91 if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
92 continue;
93 netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
94 if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
95 broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
96 if (!((*ipv4_callback)(addr,
97 (int)if_nametoindex(ifr->ifr_name),
98 netmask, broadcast,
99 parm)))
100 goto err;
101 }
102#ifdef HAVE_IPV6
103 else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
104 {
105 struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
106 /* voodoo to clear interface field in address */
107 if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
108 {
109 addr->s6_addr[2] = 0;
110 addr->s6_addr[3] = 0;
111 }
112 if (!((*ipv6_callback)(addr,
113 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
114 (int)if_nametoindex(ifr->ifr_name),
115 parm)))
116 goto err;
117 }
118#endif
119 }
120
121 ret = 1;
122
123 err:
124 errsav = errno;
125 close(fd);
126 errno = errsav;
127
128 return ret;
129}
130#endif
131
132
Simon Kelley7622fc02009-06-04 20:32:05 +0100133#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
Simon Kelley824af852008-02-12 20:43:05 +0000134#include <net/bpf.h>
135
Simon Kelley5aabfc72007-08-29 11:24:47 +0100136void init_bpf(void)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137{
138 int i = 0;
139
140 while (1)
141 {
142 /* useful size which happens to be sufficient */
143 if (expand_buf(&ifreq, sizeof(struct ifreq)))
144 {
145 sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
146 if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100147 return;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100148 }
149 if (errno != EBUSY)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100150 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100151 }
152}
153
Simon Kelley5aabfc72007-08-29 11:24:47 +0100154void send_via_bpf(struct dhcp_packet *mess, size_t len,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100155 struct in_addr iface_addr, struct ifreq *ifr)
156{
157 /* Hairy stuff, packet either has to go to the
158 net broadcast or the destination can't reply to ARP yet,
159 but we do know the physical address.
160 Build the packet by steam, and send directly, bypassing
161 the kernel IP stack */
162
Simon Kelley849a8352006-06-09 21:02:31 +0100163 struct ether_header ether;
164 struct ip ip;
165 struct udphdr {
166 u16 uh_sport; /* source port */
167 u16 uh_dport; /* destination port */
168 u16 uh_ulen; /* udp length */
169 u16 uh_sum; /* udp checksum */
170 } udp;
171
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100172 u32 i, sum;
Simon Kelley849a8352006-06-09 21:02:31 +0100173 struct iovec iov[4];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100174
175 /* Only know how to do ethernet on *BSD */
176 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
177 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100178 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100179 mess->htype, ifr->ifr_name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100180 return;
181 }
182
183 ifr->ifr_addr.sa_family = AF_LINK;
184 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
185 return;
186
Simon Kelley849a8352006-06-09 21:02:31 +0100187 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
188 ether.ether_type = htons(ETHERTYPE_IP);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100189
190 if (ntohs(mess->flags) & 0x8000)
191 {
Simon Kelley849a8352006-06-09 21:02:31 +0100192 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
193 ip.ip_dst.s_addr = INADDR_BROADCAST;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100194 }
195 else
196 {
Simon Kelley849a8352006-06-09 21:02:31 +0100197 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
198 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100199 }
200
Simon Kelley849a8352006-06-09 21:02:31 +0100201 ip.ip_p = IPPROTO_UDP;
202 ip.ip_src.s_addr = iface_addr.s_addr;
203 ip.ip_len = htons(sizeof(struct ip) +
204 sizeof(struct udphdr) +
205 len) ;
206 ip.ip_hl = sizeof(struct ip) / 4;
207 ip.ip_v = IPVERSION;
208 ip.ip_tos = 0;
209 ip.ip_id = htons(0);
210 ip.ip_off = htons(0x4000); /* don't fragment */
211 ip.ip_ttl = IPDEFTTL;
212 ip.ip_sum = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100213 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100214 sum += ((u16 *)&ip)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100215 while (sum>>16)
216 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100217 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100218
Simon Kelley9e038942008-05-30 20:06:34 +0100219 udp.uh_sport = htons(daemon->dhcp_server_port);
220 udp.uh_dport = htons(daemon->dhcp_client_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100221 if (len & 1)
222 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
Simon Kelley849a8352006-06-09 21:02:31 +0100223 udp.uh_sum = 0;
224 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100225 sum += htons(IPPROTO_UDP);
Simon Kelley824af852008-02-12 20:43:05 +0000226 sum += ip.ip_src.s_addr & 0xffff;
227 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
228 sum += ip.ip_dst.s_addr & 0xffff;
229 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100230 for (i = 0; i < sizeof(struct udphdr)/2; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100231 sum += ((u16 *)&udp)[i];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100232 for (i = 0; i < (len + 1) / 2; i++)
233 sum += ((u16 *)mess)[i];
234 while (sum>>16)
235 sum = (sum & 0xffff) + (sum >> 16);
Simon Kelley849a8352006-06-09 21:02:31 +0100236 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100237
238 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
239
Simon Kelley849a8352006-06-09 21:02:31 +0100240 iov[0].iov_base = &ether;
241 iov[0].iov_len = sizeof(ether);
242 iov[1].iov_base = &ip;
243 iov[1].iov_len = sizeof(ip);
244 iov[2].iov_base = &udp;
245 iov[2].iov_len = sizeof(udp);
246 iov[3].iov_base = mess;
247 iov[3].iov_len = len;
248
249 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100250}
251
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100252#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100253
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100254