1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
2
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
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
8 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.
12
13 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/>.
15 */
16
17 #include "dnsmasq.h"
18
19 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
20
21 static struct iovec ifconf = {
22 .iov_base = NULL,
23 .iov_len = 0
24 };
25
26 static struct iovec ifreq = {
27 .iov_base = NULL,
28 .iov_len = 0
29 };
30
iface_enumerate(void * parm,int (* ipv4_callback)(),int (* ipv6_callback)())31 int 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
66 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
67 {
68 /* subsequent entries may not be aligned, so copy into
69 an aligned buffer to avoid nasty complaints about
70 unaligned accesses. */
71
72 len = sizeof(struct ifreq);
73
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);
78 #endif
79
80 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
133 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
134 #include <net/bpf.h>
135
init_bpf(void)136 void init_bpf(void)
137 {
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)
147 return;
148 }
149 if (errno != EBUSY)
150 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
151 }
152 }
153
send_via_bpf(struct dhcp_packet * mess,size_t len,struct in_addr iface_addr,struct ifreq * ifr)154 void send_via_bpf(struct dhcp_packet *mess, size_t len,
155 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
163 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
172 u32 i, sum;
173 struct iovec iov[4];
174
175 /* Only know how to do ethernet on *BSD */
176 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
177 {
178 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
179 mess->htype, ifr->ifr_name);
180 return;
181 }
182
183 ifr->ifr_addr.sa_family = AF_LINK;
184 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
185 return;
186
187 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
188 ether.ether_type = htons(ETHERTYPE_IP);
189
190 if (ntohs(mess->flags) & 0x8000)
191 {
192 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
193 ip.ip_dst.s_addr = INADDR_BROADCAST;
194 }
195 else
196 {
197 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
198 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
199 }
200
201 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;
213 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
214 sum += ((u16 *)&ip)[i];
215 while (sum>>16)
216 sum = (sum & 0xffff) + (sum >> 16);
217 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
218
219 udp.uh_sport = htons(daemon->dhcp_server_port);
220 udp.uh_dport = htons(daemon->dhcp_client_port);
221 if (len & 1)
222 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
223 udp.uh_sum = 0;
224 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
225 sum += htons(IPPROTO_UDP);
226 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;
230 for (i = 0; i < sizeof(struct udphdr)/2; i++)
231 sum += ((u16 *)&udp)[i];
232 for (i = 0; i < (len + 1) / 2; i++)
233 sum += ((u16 *)mess)[i];
234 while (sum>>16)
235 sum = (sum & 0xffff) + (sum >> 16);
236 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
237
238 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
239
240 iov[0].iov_base = ðer;
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());
250 }
251
252 #endif
253
254
255