1 /* dnsmasq is Copyright (c) 2000-2013 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)
22 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
23 #include <sys/param.h>
24 #include <sys/sysctl.h>
25 #include <net/route.h>
26 #include <net/if_dl.h>
27 #include <netinet/if_ether.h>
28 #if defined(__FreeBSD__)
29 # include <net/if_var.h>
31 #include <netinet/in_var.h>
35 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
37 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
40 int arp_enumerate(void *parm
, int (*callback
)())
45 struct rt_msghdr
*rtm
;
46 struct sockaddr_inarp
*sin2
;
47 struct sockaddr_dl
*sdl
;
58 mib
[4] = NET_RT_FLAGS
;
64 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) == -1 || needed
== 0)
69 if (!expand_buf(&buff
, needed
))
71 if ((rc
= sysctl(mib
, 6, buff
.iov_base
, &needed
, NULL
, 0)) == 0 ||
79 for (next
= buff
.iov_base
; next
< (char *)buff
.iov_base
+ needed
; next
+= rtm
->rtm_msglen
)
81 rtm
= (struct rt_msghdr
*)next
;
82 sin2
= (struct sockaddr_inarp
*)(rtm
+ 1);
83 sdl
= (struct sockaddr_dl
*)((char *)sin2
+ SA_SIZE(sin2
));
84 if (!(*callback
)(AF_INET
, &sin2
->sin_addr
, LLADDR(sdl
), sdl
->sdl_alen
, parm
))
93 int iface_enumerate(int family
, void *parm
, int (*callback
)())
95 struct ifaddrs
*head
, *addrs
;
96 int errsav
, fd
= -1, ret
= 0;
98 if (family
== AF_UNSPEC
)
99 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
100 return arp_enumerate(parm
, callback
);
102 return 0; /* need code for Solaris and MacOS*/
105 /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
106 if (family
== AF_LOCAL
)
109 if (getifaddrs(&head
) == -1)
112 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
113 if (family
== AF_INET6
)
114 fd
= socket(PF_INET6
, SOCK_DGRAM
, 0);
117 for (addrs
= head
; addrs
; addrs
= addrs
->ifa_next
)
119 if (addrs
->ifa_addr
->sa_family
== family
)
121 int iface_index
= if_nametoindex(addrs
->ifa_name
);
123 if (iface_index
== 0 || !addrs
->ifa_addr
||
124 (!addrs
->ifa_netmask
&& family
!= AF_LINK
))
127 if (family
== AF_INET
)
129 struct in_addr addr
, netmask
, broadcast
;
130 addr
= ((struct sockaddr_in
*) addrs
->ifa_addr
)->sin_addr
;
131 netmask
= ((struct sockaddr_in
*) addrs
->ifa_netmask
)->sin_addr
;
132 if (addrs
->ifa_broadaddr
)
133 broadcast
= ((struct sockaddr_in
*) addrs
->ifa_broadaddr
)->sin_addr
;
135 broadcast
.s_addr
= 0;
136 if (!((*callback
)(addr
, iface_index
, NULL
, netmask
, broadcast
, parm
)))
140 else if (family
== AF_INET6
)
142 struct in6_addr
*addr
= &((struct sockaddr_in6
*) addrs
->ifa_addr
)->sin6_addr
;
143 unsigned char *netmask
= (unsigned char *) &((struct sockaddr_in6
*) addrs
->ifa_netmask
)->sin6_addr
;
144 int scope_id
= ((struct sockaddr_in6
*) addrs
->ifa_addr
)->sin6_scope_id
;
145 int i
, j
, prefix
= 0;
146 u32 valid
= 0xffffffff, preferred
= 0xffffffff;
148 #ifdef HAVE_BSD_NETWORK
149 struct in6_ifreq ifr6
;
151 memset(&ifr6
, 0, sizeof(ifr6
));
152 strncpy(ifr6
.ifr_name
, addrs
->ifa_name
, sizeof(ifr6
.ifr_name
));
154 ifr6
.ifr_addr
= *((struct sockaddr_in6
*) addrs
->ifa_addr
);
155 if (fd
!= -1 && ioctl(fd
, SIOCGIFAFLAG_IN6
, &ifr6
) != -1)
157 if (ifr6
.ifr_ifru
.ifru_flags6
& IN6_IFF_TENTATIVE
)
158 flags
|= IFACE_TENTATIVE
;
160 if (ifr6
.ifr_ifru
.ifru_flags6
& IN6_IFF_DEPRECATED
)
161 flags
|= IFACE_DEPRECATED
;
163 #ifdef IN6_IFF_TEMPORARY
164 if (!(ifr6
.ifr_ifru
.ifru_flags6
& (IN6_IFF_AUTOCONF
| IN6_IFF_TEMPORARY
)))
165 flags
|= IFACE_PERMANENT
;
168 #ifdef IN6_IFF_PRIVACY
169 if (!(ifr6
.ifr_ifru
.ifru_flags6
& (IN6_IFF_AUTOCONF
| IN6_IFF_PRIVACY
)))
170 flags
|= IFACE_PERMANENT
;
174 ifr6
.ifr_addr
= *((struct sockaddr_in6
*) addrs
->ifa_addr
);
175 if (fd
!= -1 && ioctl(fd
, SIOCGIFALIFETIME_IN6
, &ifr6
) != -1)
177 valid
= ifr6
.ifr_ifru
.ifru_lifetime
.ia6t_vltime
;
178 preferred
= ifr6
.ifr_ifru
.ifru_lifetime
.ia6t_pltime
;
182 for (i
= 0; i
< IN6ADDRSZ
; i
++, prefix
+= 8)
183 if (netmask
[i
] != 0xff)
186 if (i
!= IN6ADDRSZ
&& netmask
[i
])
187 for (j
= 7; j
> 0; j
--, prefix
++)
188 if ((netmask
[i
] & (1 << j
)) == 0)
191 /* voodoo to clear interface field in address */
192 if (!option_bool(OPT_NOWILD
) && IN6_IS_ADDR_LINKLOCAL(addr
))
194 addr
->s6_addr
[2] = 0;
195 addr
->s6_addr
[3] = 0;
198 if (!((*callback
)(addr
, prefix
, scope_id
, iface_index
, flags
,
199 (int) preferred
, (int)valid
, parm
)))
202 #endif /* HAVE_IPV6 */
205 else if (family
== AF_LINK
)
207 /* Assume ethernet again here */
208 struct sockaddr_dl
*sdl
= (struct sockaddr_dl
*) addrs
->ifa_addr
;
209 if (sdl
->sdl_alen
!= 0 &&
210 !((*callback
)(iface_index
, ARPHRD_ETHER
, LLADDR(sdl
), sdl
->sdl_alen
, parm
)))
231 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
240 sprintf(daemon
->dhcp_buff
, "/dev/bpf%d", i
++);
241 if ((daemon
->dhcp_raw_fd
= open(daemon
->dhcp_buff
, O_RDWR
, 0)) != -1)
245 die(_("cannot create DHCP BPF socket: %s"), NULL
, EC_BADNET
);
249 void send_via_bpf(struct dhcp_packet
*mess
, size_t len
,
250 struct in_addr iface_addr
, struct ifreq
*ifr
)
252 /* Hairy stuff, packet either has to go to the
253 net broadcast or the destination can't reply to ARP yet,
254 but we do know the physical address.
255 Build the packet by steam, and send directly, bypassing
256 the kernel IP stack */
258 struct ether_header ether
;
261 u16 uh_sport
; /* source port */
262 u16 uh_dport
; /* destination port */
263 u16 uh_ulen
; /* udp length */
264 u16 uh_sum
; /* udp checksum */
270 /* Only know how to do ethernet on *BSD */
271 if (mess
->htype
!= ARPHRD_ETHER
|| mess
->hlen
!= ETHER_ADDR_LEN
)
273 my_syslog(MS_DHCP
| LOG_WARNING
, _("DHCP request for unsupported hardware type (%d) received on %s"),
274 mess
->htype
, ifr
->ifr_name
);
278 ifr
->ifr_addr
.sa_family
= AF_LINK
;
279 if (ioctl(daemon
->dhcpfd
, SIOCGIFADDR
, ifr
) < 0)
282 memcpy(ether
.ether_shost
, LLADDR((struct sockaddr_dl
*)&ifr
->ifr_addr
), ETHER_ADDR_LEN
);
283 ether
.ether_type
= htons(ETHERTYPE_IP
);
285 if (ntohs(mess
->flags
) & 0x8000)
287 memset(ether
.ether_dhost
, 255, ETHER_ADDR_LEN
);
288 ip
.ip_dst
.s_addr
= INADDR_BROADCAST
;
292 memcpy(ether
.ether_dhost
, mess
->chaddr
, ETHER_ADDR_LEN
);
293 ip
.ip_dst
.s_addr
= mess
->yiaddr
.s_addr
;
296 ip
.ip_p
= IPPROTO_UDP
;
297 ip
.ip_src
.s_addr
= iface_addr
.s_addr
;
298 ip
.ip_len
= htons(sizeof(struct ip
) +
299 sizeof(struct udphdr
) +
301 ip
.ip_hl
= sizeof(struct ip
) / 4;
305 ip
.ip_off
= htons(0x4000); /* don't fragment */
306 ip
.ip_ttl
= IPDEFTTL
;
308 for (sum
= 0, i
= 0; i
< sizeof(struct ip
) / 2; i
++)
309 sum
+= ((u16
*)&ip
)[i
];
311 sum
= (sum
& 0xffff) + (sum
>> 16);
312 ip
.ip_sum
= (sum
== 0xffff) ? sum
: ~sum
;
314 udp
.uh_sport
= htons(daemon
->dhcp_server_port
);
315 udp
.uh_dport
= htons(daemon
->dhcp_client_port
);
317 ((char *)mess
)[len
] = 0; /* for checksum, in case length is odd. */
319 udp
.uh_ulen
= sum
= htons(sizeof(struct udphdr
) + len
);
320 sum
+= htons(IPPROTO_UDP
);
321 sum
+= ip
.ip_src
.s_addr
& 0xffff;
322 sum
+= (ip
.ip_src
.s_addr
>> 16) & 0xffff;
323 sum
+= ip
.ip_dst
.s_addr
& 0xffff;
324 sum
+= (ip
.ip_dst
.s_addr
>> 16) & 0xffff;
325 for (i
= 0; i
< sizeof(struct udphdr
)/2; i
++)
326 sum
+= ((u16
*)&udp
)[i
];
327 for (i
= 0; i
< (len
+ 1) / 2; i
++)
328 sum
+= ((u16
*)mess
)[i
];
330 sum
= (sum
& 0xffff) + (sum
>> 16);
331 udp
.uh_sum
= (sum
== 0xffff) ? sum
: ~sum
;
333 ioctl(daemon
->dhcp_raw_fd
, BIOCSETIF
, ifr
);
335 iov
[0].iov_base
= ðer
;
336 iov
[0].iov_len
= sizeof(ether
);
337 iov
[1].iov_base
= &ip
;
338 iov
[1].iov_len
= sizeof(ip
);
339 iov
[2].iov_base
= &udp
;
340 iov
[2].iov_len
= sizeof(udp
);
341 iov
[3].iov_base
= mess
;
342 iov
[3].iov_len
= len
;
344 while (writev(daemon
->dhcp_raw_fd
, iov
, 4) == -1 && retry_send());