11use std:: {
22 io:: { self , ErrorKind } ,
33 mem,
4- net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ,
5- os:: unix:: io:: { AsRawFd , RawFd } ,
4+ net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr , TcpStream as StdTcpStream } ,
5+ os:: unix:: io:: { AsRawFd , FromRawFd , IntoRawFd , RawFd } ,
66 pin:: Pin ,
77 ptr,
88 sync:: atomic:: { AtomicBool , Ordering } ,
@@ -13,14 +13,15 @@ use log::{debug, error, warn};
1313use pin_project:: pin_project;
1414use socket2:: { Domain , Protocol , SockAddr , Socket , Type } ;
1515use tokio:: {
16- io:: { AsyncRead , AsyncWrite , ReadBuf } ,
16+ io:: { AsyncRead , AsyncWrite , Interest , ReadBuf } ,
1717 net:: { TcpSocket , TcpStream as TokioTcpStream , UdpSocket } ,
1818} ;
1919use tokio_tfo:: TfoStream ;
2020
2121use crate :: net:: {
2222 sys:: { set_common_sockopt_after_connect, set_common_sockopt_for_connect, socket_bind_dual_stack} ,
2323 udp:: { BatchRecvMessage , BatchSendMessage } ,
24+ AcceptOpts ,
2425 AddrFamily ,
2526 ConnectOpts ,
2627} ;
@@ -34,11 +35,34 @@ pub enum TcpStream {
3435
3536impl TcpStream {
3637 pub async fn connect ( addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
38+ if opts. tcp . mptcp {
39+ return TcpStream :: connect_mptcp ( addr, opts) . await ;
40+ }
41+
3742 let socket = match addr {
3843 SocketAddr :: V4 ( ..) => TcpSocket :: new_v4 ( ) ?,
3944 SocketAddr :: V6 ( ..) => TcpSocket :: new_v6 ( ) ?,
4045 } ;
4146
47+ TcpStream :: connect_with_socket ( socket, addr, opts) . await
48+ }
49+
50+ async fn connect_mptcp ( addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
51+ // https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/socket.h.auto.html
52+ const AF_MULTIPATH : libc:: c_int = 39 ;
53+
54+ let socket = unsafe {
55+ let fd = libc:: socket ( AF_MULTIPATH , libc:: SOCK_STREAM , libc:: IPPROTO_TCP ) ;
56+ let socket = Socket :: from_raw_fd ( fd) ;
57+ socket. set_nonblocking ( true ) ?;
58+ TcpSocket :: from_raw_fd ( socket. into_raw_fd ( ) )
59+ } ;
60+
61+ TcpStream :: connect_with_socket ( socket, addr, opts) . await
62+ }
63+
64+ #[ inline]
65+ async fn connect_with_socket ( socket : TcpSocket , addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
4266 // Binds to a specific network interface (device)
4367 if let Some ( ref iface) = opts. bind_interface {
4468 set_ip_bound_if ( & socket, addr, iface) ?;
@@ -48,7 +72,50 @@ impl TcpStream {
4872
4973 if !opts. tcp . fastopen {
5074 // If TFO is not enabled, it just works like a normal TcpStream
51- let stream = socket. connect ( addr) . await ?;
75+ //
76+ // But for Multipath-TCP, we must use connectx
77+ // http://blog.multipath-tcp.org/blog/html/2018/12/17/multipath_tcp_apis.html
78+ let stream = if opts. tcp . mptcp {
79+ let stream = unsafe {
80+ let raddr = SockAddr :: from ( addr) ;
81+
82+ let mut endpoints: libc:: sa_endpoints_t = mem:: zeroed ( ) ;
83+ endpoints. sae_dstaddr = raddr. as_ptr ( ) ;
84+ endpoints. sae_dstaddrlen = raddr. len ( ) ;
85+
86+ let ret = libc:: connectx (
87+ socket. as_raw_fd ( ) ,
88+ & endpoints as * const _ ,
89+ libc:: SAE_ASSOCID_ANY ,
90+ 0 ,
91+ ptr:: null ( ) ,
92+ 0 ,
93+ ptr:: null_mut ( ) ,
94+ ptr:: null_mut ( ) ,
95+ ) ;
96+
97+ if ret != 0 {
98+ let err = io:: Error :: last_os_error ( ) ;
99+ if err. raw_os_error ( ) != Some ( libc:: EINPROGRESS ) {
100+ return Err ( err) ;
101+ }
102+ }
103+
104+ let fd = socket. into_raw_fd ( ) ;
105+ TokioTcpStream :: from_std ( StdTcpStream :: from_raw_fd ( fd) ) ?
106+ } ;
107+
108+ stream. ready ( Interest :: WRITABLE ) . await ?;
109+
110+ if let Err ( err) = stream. take_error ( ) {
111+ return Err ( err) ;
112+ }
113+
114+ stream
115+ } else {
116+ socket. connect ( addr) . await ?
117+ } ;
118+
52119 set_common_sockopt_after_connect ( & stream, opts) ?;
53120 return Ok ( TcpStream :: Standard ( stream) ) ;
54121 }
@@ -155,6 +222,14 @@ pub fn set_tcp_fastopen<S: AsRawFd>(socket: &S) -> io::Result<()> {
155222 Ok ( ( ) )
156223}
157224
225+ /// Create a TCP socket for listening
226+ pub async fn create_inbound_tcp_socket ( bind_addr : & SocketAddr , _accept_opts : & AcceptOpts ) -> io:: Result < TcpSocket > {
227+ match bind_addr {
228+ SocketAddr :: V4 ( ..) => TcpSocket :: new_v4 ( ) ,
229+ SocketAddr :: V6 ( ..) => TcpSocket :: new_v6 ( ) ,
230+ }
231+ }
232+
158233fn set_ip_bound_if < S : AsRawFd > ( socket : & S , addr : SocketAddr , iface : & str ) -> io:: Result < ( ) > {
159234 const IP_BOUND_IF : libc:: c_int = 25 ; // bsd/netinet/in.h
160235 const IPV6_BOUND_IF : libc:: c_int = 125 ; // bsd/netinet6/in6.h
0 commit comments