@@ -2,7 +2,8 @@ use crate::types::NicType;
22use  anyhow:: { Result ,  anyhow} ; 
33use  serde:: Deserializer ; 
44use  serde:: de:: { Unexpected ,  Visitor } ; 
5- use  std:: net:: IpAddr ; 
5+ use  std:: net:: { IpAddr ,  Ipv4Addr ,  Ipv6Addr ,  SocketAddr } ; 
6+ use  std:: os:: fd:: { AsRawFd ,  FromRawFd ,  OwnedFd } ; 
67use  std:: str:: FromStr ; 
78
89/// Network protocol 
@@ -207,6 +208,68 @@ pub fn query_nics(filter: &[NicFilter]) -> Result<Vec<Nic>> {
207208    Ok ( filtered_nics) 
208209} 
209210
211+ /// Selects address to bind to for listening: Checks if IPv6 sockets are available on this host 
212+ /// according to our rules: IPv6 must be enabled during boot and at runtime, and IPv6 sockets must 
213+ /// be dual stack. Then it returns `::` (IPv6), otherwise `0.0.0.0` (IPv4). 
214+ pub  fn  select_bind_addr ( port :  u16 )  -> SocketAddr  { 
215+     // SAFETY: Any data used in the libc calls is local only 
216+     unsafe  { 
217+         // Check if IPv6 socket can be created 
218+         let  sock = libc:: socket ( libc:: AF_INET6 ,  libc:: SOCK_STREAM ,  0 ) ; 
219+         if  sock < 0  { 
220+             log:: info!( "IPv6 is unavailable on this host, falling back to IPv4 sockets" ) ; 
221+             return  SocketAddr :: new ( Ipv4Addr :: UNSPECIFIED . into ( ) ,  port) ; 
222+         } 
223+         // Make sure the socket is closed on drop 
224+         let  sock = OwnedFd :: from_raw_fd ( sock) ; 
225+ 
226+         // Check if we can connect the socket to ipv6. We are not interested in an actual connection 
227+         // here but rather if it fails with EADDRNOTAVAIL, which indicates ipv6 is loaded in 
228+         // kernel but disabled at runtime 
229+         libc:: fcntl ( sock. as_raw_fd ( ) ,  libc:: F_SETFL ,  libc:: O_NONBLOCK ) ; 
230+         let  addr_in6 = libc:: sockaddr_in6  { 
231+             sin6_family :  libc:: AF_INET6  as  u16 , 
232+             sin6_port :  libc:: htons ( port) , 
233+             sin6_flowinfo :  0 , 
234+             sin6_addr :  libc:: in6_addr  { 
235+                 s6_addr :  Ipv6Addr :: LOCALHOST . octets ( ) , 
236+             } , 
237+             sin6_scope_id :  0 , 
238+         } ; 
239+         let  res = libc:: connect ( 
240+             sock. as_raw_fd ( ) , 
241+             & addr_in6 as  * const  _  as  * const  _ , 
242+             size_of :: < libc:: sockaddr_in6 > ( )  as  u32 , 
243+         ) ; 
244+ 
245+         if  res < 0  && std:: io:: Error :: last_os_error ( ) . raw_os_error ( )  == Some ( libc:: EADDRNOTAVAIL )  { 
246+             log:: info!( "IPv6 is disabled on this host, falling back to IPv4 sockets" ) ; 
247+             return  SocketAddr :: new ( Ipv4Addr :: UNSPECIFIED . into ( ) ,  port) ; 
248+         } 
249+ 
250+         // Check if dual stack sockets are enabled by querying the socket option 
251+         let  mut  ipv6_only:  std:: ffi:: c_int  = 0 ; 
252+         let  mut  ipv6_only_size = size_of :: < std:: ffi:: c_int > ( ) ; 
253+ 
254+         let  res = libc:: getsockopt ( 
255+             sock. as_raw_fd ( ) , 
256+             libc:: IPPROTO_IPV6 , 
257+             libc:: IPV6_V6ONLY , 
258+             & mut  ipv6_only as  * mut  _  as  * mut  libc:: c_void , 
259+             & mut  ipv6_only_size as  * mut  _  as  * mut  libc:: socklen_t , 
260+         ) ; 
261+ 
262+         if  res < 0  || ipv6_only == 1  { 
263+             log:: info!( 
264+                 "IPv6 dual stack sockets are unavailable on this host, falling back to IPv4 sockets" 
265+             ) ; 
266+             return  SocketAddr :: new ( Ipv4Addr :: UNSPECIFIED . into ( ) ,  port) ; 
267+         } 
268+     } 
269+ 
270+     SocketAddr :: new ( Ipv6Addr :: UNSPECIFIED . into ( ) ,  port) 
271+ } 
272+ 
210273#[ cfg( test) ]  
211274mod  test { 
212275    use  super :: * ; 
0 commit comments