wip: unified ipv6 socket for ipv4 and ipv6 traffic

This commit is contained in:
Ishan Jain 2023-11-17 23:49:19 +05:30
parent 2c50a98e63
commit c778de9bf9
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
6 changed files with 554 additions and 47 deletions

267
Cargo.lock generated
View File

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "1.0.2"
@ -11,6 +22,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -47,6 +64,73 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
dependencies = [
"cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset 0.9.0",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "dns-parser"
version = "0.8.0"
@ -57,6 +141,19 @@ dependencies = [
"quick-error",
]
[[package]]
name = "endorphin"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2dc87c62efee2c20e9287b6421800c02b56551b3cc5ee9af1ac9336e8173ebd"
dependencies = [
"crossbeam",
"hashbrown 0.11.2",
"once_cell",
"parking_lot",
"slotmap",
]
[[package]]
name = "env_logger"
version = "0.10.0"
@ -125,6 +222,26 @@ dependencies = [
"libc",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
@ -150,7 +267,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.14.0",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
@ -176,6 +302,16 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
@ -189,16 +325,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "multicast-socket"
version = "0.2.2"
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53eacba0998466687d051439bf144286203d7286623275a8007505d22fd21cfb"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"get_if_addrs",
"libc",
"nix",
"socket2",
"winapi 0.3.9",
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
@ -206,27 +347,68 @@ name = "multicaster"
version = "0.1.0"
dependencies = [
"dns-parser",
"endorphin",
"env_logger",
"get_if_addrs",
"libc",
"log",
"multicast-socket",
"nix",
"serde",
"socket2",
"toml",
]
[[package]]
name = "nix"
version = "0.19.1"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags 1.3.2",
"cc",
"cfg-if",
"libc",
"memoffset 0.7.1",
"pin-utils",
"static_assertions",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.9",
]
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.66"
@ -251,6 +433,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.9.1"
@ -293,6 +484,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.177"
@ -323,16 +520,36 @@ dependencies = [
]
[[package]]
name = "socket2"
version = "0.3.19"
name = "slotmap"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
dependencies = [
"cfg-if",
"libc",
"winapi 0.3.9",
"version_check",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "socket2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "2.0.27"
@ -393,6 +610,18 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.2.8"

View File

@ -5,10 +5,12 @@ edition = "2021"
[dependencies]
dns-parser = "0.8.0"
endorphin = "0.1.9"
env_logger = "0.10.0"
get_if_addrs = "0.5.3"
libc = "0.2.147"
log = "0.4.18"
multicast-socket = "0.2.2"
nix = { version = "0.26.2", features = ["net"] }
serde = { version = "1.0.163", features = ["derive"] }
socket2 = { version = "0.5.3", features = ["all"] }
toml = "0.7.4"

View File

@ -6,7 +6,7 @@ It'll allow you to be very specific about the exact traffic that is sent over.
# Working Notes
1. It needs to listen on the specified port to receive multicast traffic.
* It needs to listen on the specified port to receive multicast traffic.
This causes problems if there are other softwares that are also listening without using `SO_REUSE_ADDR`.
For now, Disable those softwares when running this. A list of such softwares,
@ -14,14 +14,14 @@ For now, Disable those softwares when running this. A list of such softwares,
a. avahi-daemon
* Multicast DNS RFC https://datatracker.ietf.org/doc/html/rfc6762
### How should this be designed??
For now, I am restricting it to only consider 1 config.
It won't listen on multiple ports for multicast traffic. This will be changed once I have the basic structure ready.
For now, Only work on IPv4. IPv6 will be added once IPv4 is ready
@ -35,8 +35,3 @@ For now, Only work on IPv4. IPv6 will be added once IPv4 is ready
4. A DNS answer should not be forwarded from destination to source in any circumstances. This is not enforced right now.

View File

@ -9,6 +9,7 @@ pub mod config;
pub use config::*;
pub mod mdns;
pub use mdns::*;
mod socket;
fn main() {
env_logger::init();

View File

@ -1,7 +1,11 @@
use crate::socket::{
Interface as MulticastInterface, MulticastGroup, MulticastOptions, MulticastSocket,
};
use crate::Config;
use dns_parser::Packet;
use log::{info, trace, warn};
use multicast_socket::{Interface as MulticastInterface, MulticastOptions, MulticastSocket};
use nix::errno::Errno;
use std::net::{Ipv4Addr, SocketAddrV6};
use std::{ffi::CString, net::SocketAddrV4};
pub struct Mdns {
@ -12,15 +16,12 @@ pub struct Mdns {
impl Mdns {
pub fn new(config: Config) -> Self {
// mdns
let mdns_address = SocketAddrV4::new([224, 0, 0, 251].into(), 5353);
let multicast_socket = MulticastSocket::with_options(
mdns_address,
// TODO(ishan): Listen on ALL Interfaces, including ipv6
multicast_socket::all_ipv4_interfaces().expect("could not fetch all interfaces"),
MulticastOptions {
loopback: false,
buffer_size: 4096,
..Default::default()
let multicast_socket = MulticastSocket::new(
MulticastOptions::default(),
MulticastSocket::all_interfaces().unwrap(),
MulticastGroup {
ipv4: Some(SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 251), 5353)),
port: 5353,
},
)
.expect("error in creating multicast socket");
@ -37,7 +38,14 @@ impl Mdns {
loop {
match self.socket.receive() {
Ok(msg) => self.process_packet(msg),
Err(e) if e.to_string().contains("EAGAIN") => continue,
Err(e)
if e.get_ref().map_or(false, |e| {
e.downcast_ref::<nix::Error>()
.is_some_and(|c| *c == Errno::EAGAIN)
}) =>
{
continue;
}
Err(e) => {
warn!("error in reading from socket {:?} ", e);
}
@ -45,9 +53,16 @@ impl Mdns {
}
}
pub fn process_packet(&self, msg: multicast_socket::Message) {
pub fn process_packet(&self, msg: crate::socket::Message) {
// TODO: Generalize this to parse any type of supported packet
let packet = Packet::parse(&msg.data).expect("failed to parse packet as a dns packet");
let packet = Packet::parse(&msg.data).unwrap_or_else(|e| {
panic!(
"failed to parse packet as a dns packet: {:?} error = {:?}, loose_string = {}",
msg,
e,
String::from_utf8_lossy(&msg.data)
)
});
let src_ifname = if let MulticastInterface::Index(idx) = msg.interface {
ifidx_to_ifname(idx as u32)
@ -56,12 +71,12 @@ impl Mdns {
};
trace!(
"EVENT src-if = {} if-index {:?} address = {}, packet: {:?} answers = {:?}",
"EVENT src-if = {} if-index {:?} address = {:?}, packet: {:?} answers = {:?}",
src_ifname,
msg.interface,
msg.origin_address,
packet.questions.iter().map(|q| q.qname).collect::<Vec<_>>(),
packet.answers.iter().map(|q| q.name).collect::<Vec<_>>()
packet.questions.iter().collect::<Vec<_>>(),
packet.answers.iter().collect::<Vec<_>>()
);
let interfaces = get_if_addrs::get_if_addrs().unwrap();
@ -97,8 +112,8 @@ impl Mdns {
let dst_ifid = ifname_to_ifidx(dst_if.name.to_string());
info!(
"forwarding packet questions {:?} answers = {:?} from {} to {}",
packet.questions, packet.answers, src_ifname, dst_if.name
"forwarding packet packet {:?} from {} to {}",
packet, src_ifname, dst_if.name
);
// TODO(ishan): Take a note of transaction id
// and avoid feedback loops

265
src/socket.rs Normal file
View File

@ -0,0 +1,265 @@
#![allow(unused)]
use log::trace;
// This code has been adapted from multicast_socket crate
use nix::sys::socket::{self as sock, AddressFamily, SockaddrIn, SockaddrLike, SockaddrStorage};
use serde::de::value;
use socket2::{Domain, Protocol, Socket, Type};
use std::{
collections::{HashMap, HashSet},
hash::Hash,
io::{self, IoSlice, IoSliceMut, Result as IoResult},
mem,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
os::unix::io::AsRawFd,
time::Duration,
};
pub struct MulticastOptions {
pub read_timeout: Duration,
pub buffer_size: usize,
}
impl Default for MulticastOptions {
fn default() -> Self {
MulticastOptions {
read_timeout: Duration::from_secs(1),
buffer_size: 512,
}
}
}
#[derive(Debug)]
pub struct MulticastSocket {
socket: socket2::Socket,
interfaces: HashMap<String, Vec<IpAddr>>,
multicast_group: MulticastGroup,
buffer_size: usize,
}
#[derive(Debug, Clone)]
pub struct MulticastGroup {
pub ipv4: SocketAddrV4,
pub port: u16,
}
impl MulticastSocket {
pub fn new(
options: MulticastOptions,
interfaces: HashMap<String, Vec<IpAddr>>,
multicast_group: MulticastGroup,
) -> Result<Self, std::io::Error> {
// We want to accept traffic on IPv4 and IPv6.
// and I don't want to deal with 2 sockets. 1 for ipv4 and 1 for ipv6
// so we do this
// Create a single Ipv6 socket and disable IPV6_ONLY option
// This is already disabled on new OSes but just to be safe we do it any way
// With this, We can accept IPv6 traffic _and_ we can accept IPv4 traffic
// except the address for IPv4 will be presented within IPv6
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_read_timeout(Some(options.read_timeout))?;
socket.set_multicast_loop_v4(false)?;
socket.set_reuse_address(true)?;
socket.set_reuse_port(true)?;
// Ipv4PacketInfo translates to `IP_PKTINFO`. Checkout the [ip
// manpage](https://man7.org/linux/man-pages/man7/ip.7.html) for more details. In summary
// setting this option allows for determining on which interface a packet was received.
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv4PacketInfo, &true)
.map_err(nix_to_io_error)?;
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv6RecvPacketInfo, &true)
.map_err(nix_to_io_error)?;
// Receive IPv4 traffic on IPv6 socket
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv6V6Only, &false)
.map_err(nix_to_io_error);
for (if_name, addresses) in interfaces.iter() {
trace!(
"joining groups if_name = {} addresses = {:?}",
if_name,
addresses
);
if addresses.iter().any(|addr| addr.is_ipv6()) {
trace!(
"joined ipv6 multicast group {} if_name {}",
multicast_group.ipv6.ip(),
if_name
);
socket.join_multicast_v6(
multicast_group.ipv6.ip(),
ifname_to_ifidx(if_name.to_string()),
);
}
for address in addresses {
if let IpAddr::V4(v4_addr) = address {
if v4_addr.is_loopback() {
continue;
}
if let Some(ipv4_mdns_group) = multicast_group.ipv4 {
trace!(
"joined ipv4 multicast group {} {}",
ipv4_mdns_group.ip(),
v4_addr
);
socket.join_multicast_v4(ipv4_mdns_group.ip(), v4_addr)?;
}
}
}
}
socket.bind(
&SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), multicast_group.port).into(),
)?;
Ok(MulticastSocket {
socket,
interfaces,
buffer_size: options.buffer_size,
multicast_group,
})
}
pub fn all_interfaces() -> IoResult<HashMap<String, Vec<IpAddr>>> {
let interfaces = get_if_addrs::get_if_addrs()?.into_iter();
// We have to filter the same interface if it has multiple ips
// https://stackoverflow.com/questions/49819010/ip-add-membership-fails-when-set-both-on-interface-and-its-subinterface-is-that
let mut map = HashMap::new();
for interface in interfaces {
map.entry(interface.name.clone())
.or_insert_with(Vec::new)
.push(interface.ip());
}
// TODO: remove loopback?
map.remove("lo");
Ok(map)
}
}
#[derive(Debug, Clone)]
pub struct Message {
pub data: Vec<u8>,
pub origin_address: Option<SocketAddr>,
pub interface: Interface,
}
#[derive(Debug, Clone)]
pub enum Interface {
Default,
Index(i32),
IpAddr(Ipv6Addr),
}
#[inline]
fn ifname_to_ifidx(name: String) -> u32 {
let out = name.as_ptr() as *const _;
unsafe { libc::if_nametoindex(out) }
}
fn nix_to_io_error(e: nix::Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, e)
}
impl MulticastSocket {
pub fn receive(&self) -> IoResult<Message> {
let mut data_buffer = vec![0; self.buffer_size];
let mut control_buffer = nix::cmsg_space!(libc::in6_pktinfo, libc::in_pktinfo);
let (origin_address, interface, bytes_read) = {
let message = sock::recvmsg(
self.socket.as_raw_fd(),
&mut [IoSliceMut::new(&mut data_buffer)],
Some(&mut control_buffer),
sock::MsgFlags::empty(),
)
.map_err(nix_to_io_error)?;
let origin_address = match message.address {
//v4 @ Some(SockaddrIn) => v4,
Some(sock::SockAddr::Inet(inet)) => Some(inet.to_std()),
_ => None,
};
// let origin_address = match origin_address {
// Some(SocketAddr::V6(v6)) => v6,
// _ => SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0),
// };
println!("{:?}", origin_address);
let mut interface = Interface::Default;
for cmsg in message.cmsgs() {
if let sock::ControlMessageOwned::Ipv6PacketInfo(pktinfo) = cmsg {
interface = Interface::Index(pktinfo.ipi6_ifindex as _);
trace!("control packet ipv6: {:?}", pktinfo);
}
if let sock::ControlMessageOwned::Ipv4PacketInfo(pktinfo) = cmsg {
interface = Interface::Index(pktinfo.ipi_ifindex as _);
trace!("control packet ipv4: {:?}", pktinfo);
}
}
(origin_address, interface, message.bytes)
};
Ok(Message {
data: data_buffer[0..bytes_read].to_vec(),
origin_address,
interface,
})
}
pub fn send(&self, buf: &[u8], interface: &Interface) -> io::Result<usize> {
Ok(0)
// match interface {
// Interface::Default => todo!(),
// Interface::Index(index) => {
// // TODO: Send over ipv4 and ipv6
// pkt_info.ipi_ifindex = *index as _;
// }
// Interface::Ip(IpAddr::V4(v4)) => {
// let mut pkt_info: libc::in_pktinfo = unsafe { mem::zeroed() };
// pkt_info.ipi_spec_dst = libc::in_addr {
// s_addr: (*v4).into(),
// };
// }
// Interface::Ip(IpAddr::V6(v6)) => {
// let mut pkt_info: libc::in6_pktinfo = unsafe { mem::zeroed() };
// }
// }
// match interface {
// Interface::Default => {}
// Interface::Ipv6(address) => {}
// Interface::Ipv4(address) => {
// pkt_info.ipi_spec_dst = libc::in_addr {
// s_addr: (*address).into(),
// }
// }
// Interface::Index(index) => pkt_info.ipi_ifindex = *index as _,
// };
// let destination = SockaddrIn::from(self.multicast_group);
// sock::sendmsg(
// self.socket.as_raw_fd(),
// &[IoSlice::new(buf)],
// &[sock::ControlMessage::Ipv4PacketInfo(&pkt_info)],
// sock::MsgFlags::empty(),
// Some(&destination),
// )
// .map_err(nix_to_io_error)
}
}
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}