@@ -38,15 +38,30 @@ impl std::error::Error for NodeError {}
3838
3939// ── TUN setup (Android only) ────────────────────────────────────────────────
4040
41- /// On Android the `tun` crate expects an already-opened file descriptor.
42- /// This function opens `/dev/tun`, configures the interface with TUNSETIFF,
43- /// and returns the raw fd. Requires `CAP_NET_ADMIN`.
41+ /// Open and fully configure a TUN file descriptor on Android.
42+ ///
43+ /// The `tun/android.rs` data-plane code expects an already-configured fd
44+ /// (the same contract the `mobile/` crate satisfies via `VpnService.Builder`).
45+ /// For the FFI path there is no VpnService doing that work, so this function
46+ /// does it: opens `/dev/tun`, sets `IFF_TUN | IFF_NO_PI` via `TUNSETIFF`,
47+ /// assigns `node_addr` with `prefix_len` (`/7` causes the kernel to install
48+ /// the on-link route for the global mycelium subnet), sets the MTU, and
49+ /// brings the interface up.
50+ ///
51+ /// Requires `CAP_NET_ADMIN`.
4452#[ cfg( target_os = "android" ) ]
45- pub fn create_tun_fd ( tun_name : & str ) -> Result < i32 , std:: io:: Error > {
53+ pub fn create_tun_fd (
54+ tun_name : & str ,
55+ node_addr : std:: net:: Ipv6Addr ,
56+ prefix_len : u8 ,
57+ mtu : i32 ,
58+ ) -> Result < i32 , std:: io:: Error > {
4659 const TUNSETIFF : libc:: c_ulong = 0x400454ca ;
4760 const IFF_TUN : libc:: c_short = 0x0001 ;
4861 const IFF_NO_PI : libc:: c_short = 0x1000 ;
4962
63+ // SAFETY: the path is a static NUL-terminated byte string; `open` has no
64+ // other preconditions.
5065 let fd = unsafe { libc:: open ( b"/dev/tun\0 " . as_ptr ( ) as * const libc:: c_char , libc:: O_RDWR ) } ;
5166 if fd < 0 {
5267 return Err ( std:: io:: Error :: last_os_error ( ) ) ;
@@ -60,16 +75,117 @@ pub fn create_tun_fd(tun_name: &str) -> Result<i32, std::io::Error> {
6075 ifr[ 16 ] = ( flags & 0xff ) as u8 ;
6176 ifr[ 17 ] = ( ( flags >> 8 ) & 0xff ) as u8 ;
6277
78+ // SAFETY: `fd` is a valid file descriptor that we just opened. `ifr` is a
79+ // 40-byte buffer matching the kernel's `struct ifreq` layout, with name
80+ // bytes (offset 0..15), a NUL terminator (offset 15, left at zero), and
81+ // `ifr_flags` (offset 16..18) populated; remaining bytes are zero, which
82+ // is what TUNSETIFF expects for unused union members.
6383 let ret = unsafe { libc:: ioctl ( fd, TUNSETIFF as i32 , ifr. as_ptr ( ) ) } ;
6484 if ret < 0 {
6585 let err = std:: io:: Error :: last_os_error ( ) ;
86+ // SAFETY: `fd` is the open fd from the `open` call above; nothing else
87+ // can reference it because it has not escaped this function.
6688 unsafe { libc:: close ( fd) } ;
6789 return Err ( err) ;
6890 }
6991
92+ if let Err ( e) = configure_tun_interface ( tun_name, node_addr, prefix_len, mtu) {
93+ // SAFETY: `fd` is the open fd from the `open` call above; nothing else
94+ // can reference it because it has not escaped this function.
95+ unsafe { libc:: close ( fd) } ;
96+ return Err ( e) ;
97+ }
98+
7099 Ok ( fd)
71100}
72101
102+ /// Assign address, set MTU and bring the named interface up via an
103+ /// `AF_INET6` control socket. `SIOCSIFADDR` for IPv6 requires `AF_INET6`.
104+ #[ cfg( target_os = "android" ) ]
105+ fn configure_tun_interface (
106+ tun_name : & str ,
107+ node_addr : std:: net:: Ipv6Addr ,
108+ prefix_len : u8 ,
109+ mtu : i32 ,
110+ ) -> std:: io:: Result < ( ) > {
111+ // SAFETY: `socket(2)` has no preconditions; the arguments are valid
112+ // constants exported by libc.
113+ let sock = unsafe { libc:: socket ( libc:: AF_INET6 , libc:: SOCK_DGRAM , 0 ) } ;
114+ if sock < 0 {
115+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
116+ }
117+
118+ let result = ( || -> std:: io:: Result < ( ) > {
119+ // SAFETY: `libc::ifreq` is a `repr(C)` aggregate of POD types and a
120+ // C union; the all-zero bit pattern is a valid value of every
121+ // variant (the union members are all integers or fixed-size byte
122+ // arrays), and the kernel ignores fields not consumed by the
123+ // specific ioctl request.
124+ let mut ifr: libc:: ifreq = unsafe { std:: mem:: zeroed ( ) } ;
125+ let name_bytes = tun_name. as_bytes ( ) ;
126+ let copy_len = name_bytes. len ( ) . min ( libc:: IFNAMSIZ - 1 ) ;
127+ for ( dst, & src) in ifr. ifr_name [ ..copy_len] . iter_mut ( ) . zip ( name_bytes) {
128+ * dst = src as libc:: c_char ;
129+ }
130+
131+ // SAFETY: `sock` is a valid socket fd opened above; `ifr` is fully
132+ // initialised with the interface name in `ifr_name` (NUL-terminated:
133+ // we copied at most IFNAMSIZ-1 bytes into a zero-initialised buffer)
134+ // — the layout expected by SIOCGIFINDEX.
135+ if unsafe { libc:: ioctl ( sock, libc:: SIOCGIFINDEX as _ , & mut ifr) } < 0 {
136+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
137+ }
138+ // SAFETY: SIOCGIFINDEX populates the `ifru_ifindex` union variant on
139+ // success; the ioctl returned >= 0 above.
140+ let ifindex = unsafe { ifr. ifr_ifru . ifru_ifindex } ;
141+
142+ let ifr6 = libc:: in6_ifreq {
143+ ifr6_addr : libc:: in6_addr {
144+ s6_addr : node_addr. octets ( ) ,
145+ } ,
146+ ifr6_prefixlen : prefix_len as u32 ,
147+ ifr6_ifindex : ifindex,
148+ } ;
149+ // SAFETY: `sock` is a valid AF_INET6 socket fd (required for IPv6
150+ // SIOCSIFADDR); `ifr6` is a fully initialised `in6_ifreq` with the
151+ // address, prefix length, and interface index expected by the ioctl.
152+ if unsafe { libc:: ioctl ( sock, libc:: SIOCSIFADDR as _ , & ifr6) } < 0 {
153+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
154+ }
155+
156+ ifr. ifr_ifru . ifru_mtu = mtu;
157+ // SAFETY: `sock` is a valid socket fd; `ifr` has the interface name
158+ // set in `ifr_name` and the MTU written into the `ifru_mtu` union
159+ // variant — the layout SIOCSIFMTU expects.
160+ if unsafe { libc:: ioctl ( sock, libc:: SIOCSIFMTU as _ , & ifr) } < 0 {
161+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
162+ }
163+
164+ // SAFETY: `sock` is a valid socket fd; `ifr_name` is still set from
165+ // the SIOCGIFINDEX call above (the kernel only writes the `ifr_ifru`
166+ // union on success, leaving `ifr_name` intact).
167+ if unsafe { libc:: ioctl ( sock, libc:: SIOCGIFFLAGS as _ , & mut ifr) } < 0 {
168+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
169+ }
170+ // SAFETY: SIOCGIFFLAGS populates the `ifru_flags` union variant on
171+ // success; the ioctl returned >= 0 above.
172+ ifr. ifr_ifru . ifru_flags = unsafe { ifr. ifr_ifru . ifru_flags } | libc:: IFF_UP as libc:: c_short ;
173+ // SAFETY: `sock` is a valid socket fd; `ifr` has the interface name
174+ // set and the updated flags written into the `ifru_flags` union
175+ // variant.
176+ if unsafe { libc:: ioctl ( sock, libc:: SIOCSIFFLAGS as _ , & ifr) } < 0 {
177+ return Err ( std:: io:: Error :: last_os_error ( ) ) ;
178+ }
179+
180+ Ok ( ( ) )
181+ } ) ( ) ;
182+
183+ // SAFETY: `sock` is the fd from the `socket` call above; it has not
184+ // escaped this function and is no longer used.
185+ unsafe { libc:: close ( sock) } ;
186+ result
187+ }
188+
73189// ── NodeHandle ──────────────────────────────────────────────────────────────
74190
75191/// Upper bound on how long node teardown waits for the background Tokio
0 commit comments