Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set the socket option SO_REUSEPORT in Rust?

I've read the documentation for std::net and mio, and I've found some methods like set_nodelay and set_keepalive, but I haven't found a way to set other socket options like SO_REUSEPORT and SO_REUSEADDR on a given socket. How can I do this?

like image 260
huron Avatar asked Nov 07 '16 15:11

huron


1 Answers

Because SO_REUSEPORT isn't cross-platform, you will need to dip into platform-specific code. In this case, you can get the raw file descriptor from the socket and then use functions, types, and values from the libc crate to set the options you want:

extern crate libc; // 0.2.43

use std::{io, mem, net::TcpListener, os::unix::io::AsRawFd};

fn main() -> Result<(), io::Error> {
    let listener = TcpListener::bind("0.0.0.0:8888")?;

    unsafe {
        let optval: libc::c_int = 1;
        let ret = libc::setsockopt(
            listener.as_raw_fd(),
            libc::SOL_SOCKET,
            libc::SO_REUSEPORT,
            &optval as *const _ as *const libc::c_void,
            mem::size_of_val(&optval) as libc::socklen_t,
        );
        if ret != 0 {
            return Err(io::Error::last_os_error());
        }
    }

    Ok(())
}

I make no guarantee that this is the right place to set this option, or that I haven't screwed up something in the unsafe block, but it does compile and run on macOS 10.12.

A better solution may be to check out the nix crate, which provides nicer wrappers for most *nix-specific code:

extern crate nix; // 0.11.0

use nix::sys::socket::{self, sockopt::ReusePort};
use std::{error::Error, net::TcpListener, os::unix::io::AsRawFd};

fn main() -> Result<(), Box<Error>> {
    let listener = TcpListener::bind("0.0.0.0:8888")?;
    socket::setsockopt(listener.as_raw_fd(), ReusePort, &true)?;

    Ok(())
}

An even better solution may be to check out the net2 crate, which provides higher-level methods aimed specifically at networking-related code:

extern crate net2; // 0.2.33

use net2::{unix::UnixTcpBuilderExt, TcpBuilder};

fn main() -> Result<(), std::io::Error> {
    let listener = TcpBuilder::new_v4()?
        .reuse_address(true)?
        .reuse_port(true)?
        .bind("0.0.0.0:8888")?
        .listen(42)?;

    Ok(())
}
like image 199
Shepmaster Avatar answered Nov 19 '22 20:11

Shepmaster