From 2a65c98c0c2c9279ea80621fac886431325e9bef Mon Sep 17 00:00:00 2001 From: German Service Network Date: Sat, 25 Apr 2026 09:33:06 +0200 Subject: [PATCH 1/3] New option --oiface for outgoing interface --- .github/workflows/rust-ci.yml | 34 +++++++- CHANGELOG.md | 4 + README.md | 1 + ci/test-oiface.sh | 79 +++++++++++++++++++ src/args.rs | 4 + src/pinger.rs | 24 +++++- src/socket.rs | 141 +++++++++++++++++++++++++++++----- 7 files changed, 264 insertions(+), 23 deletions(-) create mode 100755 ci/test-oiface.sh diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 74be4de..f890675 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -33,4 +33,36 @@ jobs: run: cargo build --all-features --verbose - name: Run tests - run: cargo test --all-features --verbose -- --nocapture \ No newline at end of file + run: cargo test --all-features --verbose -- --nocapture + + Test-Asymmetric-Routing: + needs: [ci] + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-24.04] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build + run: cargo build --all-features --verbose + + - name: Install tcpdump and net-tools + run: | + sudo apt-get update -qq + sudo apt-get install -y tcpdump net-tools + + - name: Test asymmetric routing (--oiface) + run: sudo ci/test-oiface.sh \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 699c9be..71c8e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +fping-rust 0.2.0 (new) +====================== +- New option --oiface for outgoing interface (#18, @gsnw-sebast) + fping-rust 0.1.1 (2026-03-28) ====================== - fix: validate ICMP ID per socket, fix dual-stack ID mismatch (#15, @gsnw-sebast) diff --git a/README.md b/README.md index f2a24df..3b8ea68 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ Options: -6, --ipv6 Use IPv6 only --report-all-rtts Show all individual RTTs -x, --reachable Minimum number of reachable hosts to be considered success + --oiface Bind outgoing packets to this network interface (e.g. eth0) -h, --help Print help -V, --version Print version ``` diff --git a/ci/test-oiface.sh b/ci/test-oiface.sh new file mode 100755 index 0000000..f9ce688 --- /dev/null +++ b/ci/test-oiface.sh @@ -0,0 +1,79 @@ +set -ex + +NS_TESTER="tester_ns" +NS_TARGET="target_ns" +LOG_FILE="/tmp/asym_trace.log" + +cleanup() { + echo "--- FINALER TRACE-LOG ---" + [ -f "$LOG_FILE" ] && cat $LOG_FILE + sudo ip netns del $NS_TESTER 2>/dev/null || true + sudo ip netns del $NS_TARGET 2>/dev/null || true +} +trap cleanup EXIT + +echo "--- Network-Setup ---" +sudo ip netns add $NS_TESTER +sudo ip netns add $NS_TARGET + +sudo ip link add veth1_tst type veth peer name veth1_trg +sudo ip link add veth2_tst type veth peer name veth2_trg + +sudo ip link set veth1_tst netns $NS_TESTER +sudo ip link set veth2_tst netns $NS_TESTER +sudo ip link set veth1_trg netns $NS_TARGET +sudo ip link set veth2_trg netns $NS_TARGET + +sudo ip netns exec $NS_TESTER ip addr add 10.0.1.1/24 dev veth1_tst +sudo ip netns exec $NS_TESTER ip addr add 10.0.2.1/24 dev veth2_tst +sudo ip netns exec $NS_TARGET ip addr add 10.0.1.2/24 dev veth1_trg +sudo ip netns exec $NS_TARGET ip addr add 10.0.2.2/24 dev veth2_trg + +sudo ip netns exec $NS_TESTER ip link set veth1_tst up +sudo ip netns exec $NS_TESTER ip link set veth2_tst up +sudo ip netns exec $NS_TESTER ip link set lo up +sudo ip netns exec $NS_TARGET ip link set veth1_trg up +sudo ip netns exec $NS_TARGET ip link set veth2_trg up +sudo ip netns exec $NS_TARGET ip link set lo up + +echo "--- Configuration for asymmetric routing ---" +for ns in $NS_TESTER $NS_TARGET; do + sudo ip netns exec $ns sysctl -w net.ipv4.conf.all.rp_filter=0 + sudo ip netns exec $ns sysctl -w net.ipv4.conf.default.rp_filter=0 + sudo ip netns exec $ns sysctl -w net.ipv4.conf.all.accept_local=1 + sudo ip netns exec $ns sysctl -w net.ipv4.ip_forward=1 + + for dev in $(sudo ip netns exec $ns ls /sys/class/net/); do + sudo ip netns exec $ns sysctl -w net.ipv4.conf.$dev.rp_filter=0 2>/dev/null || true + done +done + +T_MAC2=$(sudo ip netns exec $NS_TARGET cat /sys/class/net/veth2_trg/address) +sudo ip netns exec $NS_TESTER arp -s 10.0.2.2 $T_MAC2 -i veth2_tst + +echo "--- Tests ---" +sudo ip netns exec $NS_TESTER tcpdump -i any icmp -n -l > $LOG_FILE 2>&1 & +TCP_PID=$! +sleep 2 + +echo "Send fping (asymmetry check)..." +sudo ip netns exec $NS_TESTER ./target/debug/fping -c 1 -t 1000 --oiface veth2_tst -S 10.0.1.1 10.0.2.2 || FPING_STATUS=$? + +sleep 1 +sudo kill $TCP_PID 2>/dev/null || true +sleep 1 + +echo "--- Analysis ---" + +REQ_OK=$(grep "veth2_tst Out IP 10.0.1.1 > 10.0.2.2" $LOG_FILE | wc -l) +REP_OK=$(grep "veth1_tst In IP 10.0.2.2 > 10.0.1.1" $LOG_FILE | wc -l) + +if [ "$REQ_OK" -gt 0 ] && [ "$REP_OK" -gt 0 ]; then + echo "RESULT: TEST SUCCESSFUL (True asymmetry detected)" + exit 0 +else + echo "RESULT: TEST FAILED" + [ "$REQ_OK" -eq 0 ] && echo "- The request was not sent correctly with source 10.0.1.1 via veth2_tst." + [ "$REP_OK" -eq 0 ] && echo "- The reply was not received asymmetrically via veth1_tst." + exit 1 +fi \ No newline at end of file diff --git a/src/args.rs b/src/args.rs index c0c46e4..d608872 100644 --- a/src/args.rs +++ b/src/args.rs @@ -98,6 +98,10 @@ pub struct Args { /// Minimum number of reachable hosts to be considered success #[arg(short = 'x', long = "reachable", value_name = "N")] pub reachable: Option, + + /// Bind outgoing packets to this network interface (e.g. eth0) + #[arg(long = "oiface", value_name = "IFACE")] + pub oiface: Option, } impl Args { diff --git a/src/pinger.rs b/src/pinger.rs index 85909aa..bbb38fc 100644 --- a/src/pinger.rs +++ b/src/pinger.rs @@ -9,7 +9,7 @@ use crate::output::{ print_global_stats, print_per_host_stats, print_recv, print_timeout, max_host_len, GlobalStatsSummary, RecvLineOpts, TimeoutLineOpts, }; -use crate::socket::{build_icmp_packet, open_raw_socket, recv_ping, send_ping_v4, send_ping_v6, SocketKind}; +use crate::socket::{build_icmp_packet, open_raw_socket, recv_ping, send_ping_v4, send_ping_v6, set_outgoing_iface_v4, set_outgoing_iface_v6, SocketKind}; use crate::types::{HostEntry, PendingPing}; pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { @@ -56,6 +56,24 @@ pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { let fd4 = owned_fd4.as_ref().map(|o| o.as_raw_fd()); let fd6 = owned_fd6.as_ref().map(|o| o.as_raw_fd()); + let oiface_idx4: Option = if let Some(ref iface) = args.oiface { + if let Some(fd) = fd4 { + match set_outgoing_iface_v4(fd, iface) { + Ok(idx) => Some(idx), + Err(e) => { eprintln!("fping: {}", e); std::process::exit(1); } + } + } else { None } + } else { None }; + + let oiface_idx6: Option = if let Some(ref iface) = args.oiface { + if let Some(fd) = fd6 { + match set_outgoing_iface_v6(fd, iface) { + Ok(idx) => Some(idx), + Err(e) => { eprintln!("fping: {}", e); std::process::exit(1); } + } + } else { None } + } else { None }; + let pid_id = (std::process::id() & 0xFFFF) as u16; let my_id4 = dgram_id4.unwrap_or(pid_id); let my_id6 = dgram_id6.unwrap_or(pid_id); @@ -108,8 +126,8 @@ pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { let pkt = build_icmp_packet(pkt_id, seq, args.size, is_ipv6, kind); let sent = match hosts[idx].addr { - IpAddr::V4(ref a) => fd4.map(|fd| send_ping_v4(fd, a, &pkt)).unwrap_or(false), - IpAddr::V6(ref a) => fd6.map(|fd| send_ping_v6(fd, a, &pkt)).unwrap_or(false), + IpAddr::V4(ref a) => fd4.map(|fd| send_ping_v4(fd, a, &pkt, oiface_idx4)).unwrap_or(false), + IpAddr::V6(ref a) => fd6.map(|fd| send_ping_v6(fd, a, &pkt, oiface_idx6)).unwrap_or(false), }; if sent && !seqmap.contains_key(&seq) { diff --git a/src/socket.rs b/src/socket.rs index 34ee9fa..a2d1a21 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1,10 +1,11 @@ use libc::{ - c_void, recvfrom, sendto, sockaddr, socklen_t, + c_void, recvfrom, sendmsg, sendto, sockaddr, socklen_t, AF_INET, AF_INET6, IPPROTO_ICMP, IPPROTO_ICMPV6, SOCK_DGRAM, SOCK_RAW, }; use std::io::ErrorKind; use std::net::{Ipv4Addr, Ipv6Addr}; use std::os::unix::io::RawFd; +use std::ffi::CString; use crate::constants::{ ICMP6_ECHO_REPLY, ICMP6_ECHO_REQUEST, ICMP_ECHO_REPLY, ICMP_ECHO_REQUEST, ICMP_HEADER_LEN, @@ -120,6 +121,52 @@ fn set_nonblocking(fd: RawFd) -> Result<(), std::io::Error> { } } +pub fn set_outgoing_iface_v4(fd: RawFd, iface: &str) -> Result { + let cname = CString::new(iface).map_err(|_| format!("Invalid interface name: {}", iface))?; + let idx = unsafe { libc::if_nametoindex(cname.as_ptr()) }; + if idx == 0 { + return Err(format!("fping: unknown interface '{}'", iface)); + } + + let on: libc::c_int = 1; + let r = unsafe { + libc::setsockopt( + fd, + libc::IPPROTO_IP, + libc::IP_PKTINFO, + &on as *const _ as *const libc::c_void, + std::mem::size_of::() as socklen_t, + ) + }; + + if r < 0 { + return Err(format!("setsockopt IP_PKTINFO failed: {}", std::io::Error::last_os_error())); + } + + Ok(idx) +} + +pub fn set_outgoing_iface_v6(fd: RawFd, iface: &str) -> Result { + let cname = CString::new(iface).map_err(|_| format!("Invalid interface name: {}", iface))?; + + let idx = unsafe { libc::if_nametoindex(cname.as_ptr()) }; + if idx == 0 { + return Err(format!("fping: unknown interface '{}'", iface)); + } + + let _ = unsafe { + libc::setsockopt( + fd, + libc::IPPROTO_IPV6, + libc::IPV6_MULTICAST_IF, + &idx as *const _ as *const libc::c_void, + std::mem::size_of::() as socklen_t, + ) + }; + + Ok(idx) +} + pub fn build_icmp_packet(id: u16, seq: u16, data_size: usize, is_ipv6: bool, kind: SocketKind) -> Vec { let total = ICMP_HEADER_LEN + data_size; let mut pkt = vec![0u8; total]; @@ -164,38 +211,94 @@ fn icmp_checksum(data: &[u8]) -> u16 { !(sum as u16) } -pub fn send_ping_v4(fd: RawFd, addr: &Ipv4Addr, pkt: &[u8]) -> bool { +pub fn send_ping_v4(fd: RawFd, addr: &Ipv4Addr, pkt: &[u8], iface_idx: Option) -> bool { unsafe { let mut sa: libc::sockaddr_in = std::mem::zeroed(); sa.sin_family = AF_INET as libc::sa_family_t; sa.sin_addr.s_addr = u32::from_ne_bytes(addr.octets()); - let n = sendto( - fd, - pkt.as_ptr() as *const c_void, - pkt.len(), - 0, - &sa as *const _ as *const sockaddr, - std::mem::size_of::() as socklen_t, - ); + let n = if let Some(idx) = iface_idx { + let iov = libc::iovec { + iov_base: pkt.as_ptr() as *mut c_void, + iov_len: pkt.len(), + }; + let cmsg_space = libc::CMSG_SPACE(std::mem::size_of::() as u32) as usize; + let mut cmsg_buf = vec![0u8; cmsg_space]; + + let mut msg: libc::msghdr = std::mem::zeroed(); + msg.msg_name = &sa as *const _ as *mut libc::c_void; + msg.msg_namelen = std::mem::size_of::() as socklen_t; + msg.msg_iov = &iov as *const _ as *mut libc::iovec; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf.as_mut_ptr() as *mut libc::c_void; + msg.msg_controllen = cmsg_space as _; + + let cmsg = libc::CMSG_FIRSTHDR(&msg); + (*cmsg).cmsg_level = libc::IPPROTO_IP; + (*cmsg).cmsg_type = libc::IP_PKTINFO; + (*cmsg).cmsg_len = libc::CMSG_LEN(std::mem::size_of::() as u32) as _; + + let pktinfo = libc::CMSG_DATA(cmsg) as *mut libc::in_pktinfo; + std::ptr::write_bytes(pktinfo, 0, 1); + (*pktinfo).ipi_ifindex = idx as _; + + sendmsg(fd, &msg, 0) + } else { + sendto( + fd, + pkt.as_ptr() as *const c_void, + pkt.len(), + 0, + &sa as *const _ as *const sockaddr, + std::mem::size_of::() as socklen_t, + ) + }; n == pkt.len() as isize } } -pub fn send_ping_v6(fd: RawFd, addr: &Ipv6Addr, pkt: &[u8]) -> bool { +pub fn send_ping_v6(fd: RawFd, addr: &Ipv6Addr, pkt: &[u8], iface_idx: Option) -> bool { unsafe { let mut sa: libc::sockaddr_in6 = std::mem::zeroed(); sa.sin6_family = AF_INET6 as libc::sa_family_t; sa.sin6_addr.s6_addr = addr.octets(); - let n = sendto( - fd, - pkt.as_ptr() as *const c_void, - pkt.len(), - 0, - &sa as *const _ as *const sockaddr, - std::mem::size_of::() as socklen_t, - ); + let n = if let Some(idx) = iface_idx { + let iov = libc::iovec { + iov_base: pkt.as_ptr() as *mut c_void, + iov_len: pkt.len(), + }; + let cmsg_space = libc::CMSG_SPACE(std::mem::size_of::() as u32) as usize; + let mut cmsg_buf = vec![0u8; cmsg_space]; + + let mut msg: libc::msghdr = std::mem::zeroed(); + msg.msg_name = &sa as *const _ as *mut libc::c_void; + msg.msg_namelen = std::mem::size_of::() as socklen_t; + msg.msg_iov = &iov as *const _ as *mut libc::iovec; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf.as_mut_ptr() as *mut libc::c_void; + msg.msg_controllen = cmsg_space as _; + + let cmsg = libc::CMSG_FIRSTHDR(&msg); + (*cmsg).cmsg_level = libc::IPPROTO_IPV6; + (*cmsg).cmsg_type = libc::IPV6_PKTINFO; + (*cmsg).cmsg_len = libc::CMSG_LEN(std::mem::size_of::() as u32) as _; + + let pktinfo = libc::CMSG_DATA(cmsg) as *mut libc::in6_pktinfo; + std::ptr::write_bytes(pktinfo, 0, 1); + (*pktinfo).ipi6_ifindex = idx; + + sendmsg(fd, &msg, 0) + } else { + sendto( + fd, + pkt.as_ptr() as *const c_void, + pkt.len(), + 0, + &sa as *const _ as *const sockaddr, + std::mem::size_of::() as socklen_t, + ) + }; n == pkt.len() as isize } } From ca7b1b11cb18e8cd6fa9e68d3789379de52705d0 Mon Sep 17 00:00:00 2001 From: German Service Network Date: Sat, 25 Apr 2026 12:51:13 +0200 Subject: [PATCH 2/3] Add source flag to define source ip --- CHANGELOG.md | 2 +- src/args.rs | 4 ++++ src/pinger.rs | 27 ++++++++++++++++++++++++++- src/socket.rs | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c8e7c..86d7c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ fping-rust 0.2.0 (new) ====================== -- New option --oiface for outgoing interface (#18, @gsnw-sebast) +- New option --oiface for outgoing interface and source ip flag (#18, @gsnw-sebast) fping-rust 0.1.1 (2026-03-28) ====================== diff --git a/src/args.rs b/src/args.rs index d608872..3b4af45 100644 --- a/src/args.rs +++ b/src/args.rs @@ -99,6 +99,10 @@ pub struct Args { #[arg(short = 'x', long = "reachable", value_name = "N")] pub reachable: Option, + /// Source address for outgoing pings + #[arg(short = 'S', long = "source")] + pub source: Option, + /// Bind outgoing packets to this network interface (e.g. eth0) #[arg(long = "oiface", value_name = "IFACE")] pub oiface: Option, diff --git a/src/pinger.rs b/src/pinger.rs index bbb38fc..e6ebf61 100644 --- a/src/pinger.rs +++ b/src/pinger.rs @@ -9,7 +9,7 @@ use crate::output::{ print_global_stats, print_per_host_stats, print_recv, print_timeout, max_host_len, GlobalStatsSummary, RecvLineOpts, TimeoutLineOpts, }; -use crate::socket::{build_icmp_packet, open_raw_socket, recv_ping, send_ping_v4, send_ping_v6, set_outgoing_iface_v4, set_outgoing_iface_v6, SocketKind}; +use crate::socket::{bind_source_v4, bind_source_v6, build_icmp_packet, open_raw_socket, recv_ping, send_ping_v4, send_ping_v6, set_outgoing_iface_v4, set_outgoing_iface_v6, SocketKind}; use crate::types::{HostEntry, PendingPing}; pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { @@ -56,6 +56,31 @@ pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { let fd4 = owned_fd4.as_ref().map(|o| o.as_raw_fd()); let fd6 = owned_fd6.as_ref().map(|o| o.as_raw_fd()); + if let Some(ref src) = args.source { + match src.parse::() { + Ok(IpAddr::V4(_)) => { + if let Some(fd) = fd4 { + if let Err(e) = bind_source_v4(fd, src) { + eprintln!("fping: {}", e); + std::process::exit(1); + } + } + } + Ok(IpAddr::V6(_)) => { + if let Some(fd) = fd6 { + if let Err(e) = bind_source_v6(fd, src) { + eprintln!("fping: {}", e); + std::process::exit(1); + } + } + } + Err(_) => { + eprintln!("fping: -S/--source '{}' is not a valid IP address", src); + std::process::exit(1); + } + } + } + let oiface_idx4: Option = if let Some(ref iface) = args.oiface { if let Some(fd) = fd4 { match set_outgoing_iface_v4(fd, iface) { diff --git a/src/socket.rs b/src/socket.rs index a2d1a21..e8cff2f 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -18,6 +18,43 @@ pub enum SocketKind { Dgram, } +pub fn bind_source_v4(fd: RawFd, src: &str) -> Result<(), String> { + let addr: Ipv4Addr = src.parse().map_err(|_| format!("Invalid IPv4 source address: '{}'", src))?; + + unsafe { + let mut sa: libc::sockaddr_in = std::mem::zeroed(); + sa.sin_family = AF_INET as libc::sa_family_t; + sa.sin_addr.s_addr = u32::from_ne_bytes(addr.octets()); + // sin_port = 0 -> kernel picks an ephemeral port (irrelevant for RAW/DGRAM-ICMP) + + let r = libc::bind(fd, &sa as *const _ as *const libc::sockaddr, std::mem::size_of::() as socklen_t); + + if r < 0 { + return Err(format!("bind source address {} failed: {}", src, std::io::Error::last_os_error())); + } + } + + Ok(()) +} + +pub fn bind_source_v6(fd: RawFd, src: &str) -> Result<(), String> { + let addr: Ipv6Addr = src.parse().map_err(|_| format!("Invalid IPv6 source address: '{}'", src))?; + + unsafe { + let mut sa: libc::sockaddr_in6 = std::mem::zeroed(); + sa.sin6_family = AF_INET6 as libc::sa_family_t; + sa.sin6_addr.s6_addr = addr.octets(); + + let r = libc::bind(fd, &sa as *const _ as *const libc::sockaddr, std::mem::size_of::() as socklen_t); + + if r < 0 { + return Err(format!("bind source address {} failed: {}", src, std::io::Error::last_os_error())); + } + } + + Ok(()) +} + pub fn open_raw_socket(is_ipv6: bool) -> Result<(RawFd, SocketKind, Option), String> { let (domain, proto) = if is_ipv6 { (AF_INET6, IPPROTO_ICMPV6) From b65d7314a2b0485fb3042a7de60643996e6b2809 Mon Sep 17 00:00:00 2001 From: German Service Network Date: Sat, 25 Apr 2026 13:23:25 +0200 Subject: [PATCH 3/3] fix: Honour -S source address when --oiface is set --- src/pinger.rs | 7 +++++-- src/socket.rs | 12 ++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/pinger.rs b/src/pinger.rs index e6ebf61..0215f5f 100644 --- a/src/pinger.rs +++ b/src/pinger.rs @@ -99,6 +99,9 @@ pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { } else { None } } else { None }; + let src_v4: Option = args.source.as_ref().and_then(|s| s.parse::().ok()).and_then(|a| if let IpAddr::V4(v4) = a { Some(v4) } else { None }); + let src_v6: Option = args.source.as_ref().and_then(|s| s.parse::().ok()).and_then(|a| if let IpAddr::V6(v6) = a { Some(v6) } else { None }); + let pid_id = (std::process::id() & 0xFFFF) as u16; let my_id4 = dgram_id4.unwrap_or(pid_id); let my_id6 = dgram_id6.unwrap_or(pid_id); @@ -151,8 +154,8 @@ pub fn run(args: Args, hosts_in: Vec<(String, IpAddr)>) { let pkt = build_icmp_packet(pkt_id, seq, args.size, is_ipv6, kind); let sent = match hosts[idx].addr { - IpAddr::V4(ref a) => fd4.map(|fd| send_ping_v4(fd, a, &pkt, oiface_idx4)).unwrap_or(false), - IpAddr::V6(ref a) => fd6.map(|fd| send_ping_v6(fd, a, &pkt, oiface_idx6)).unwrap_or(false), + IpAddr::V4(ref a) => fd4.map(|fd| send_ping_v4(fd, a, &pkt, oiface_idx4, src_v4)).unwrap_or(false), + IpAddr::V6(ref a) => fd6.map(|fd| send_ping_v6(fd, a, &pkt, oiface_idx6, src_v6)).unwrap_or(false), }; if sent && !seqmap.contains_key(&seq) { diff --git a/src/socket.rs b/src/socket.rs index e8cff2f..43647b0 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -248,7 +248,7 @@ fn icmp_checksum(data: &[u8]) -> u16 { !(sum as u16) } -pub fn send_ping_v4(fd: RawFd, addr: &Ipv4Addr, pkt: &[u8], iface_idx: Option) -> bool { +pub fn send_ping_v4(fd: RawFd, addr: &Ipv4Addr, pkt: &[u8], iface_idx: Option, src_addr: Option) -> bool { unsafe { let mut sa: libc::sockaddr_in = std::mem::zeroed(); sa.sin_family = AF_INET as libc::sa_family_t; @@ -279,6 +279,10 @@ pub fn send_ping_v4(fd: RawFd, addr: &Ipv4Addr, pkt: &[u8], iface_idx: Option) -> bool { +pub fn send_ping_v6(fd: RawFd, addr: &Ipv6Addr, pkt: &[u8], iface_idx: Option, src_addr: Option) -> bool { unsafe { let mut sa: libc::sockaddr_in6 = std::mem::zeroed(); sa.sin6_family = AF_INET6 as libc::sa_family_t; @@ -325,6 +329,10 @@ pub fn send_ping_v6(fd: RawFd, addr: &Ipv6Addr, pkt: &[u8], iface_idx: Option