bind(): "Cannot assign request address"

Issue

I am aware of bind: cannot assign requested address and Cannot assign requested address – possible causes?, I have not been able to derive a solution from these.

I am trying to create a TCP stream (specifically here a std::net::TcpListener) directly with libc. I am encountering the error Cannot assign requested address and error code: 99 when running my code.

The exact output being:

error message: error code: : Cannot assign requested address
thread 'main' panicked at 'error code: 99', src/main.rs:43:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

My code (playground):

use libc; // 0.2.136

use std::mem::{size_of, transmute};

fn main() {
    // Create socket
    let socket_fd = unsafe {
        let socket = libc::socket(libc::AF_INET6, libc::SOCK_STREAM | libc::SOCK_NONBLOCK, 0);
        assert_ne!(socket, -1);
        let optval = 1i32;
        let res = libc::setsockopt(
            socket,
            libc::SOL_SOCKET,
            libc::SO_REUSEPORT,
            (&optval as *const i32).cast(),
            4,
        );
        assert_eq!(res, 0);
        socket
    };

    // Bind socket
    
    // decimal 127.0.0.1 -> hexadecimal 007f.0000.0000.0001
    let sin6_addr = unsafe { transmute::<_, libc::in6_addr>(*b"007f000000000001") };
    let socket_address = libc::sockaddr_in6 {
        sin6_family: libc::AF_INET6 as u16,
        sin6_port: 8080,
        sin6_flowinfo: u32::default(),
        sin6_addr,
        sin6_scope_id: u32::default(),
    };
    let socket_address_length = size_of::<libc::sockaddr_in6>() as u32;
    unsafe {
        let res = libc::bind(
            socket_fd,
            (&socket_address as *const libc::sockaddr_in6).cast(),
            socket_address_length,
        );
        if res == -1 {
            let err = errno();
            print_error("error message: ");
            panic!("error code: {err}");
        }
    }
    
    assert_eq!(unsafe { libc::close(socket_fd) },0);
}

fn print_error(s: &str) {
    unsafe {
        libc::perror(s.as_ptr().cast());
    }
}
fn errno() -> i32 {
    unsafe { *libc::__errno_location() }
}

I set the option SO_REUSEPORT here as I need to create a stream that multiple processes can listen to.

https://lwn.net/Articles/542629/:

The basic concept of SO_REUSEPORT is simple enough. Multiple servers (processes or threads) can bind to the same port

The port does not appear to be in use:

[email protected]:~$ sudo lsof -i -P -n
COMMAND    PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r  793 systemd-resolve   13u  IPv4  19452      0t0  UDP 127.0.0.53:53 
systemd-r  793 systemd-resolve   14u  IPv4  19453      0t0  TCP 127.0.0.53:53 (LISTEN)
avahi-dae  855           avahi   12u  IPv4  31377      0t0  UDP *:5353 
avahi-dae  855           avahi   13u  IPv6  31378      0t0  UDP *:5353 
avahi-dae  855           avahi   14u  IPv4  31379      0t0  UDP *:49594 
avahi-dae  855           avahi   15u  IPv6  31380      0t0  UDP *:58397 
NetworkMa  859            root   36u  IPv4  36212      0t0  UDP 192.168.0.22:68->192.168.0.1:67 
NetworkMa  859            root   37u  IPv4 110801      0t0  UDP 192.168.0.17:68->192.168.0.1:67 
tor       1038      debian-tor    6u  IPv4  31407      0t0  TCP 127.0.0.1:9050 (LISTEN)
rust-anal 4117        jonathan   46u  IPv4  33176      0t0  UDP 127.0.0.1:35972->127.0.0.53:53 
rust-anal 4189        jonathan   46u  IPv4  33176      0t0  UDP 127.0.0.1:35972->127.0.0.53:53 
firefox   4297        jonathan    3u  IPv4 112786      0t0  TCP 192.168.0.22:46702->151.101.1.69:443 (ESTABLISHED)
firefox   4297        jonathan   29u  IPv4 100032      0t0  TCP 192.168.0.22:55828->34.120.208.123:443 (ESTABLISHED)
firefox   4297        jonathan   56u  IPv4 115182      0t0  TCP 192.168.0.22:50798->52.51.190.37:443 (ESTABLISHED)
firefox   4297        jonathan   75u  IPv4  95741      0t0  TCP 192.168.0.22:48466->142.250.200.10:443 (ESTABLISHED)
firefox   4297        jonathan   81u  IPv4  67879      0t0  TCP 192.168.0.22:55638->34.214.17.205:443 (ESTABLISHED)
firefox   4297        jonathan  116u  IPv4 111739      0t0  TCP 192.168.0.22:37986->172.217.169.68:443 (ESTABLISHED)
firefox   4297        jonathan  121u  IPv4  95751      0t0  TCP 192.168.0.22:46054->198.252.206.25:443 (ESTABLISHED)
firefox   4297        jonathan  129u  IPv4 102073      0t0  TCP 192.168.0.22:51478->34.120.237.76:443 (ESTABLISHED)
firefox   4297        jonathan  136u  IPv4  96742      0t0  TCP 192.168.0.22:36606->34.120.115.102:443 (ESTABLISHED)
firefox   4297        jonathan  139u  IPv4  97943      0t0  TCP 192.168.0.22:36626->172.217.169.68:443 (ESTABLISHED)
firefox   4297        jonathan  144u  IPv4  99520      0t0  TCP 192.168.0.22:49264->198.252.206.25:443 (ESTABLISHED)
firefox   4297        jonathan  157u  IPv4 104859      0t0  TCP 192.168.0.22:58042->93.184.220.29:80 (ESTABLISHED)
[email protected]:~$ 

Solution

You are transmuting b"007f000000000001" into a libc::in6_addr. But I don’t thing that is the proper thing to do.

Looking at the man page, that struct is:

struct in6_addr {
   uint8_t   s6_addr[16];
};

That is, just 16 bytes, the proper thing for an IPv6. But your bytes are actually the ASCII values of that string: 30303766..., that while technically an IPv6 address, it is not a local one of yours so you cannot bind to it.

Moreover, in IPv6 the proper localhost address is ::1, not 127.0.0.1. That is 15 zeros followed by a single one.

If you want to bind to the IPv6 localhost by using a transmute that would be:

let sin6_addr = unsafe { transmute::<_, libc::in6_addr>([0_u8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]) };

Or if you insist on using a literal string (why?):

let sin6_addr = unsafe { transmute::<_, libc::in6_addr>(*b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01") };

Answered By – rodrigo

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published