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,16 +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- AddrFamily ,
25- ConnectOpts ,
24+ AcceptOpts , AddrFamily , ConnectOpts ,
2625} ;
2726
2827/// A `TcpStream` that supports TFO (TCP Fast Open)
@@ -34,11 +33,34 @@ pub enum TcpStream {
3433
3534impl TcpStream {
3635 pub async fn connect ( addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
36+ if opts. tcp . mptcp {
37+ return TcpStream :: connect_mptcp ( addr, opts) . await ;
38+ }
39+
3740 let socket = match addr {
3841 SocketAddr :: V4 ( ..) => TcpSocket :: new_v4 ( ) ?,
3942 SocketAddr :: V6 ( ..) => TcpSocket :: new_v6 ( ) ?,
4043 } ;
4144
45+ TcpStream :: connect_with_socket ( socket, addr, opts) . await
46+ }
47+
48+ async fn connect_mptcp ( addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
49+ // https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/socket.h.auto.html
50+ const AF_MULTIPATH : libc:: c_int = 39 ;
51+
52+ let socket = unsafe {
53+ let fd = libc:: socket ( AF_MULTIPATH , libc:: SOCK_STREAM , libc:: IPPROTO_TCP ) ;
54+ let socket = Socket :: from_raw_fd ( fd) ;
55+ socket. set_nonblocking ( true ) ?;
56+ TcpSocket :: from_raw_fd ( socket. into_raw_fd ( ) )
57+ } ;
58+
59+ TcpStream :: connect_with_socket ( socket, addr, opts) . await
60+ }
61+
62+ #[ inline]
63+ async fn connect_with_socket ( socket : TcpSocket , addr : SocketAddr , opts : & ConnectOpts ) -> io:: Result < TcpStream > {
4264 // Binds to a specific network interface (device)
4365 if let Some ( ref iface) = opts. bind_interface {
4466 set_ip_bound_if ( & socket, addr, iface) ?;
@@ -48,7 +70,50 @@ impl TcpStream {
4870
4971 if !opts. tcp . fastopen {
5072 // If TFO is not enabled, it just works like a normal TcpStream
51- let stream = socket. connect ( addr) . await ?;
73+ //
74+ // But for Multipath-TCP, we must use connectx
75+ // http://blog.multipath-tcp.org/blog/html/2018/12/17/multipath_tcp_apis.html
76+ let stream = if opts. tcp . mptcp {
77+ let stream = unsafe {
78+ let raddr = SockAddr :: from ( addr) ;
79+
80+ let mut endpoints: libc:: sa_endpoints_t = mem:: zeroed ( ) ;
81+ endpoints. sae_dstaddr = raddr. as_ptr ( ) ;
82+ endpoints. sae_dstaddrlen = raddr. len ( ) ;
83+
84+ let ret = libc:: connectx (
85+ socket. as_raw_fd ( ) ,
86+ & endpoints as * const _ ,
87+ libc:: SAE_ASSOCID_ANY ,
88+ 0 ,
89+ ptr:: null ( ) ,
90+ 0 ,
91+ ptr:: null_mut ( ) ,
92+ ptr:: null_mut ( ) ,
93+ ) ;
94+
95+ if ret != 0 {
96+ let err = io:: Error :: last_os_error ( ) ;
97+ if err. raw_os_error ( ) != Some ( libc:: EINPROGRESS ) {
98+ return Err ( err) ;
99+ }
100+ }
101+
102+ let fd = socket. into_raw_fd ( ) ;
103+ TokioTcpStream :: from_std ( StdTcpStream :: from_raw_fd ( fd) ) ?
104+ } ;
105+
106+ stream. ready ( Interest :: WRITABLE ) . await ?;
107+
108+ if let Err ( err) = stream. take_error ( ) {
109+ return Err ( err) ;
110+ }
111+
112+ stream
113+ } else {
114+ socket. connect ( addr) . await ?
115+ } ;
116+
52117 set_common_sockopt_after_connect ( & stream, opts) ?;
53118 return Ok ( TcpStream :: Standard ( stream) ) ;
54119 }
@@ -155,6 +220,14 @@ pub fn set_tcp_fastopen<S: AsRawFd>(socket: &S) -> io::Result<()> {
155220 Ok ( ( ) )
156221}
157222
223+ /// Create a TCP socket for listening
224+ pub async fn create_inbound_tcp_socket ( bind_addr : & SocketAddr , _accept_opts : & AcceptOpts ) -> io:: Result < TcpSocket > {
225+ match bind_addr {
226+ SocketAddr :: V4 ( ..) => TcpSocket :: new_v4 ( ) ,
227+ SocketAddr :: V6 ( ..) => TcpSocket :: new_v6 ( ) ,
228+ }
229+ }
230+
158231fn set_ip_bound_if < S : AsRawFd > ( socket : & S , addr : SocketAddr , iface : & str ) -> io:: Result < ( ) > {
159232 const IP_BOUND_IF : libc:: c_int = 25 ; // bsd/netinet/in.h
160233 const IPV6_BOUND_IF : libc:: c_int = 125 ; // bsd/netinet6/in6.h
0 commit comments