Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TCP FastOpen support for macOS #1632

Closed
zonyitoo opened this issue Dec 30, 2019 · 1 comment · Fixed by #1635
Closed

Add TCP FastOpen support for macOS #1632

zonyitoo opened this issue Dec 30, 2019 · 1 comment · Fixed by #1635

Comments

@zonyitoo
Copy link
Contributor

zonyitoo commented Dec 30, 2019

The connectx system call

This function is commonly used for inititiating a TCP Fast Open connection.

Available since iOS 9 and OS X 10.11

// Original definition in /usr/include/sys

__API_AVAILABLE(macosx(10.11), ios(9.0), tvos(9.0), watchos(2.0))
int connectx(int, const sa_endpoints_t *, sae_associd_t, unsigned int,
    const struct iovec *, unsigned int, size_t *, sae_connid_t *);

__API_AVAILABLE(macosx(10.11), ios(9.0), tvos(9.0), watchos(2.0))
int disconnectx(int, sae_associd_t, sae_connid_t);

Definition:

int connectx(int socket,
             const sa_endpoints_t *endpoints,
             sae_associd_t associd /* Reserved, always be SAE_ASSOCID_ANY */,
             unsigned int flags,
             const struct iovec *iov,
             unsigned int iovcnt,
             size_t *len,
             sae_connid_t *connid /* Reserved, always be NULL */);

Example usage:

struct sockaddr_in sa;
memcpy(&sa, /* TARGET ADDR */, sizeof(struct sockaddr_in));
sa.sin_len = sizeof(struct sockaddr_in);
sa_endpoints_t endpoints;
memset((char *)&endpoints, 0, sizeof(endpoints));
endpoints.sae_dstaddr    = (struct sockaddr *)&sa;
endpoints.sae_dstaddrlen = /* TARGET ADDR LEN */;

s = connectx(sockfd,
             &endpoints,
             SAE_ASSOCID_ANY,
             CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
             NULL,
             0,
             NULL,
             NULL);

Variable definitions

  • SAE_ASSOCID_ANY

    // Defined in /usr/include/sys/socket.h (MacOSX.sdk)
    #define SAE_ASSOCID_ANY 0
  • CONNECT_DATA_IDEMPOTENT and CONNECT_RESUME_ON_READ_WRITE

    // Defined in /usr/include/sys/socket.h (MacOSX.sdk)
    #define CONNECT_RESUME_ON_READ_WRITE    0x1 /* resume connect() on read/write */
    #define CONNECT_DATA_IDEMPOTENT         0x2 /* data is idempotent */

sa_endpoints_t

// Defined in /usr/include/sys/socket.h (MacOSX.sdk)

/* sockaddr endpoints */
typedef struct sa_endpoints {
	unsigned int            sae_srcif;      /* optional source interface */
	const struct sockaddr   *sae_srcaddr;   /* optional source address */
	socklen_t               sae_srcaddrlen; /* size of source address */
	const struct sockaddr   *sae_dstaddr;   /* destination address */
	socklen_t               sae_dstaddrlen; /* size of destination address */
} sa_endpoints_t;

sae_associd_t

// Defined in /usr/include/sys/socket.h (MacOSX.sdk)

typedef __uint32_t sae_associd_t;
@zonyitoo
Copy link
Contributor Author

zonyitoo commented Dec 31, 2019

Client

use std::mem;
use std::ptr;

use libc;

extern "C" {
    fn inet_pton(af: libc::c_int, src: *const libc::c_char, dst: *mut libc::c_void) -> libc::c_int;
    fn htons(hostshort: u16) -> u16;
}

fn main() {
    unsafe {
        let socket = libc::socket(libc::PF_INET, libc::SOCK_STREAM, libc::IPPROTO_TCP);
        assert!(socket > 0);

        println!("Socket {}", socket);

        let mut saddr: libc::sockaddr_in = mem::zeroed();
        saddr.sin_family = libc::AF_INET as libc::sa_family_t;
        saddr.sin_port = htons(8000);
        inet_pton(
            libc::AF_INET,
            "127.0.0.1".as_ptr() as *const _,
            &mut saddr.sin_addr as *mut _ as *mut libc::c_void,
        );

        let mut endpoints: libc::sa_endpoints_t = mem::zeroed();
        endpoints.sae_dstaddr = &mut saddr as *mut _ as *mut libc::sockaddr;
        endpoints.sae_dstaddrlen = mem::size_of_val(&saddr) as libc::socklen_t;

        let ret = libc::connectx(
            socket,
            &mut endpoints as *mut _,
            libc::SAE_ASSOCID_ANY,
            libc::CONNECT_DATA_IDEMPOTENT,
            ptr::null(),
            0,
            ptr::null_mut(),
            ptr::null_mut(),
        );
        assert!(ret == 0);

        let ret = libc::disconnectx(socket, libc::SAE_ASSOCID_ANY, libc::SAE_CONNID_ANY);
        assert!(ret == 0);
    }
}

Server

use std::mem;

use libc;

extern "C" {
    fn inet_pton(af: libc::c_int, src: *const libc::c_char, dst: *mut libc::c_void) -> libc::c_int;
    fn htons(hostshort: u16) -> u16;
}

fn main() {
    unsafe {
        let socket = libc::socket(libc::PF_INET, libc::SOCK_STREAM, 0);
        assert!(socket > 0);

        println!("Socket {}", socket);

        let mut saddr: libc::sockaddr_in = mem::zeroed();
        saddr.sin_family = libc::AF_INET as libc::sa_family_t;
        saddr.sin_port = htons(8000);
        inet_pton(
            libc::AF_INET,
            "127.0.0.1".as_ptr() as *const _,
            &mut saddr.sin_addr as *mut _ as *mut libc::c_void,
        );

        let ret = libc::bind(
            socket,
            &saddr as *const _ as *const libc::sockaddr,
            mem::size_of_val(&saddr) as libc::socklen_t,
        );
        assert!(ret == 0);

        let queue_len: libc::c_int = 5;

        let ret = libc::listen(socket, queue_len);
        assert!(ret == 0);

        let enable: libc::c_int = 1;

        let ret = libc::setsockopt(
            socket,
            libc::IPPROTO_TCP,
            libc::TCP_FASTOPEN,
            &enable as *const _ as *const libc::c_void,
            mem::size_of_val(&enable) as libc::socklen_t,
        );
        assert!(ret == 0);

        loop {
            let mut saddr: libc::sockaddr = mem::zeroed();
            let mut slen: libc::socklen_t = 0;
            let csocket = libc::accept(socket, &mut saddr, &mut slen);
            assert!(csocket > 0);

            println!("Accepted csocket {}", csocket);

            libc::close(csocket);
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant