WIP: Working on config parser and a basic version which can receive multicast traffic

This commit is contained in:
Ishan Jain 2023-06-05 01:51:39 +05:30
parent e2d443e6be
commit 46b21c2013
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
9 changed files with 432 additions and 7 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target
*.pcapng
*.pcap

270
Cargo.lock generated
View File

@ -3,10 +3,83 @@
version = 3
[[package]]
name = "mdns-repeater"
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "multicaster"
version = "0.1.0"
dependencies = [
"nix",
"once_cell",
"serde",
"socket2",
"toml",
]
[[package]]
name = "nix"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
"static_assertions",
]
[[package]]
@ -14,3 +87,198 @@ name = "once_cell"
version = "1.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
[[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.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
dependencies = [
"serde",
]
[[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.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]

View File

@ -4,4 +4,8 @@ version = "0.1.0"
edition = "2021"
[dependencies]
nix = "0.26.2"
once_cell = "1.17.2"
serde = { version = "1.0.163", features = ["derive"] }
socket2 = "0.5.3"
toml = "0.7.4"

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# Multicaster
This is a software for routing multicast traffic across L2 networks.
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.
This causes problems if there are other softwares that are also listening without using `SO_ADDR_REUSE`.
For now, Disable those softwares when running this. A list of such softwares,
a. avahi-daemon

4
config.toml Normal file
View File

@ -0,0 +1,4 @@
[config.mdns]
port = 5353
multicast_groups = ["224.0.0.251"]
destinations = ["wlp6s0"]

34
src/config.rs Normal file
View File

@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, io::Read, net::IpAddr};
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub config: HashMap<String, Record>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Record {
pub port: u16,
pub multicast_groups: Vec<IpAddr>,
pub destinations: Vec<String>,
}
impl Config {
const FILENAME: &'static str = "config.toml";
pub fn parse(mut filename: &str) -> Result<Config, String> {
if filename.is_empty() {
filename = Config::FILENAME;
}
let mut f = File::open(filename).map_err(|e| format!("error in opening file: {}", e))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|e| format!("error in reading file contents: {}", e))?;
let config: Config = toml::from_str(&contents)
.map_err(|e| format!("error in parsing config: {}", e.message()))?;
Ok(config)
}
}

View File

@ -1,6 +1,66 @@
use once_cell::sync::Lazy;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
// TODO(ishan): Eventually we'll have a listener and transmitter module for every thing we want to
// support. So, 1 for MDNS, another for WSDD?
// Or a common listener/transmitter and then different modules to parse and transmit each type of
// traffic
pub mod config;
pub use config::*;
pub mod listener;
pub static IPv4: Lazy<IpAddr> = Lazy::new(|| Ipv4Addr::new(224, 0, 0, 123).into());
pub static IPv6: Lazy<IpAddr> =
Lazy::new(|| Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x0123).into());
use crate::listener::{join_multicast, new_socket};
use once_cell::sync::Lazy;
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
thread,
};
pub static IPV4: Lazy<IpAddr> = Lazy::new(|| Ipv4Addr::new(0, 0, 0, 0).into());
pub static IPV6: Lazy<IpAddr> = Lazy::new(|| {
Ipv6Addr::new(0x2a0a, 0x6040, 0x4004, 0x10, 0xf1c6, 0xf2b0, 0x9f45, 0xb425).into()
});
pub fn start(config: crate::Config) {
// TODO(ishan): Start listeners and transmitters on v4 and v6 here
let mut handles = vec![];
for (_, v) in config.config {
let handle = thread::spawn(move || {
let mut ipv4_socket = new_socket(&SocketAddr::new(*IPV4, v.port))
.expect("error in bind op on ipv4 address");
let mut ipv6_socket = new_socket(&SocketAddr::new(*IPV6, v.port))
.expect("error in bind op on ipv6 address");
for group in v.multicast_groups {
join_multicast(&mut ipv4_socket, &group)
.expect("error in joining ipv4 multicast group");
join_multicast(&mut ipv6_socket, &group)
.expect("error in joining ipv6 multicast group");
println!(
"Listening for multicast packets on ipv4/ipv6 in group {}:{}",
group, v.port
);
}
let mut buf = [0u8; 64];
match ipv4_socket.recv_from(&mut buf) {
Ok((len, remote_addr)) => {
let buf = &buf[..len];
println!("received {}bytes response from {}", len, remote_addr);
let v = String::from_utf8_lossy(buf);
println!("output = {:?}", v);
}
Err(e) => {
println!("got an error: {}", e);
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap()
}
}

34
src/listener.rs Normal file
View File

@ -0,0 +1,34 @@
use std::{
io::{Error, Result as IoResult},
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
os::fd::AsRawFd,
};
use nix::sys::socket::sockopt::ReuseAddr;
pub fn new_socket(addr: &SocketAddr) -> Result<UdpSocket, Error> {
let socket = UdpSocket::bind(addr)?;
#[cfg(unix)]
nix::sys::socket::setsockopt(socket.as_raw_fd(), ReuseAddr, &true)?;
// socket.set_read_timeout(Some(Duration::from_millis(100)))?;
Ok(socket)
}
pub fn join_multicast(socket: &mut UdpSocket, group: &IpAddr) -> IoResult<()> {
// TODO(ishan): Eventually, this should only listen on the
// interfaces specified in config.toml
match group {
IpAddr::V4(ref mdns_v4) => {
socket.multicast_loop_v4()?;
socket.join_multicast_v4(mdns_v4, &Ipv4Addr::new(0, 0, 0, 0))
}
IpAddr::V6(ref mdns_v6) => {
socket.multicast_loop_v6()?;
socket.join_multicast_v6(mdns_v6, 0)
}
}
}

View File

@ -1,3 +1,8 @@
use multicaster::Config;
fn main() {
println!("Hello, world!");
let config = Config::parse("config.toml").expect("error in parsing config");
println!("{:?}", config);
multicaster::start(config);
}