Search code examples
linuxrustgateway

How can I send through a specific network interface?


I need send messages through different gateways dynamically. How do that and what must be my first step in this direction?

On my server I have two connections: one-direct, and secondary over VPN. Default route is direct connection, but i need dynamically change connection to VPN.

At current time I try build socket from libc::bind() it's work, but have not expected effect.

Changing the outgoing IP is not a solution to define the interface.


Solution

  • As suggested in a comment we must use SO_BINDTODEVICE, and no way to escape FFI cause it used internally. Here working example:

    extern crate libc;
    
    use libc as c;
    use std::ffi::CString;
    use std::net::{TcpStream, SocketAddr};
    use std::io::{self, Write};
    use std::os::unix::io::FromRawFd;
    use std::mem;
    
    #[cfg(any(target_os = "linux"))]
    fn connect_dev(addr: SocketAddr, link: &str) -> io::Result<TcpStream> {
        let (addr_raw, addr_len) = match addr {
            SocketAddr::V4(ref a) =>
                (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t),
            SocketAddr::V6(ref a) =>
                (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t),
        };
    
        unsafe {
            let fd = check_os_error(c::socket(c::AF_INET, c::SOCK_STREAM, 0))?;
            check_os_error(c::setsockopt(
                fd,
                c::SOL_SOCKET,
                c::SO_BINDTODEVICE,
                CString::new(link).expect("device name").as_ptr() as *const c::c_void,
                mem::size_of::<CString>() as c::socklen_t,
            ))?;
            check_os_error(c::connect(fd, addr_raw, addr_len))?;
    
            Ok(TcpStream::from_raw_fd(fd))
        }
    }
    
    #[cfg(any(target_os = "linux"))]
    pub fn check_os_error(res: c::c_int) -> io::Result<c::c_int> {
        if res == -1 {
            Err(io::Error::from_raw_os_error(unsafe { *c::__errno_location()  as i32 }))
        } else {
            Ok(res)
        }
    }