Gedweb
Gedweb

Reputation: 739

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.

Upvotes: 2

Views: 2862

Answers (2)

Gedweb
Gedweb

Reputation: 739

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)
    }
}

Upvotes: 1

George Shuklin
George Shuklin

Reputation: 7897

You can use a different source IPs. I assume that you have your system configured to route different source IPs into different gateways (if not, it's an operator problem, not a programmer). You can specify different source IP in bind function for socket. Normally you pass their 'default' value (0.0.0.0) which means 'anything OS find reasonable', but you can specify exact source IP for your task.

A C bind signature:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

addr may contain the specific address.

Upvotes: 0

Related Questions