如何通过特定的网络接口发送?

时间:2018-09-10 11:41:50

标签: linux rust gateway

我需要通过不同的网关动态发送消息。该如何做?我朝这个方向迈出的第一步是什么?

在我的服务器上,我有两个连接:一个是直接连接,另一个是通过VPN的连接。默认路由是直接连接,但我需要动态更改与VPN的连接。

目前,我尝试从libc::bind()构建套接字,这是可行的,但效果并不理想。

Changing the outgoing IP不是定义接口的解决方案。

2 个答案:

答案 0 :(得分:0)

您可以使用其他源IP。我假设您已将系统配置为将不同的源IP路由到不同的网关(如果没有,则是操作员的问题,而不是程序员的问题)。您可以在bind函数中为套接字指定不同的源IP。通常,您传递它们的“默认”值(0.0.0.0),这意味着“任何操作系统认为合理的值”,但是您可以为任务指定确切的源IP。

C bind签名:

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

addr可能包含特定地址。

答案 1 :(得分:0)

如评论中所建议,我们必须使用SO_BINDTODEVICE,并且没有办法逃脱FFI,因为它在内部使用。 这里的工作示例:

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