linkerd2-proxy/proxy/src/transport/so_original_dst.rs

113 lines
3.7 KiB
Rust

use std::net::SocketAddr;
use tokio_core::net::TcpStream;
#[cfg(not(target_os = "linux"))]
pub fn get_original_dst(_: &TcpStream) -> Option<SocketAddr> {
debug!("no support for SO_ORIGINAL_DST");
None
}
// TODO change/remove once https://github.com/tokio-rs/tokio/issues/25 is addressed
#[cfg(target_os = "linux")]
pub fn get_original_dst(sock: &TcpStream) -> Option<SocketAddr> {
use self::linux;
use std::os::unix::io::AsRawFd;
debug!("get_original_dst {:?}", sock);
let res = unsafe { linux::so_original_dst(sock.as_raw_fd()) };
res.ok()
}
#[cfg(target_os = "linux")]
mod linux {
use libc;
use std::{io, mem};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::os::unix::io::RawFd;
pub unsafe fn so_original_dst(fd: RawFd) -> io::Result<SocketAddr> {
let mut sockaddr: libc::sockaddr_storage = mem::zeroed();
let mut socklen: libc::socklen_t = mem::size_of::<libc::sockaddr_storage>() as u32;
let ret = libc::getsockopt(
fd,
libc::SOL_IP,
libc::SO_ORIGINAL_DST,
&mut sockaddr as *mut _ as *mut _,
&mut socklen as *mut _ as *mut _,
);
if ret != 0 {
let e = io::Error::last_os_error();
error!("failed to read SO_ORIGINAL_DST: {:?}", e);
return Err(e);
}
mk_addr(&sockaddr, socklen)
}
// Borrowed with love from net2-rs
// https://github.com/rust-lang-nursery/net2-rs/blob/1b4cb4fb05fbad750b271f38221eab583b666e5e/src/socket.rs#L103
fn mk_addr(storage: &libc::sockaddr_storage, len: libc::socklen_t) -> io::Result<SocketAddr> {
match storage.ss_family as libc::c_int {
libc::AF_INET => {
assert!(len as usize >= mem::size_of::<libc::sockaddr_in>());
let sa = {
let sa = storage as *const _ as *const libc::sockaddr_in;
unsafe { *sa }
};
let bits = ntoh32(sa.sin_addr.s_addr);
let ip = Ipv4Addr::new(
(bits >> 24) as u8,
(bits >> 16) as u8,
(bits >> 8) as u8,
bits as u8,
);
let port = sa.sin_port;
Ok(SocketAddr::V4(SocketAddrV4::new(ip, ntoh16(port))))
}
libc::AF_INET6 => {
assert!(len as usize >= mem::size_of::<libc::sockaddr_in6>());
let sa = {
let sa = storage as *const _ as *const libc::sockaddr_in6;
unsafe { *sa }
};
let arr = sa.sin6_addr.s6_addr;
let ip = Ipv6Addr::new(
(arr[0] as u16) << 8 | (arr[1] as u16),
(arr[2] as u16) << 8 | (arr[3] as u16),
(arr[4] as u16) << 8 | (arr[5] as u16),
(arr[6] as u16) << 8 | (arr[7] as u16),
(arr[8] as u16) << 8 | (arr[9] as u16),
(arr[10] as u16) << 8 | (arr[11] as u16),
(arr[12] as u16) << 8 | (arr[13] as u16),
(arr[14] as u16) << 8 | (arr[15] as u16),
);
let port = sa.sin6_port;
let flowinfo = sa.sin6_flowinfo;
let scope_id = sa.sin6_scope_id;
Ok(SocketAddr::V6(
SocketAddrV6::new(ip, ntoh16(port), flowinfo, scope_id),
))
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid argument",
)),
}
}
fn ntoh16(i: u16) -> u16 {
<u16>::from_be(i)
}
fn ntoh32(i: u32) -> u32 {
<u32>::from_be(i)
}
}