1 /* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
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.
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.
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/>.
19 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
21 static struct iovec ifconf = {
26 static struct iovec ifreq = {
31 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
33 #include <sys/sysctl.h>
34 #include <net/route.h>
35 #include <net/if_dl.h>
36 #include <netinet/if_ether.h>
38 int arp_enumerate(void *parm, int (*callback)())
43 struct rt_msghdr *rtm;
44 struct sockaddr_inarp *sin2;
45 struct sockaddr_dl *sdl;
52 mib[4] = NET_RT_FLAGS;
58 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
63 if (!expand_buf(&ifconf, needed))
65 if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
73 for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
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))
88 int iface_enumerate(int family, void *parm, int (*callback)())
93 int fd, errsav, ret = 0;
97 if (family == AF_UNSPEC)
98 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
99 return arp_enumerate(parm, callback);
101 return 0; /* need code for Solaris and MacOS*/
104 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
109 len += 10*sizeof(struct ifreq);
111 if (!expand_buf(&ifconf, len))
115 ifc.ifc_buf = ifconf.iov_base;
117 if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
119 if (errno != EINVAL || lastlen != 0)
124 if (ifc.ifc_len == lastlen)
125 break; /* got a big enough buffer now */
126 lastlen = ifc.ifc_len;
130 for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
132 /* subsequent entries may not be aligned, so copy into
133 an aligned buffer to avoid nasty complaints about
134 unaligned accesses. */
136 len = sizeof(struct ifreq);
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);
144 if (!expand_buf(&ifreq, len))
147 ifr = (struct ifreq *)ifreq.iov_base;
148 memcpy(ifr, ptr, len);
150 if (ifr->ifr_addr.sa_family == family)
152 if (family == AF_INET)
154 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)
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),
169 else if (family == AF_INET6)
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))
175 addr->s6_addr[2] = 0;
176 addr->s6_addr[3] = 0;
178 if (!((*callback)(addr,
179 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
180 (int)if_nametoindex(ifr->ifr_name),
200 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
209 /* useful size which happens to be sufficient */
210 if (expand_buf(&ifreq, sizeof(struct ifreq)))
212 sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
213 if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
217 die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
221 void send_via_bpf(struct dhcp_packet *mess, size_t len,
222 struct in_addr iface_addr, struct ifreq *ifr)
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 */
230 struct ether_header ether;
233 u16 uh_sport; /* source port */
234 u16 uh_dport; /* destination port */
235 u16 uh_ulen; /* udp length */
236 u16 uh_sum; /* udp checksum */
242 /* Only know how to do ethernet on *BSD */
243 if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
245 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
246 mess->htype, ifr->ifr_name);
250 ifr->ifr_addr.sa_family = AF_LINK;
251 if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
254 memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
255 ether.ether_type = htons(ETHERTYPE_IP);
257 if (ntohs(mess->flags) & 0x8000)
259 memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
260 ip.ip_dst.s_addr = INADDR_BROADCAST;
264 memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
265 ip.ip_dst.s_addr = mess->yiaddr.s_addr;
268 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) +
273 ip.ip_hl = sizeof(struct ip) / 4;
277 ip.ip_off = htons(0x4000); /* don't fragment */
278 ip.ip_ttl = IPDEFTTL;
280 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
281 sum += ((u16 *)&ip)[i];
283 sum = (sum & 0xffff) + (sum >> 16);
284 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
286 udp.uh_sport = htons(daemon->dhcp_server_port);
287 udp.uh_dport = htons(daemon->dhcp_client_port);
289 ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
291 udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
292 sum += htons(IPPROTO_UDP);
293 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;
297 for (i = 0; i < sizeof(struct udphdr)/2; i++)
298 sum += ((u16 *)&udp)[i];
299 for (i = 0; i < (len + 1) / 2; i++)
300 sum += ((u16 *)mess)[i];
302 sum = (sum & 0xffff) + (sum >> 16);
303 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
305 ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
307 iov[0].iov_base = ðer;
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;
316 while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());