Re-import repository.
This commit is contained in:
172
netlink/src/constants.rs
Normal file
172
netlink/src/constants.rs
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is direcly from https://docs.rs/libc/0.2.98/src/libc/unix/linux_like/linux/mod.rs.html#2449
|
||||
// because when we build with musl libc some of these values are missing.
|
||||
|
||||
// linux/rtnetlink.h
|
||||
pub const TCA_UNSPEC: libc::c_ushort = 0;
|
||||
pub const TCA_KIND: libc::c_ushort = 1;
|
||||
pub const TCA_OPTIONS: libc::c_ushort = 2;
|
||||
pub const TCA_STATS: libc::c_ushort = 3;
|
||||
pub const TCA_XSTATS: libc::c_ushort = 4;
|
||||
pub const TCA_RATE: libc::c_ushort = 5;
|
||||
pub const TCA_FCNT: libc::c_ushort = 6;
|
||||
pub const TCA_STATS2: libc::c_ushort = 7;
|
||||
pub const TCA_STAB: libc::c_ushort = 8;
|
||||
|
||||
pub const RTM_NEWLINK: u16 = 16;
|
||||
pub const RTM_DELLINK: u16 = 17;
|
||||
pub const RTM_GETLINK: u16 = 18;
|
||||
pub const RTM_SETLINK: u16 = 19;
|
||||
pub const RTM_NEWADDR: u16 = 20;
|
||||
pub const RTM_DELADDR: u16 = 21;
|
||||
pub const RTM_GETADDR: u16 = 22;
|
||||
pub const RTM_NEWROUTE: u16 = 24;
|
||||
pub const RTM_DELROUTE: u16 = 25;
|
||||
pub const RTM_GETROUTE: u16 = 26;
|
||||
pub const RTM_NEWNEIGH: u16 = 28;
|
||||
pub const RTM_DELNEIGH: u16 = 29;
|
||||
pub const RTM_GETNEIGH: u16 = 30;
|
||||
pub const RTM_NEWRULE: u16 = 32;
|
||||
pub const RTM_DELRULE: u16 = 33;
|
||||
pub const RTM_GETRULE: u16 = 34;
|
||||
pub const RTM_NEWQDISC: u16 = 36;
|
||||
pub const RTM_DELQDISC: u16 = 37;
|
||||
pub const RTM_GETQDISC: u16 = 38;
|
||||
pub const RTM_NEWTCLASS: u16 = 40;
|
||||
pub const RTM_DELTCLASS: u16 = 41;
|
||||
pub const RTM_GETTCLASS: u16 = 42;
|
||||
pub const RTM_NEWTFILTER: u16 = 44;
|
||||
pub const RTM_DELTFILTER: u16 = 45;
|
||||
pub const RTM_GETTFILTER: u16 = 46;
|
||||
pub const RTM_NEWACTION: u16 = 48;
|
||||
pub const RTM_DELACTION: u16 = 49;
|
||||
pub const RTM_GETACTION: u16 = 50;
|
||||
pub const RTM_NEWPREFIX: u16 = 52;
|
||||
pub const RTM_GETMULTICAST: u16 = 58;
|
||||
pub const RTM_GETANYCAST: u16 = 62;
|
||||
pub const RTM_NEWNEIGHTBL: u16 = 64;
|
||||
pub const RTM_GETNEIGHTBL: u16 = 66;
|
||||
pub const RTM_SETNEIGHTBL: u16 = 67;
|
||||
pub const RTM_NEWNDUSEROPT: u16 = 68;
|
||||
pub const RTM_NEWADDRLABEL: u16 = 72;
|
||||
pub const RTM_DELADDRLABEL: u16 = 73;
|
||||
pub const RTM_GETADDRLABEL: u16 = 74;
|
||||
pub const RTM_GETDCB: u16 = 78;
|
||||
pub const RTM_SETDCB: u16 = 79;
|
||||
pub const RTM_NEWNETCONF: u16 = 80;
|
||||
pub const RTM_GETNETCONF: u16 = 82;
|
||||
pub const RTM_NEWMDB: u16 = 84;
|
||||
pub const RTM_DELMDB: u16 = 85;
|
||||
pub const RTM_GETMDB: u16 = 86;
|
||||
pub const RTM_NEWNSID: u16 = 88;
|
||||
pub const RTM_DELNSID: u16 = 89;
|
||||
pub const RTM_GETNSID: u16 = 90;
|
||||
|
||||
pub const RTM_F_NOTIFY: libc::c_uint = 0x100;
|
||||
pub const RTM_F_CLONED: libc::c_uint = 0x200;
|
||||
pub const RTM_F_EQUALIZE: libc::c_uint = 0x400;
|
||||
pub const RTM_F_PREFIX: libc::c_uint = 0x800;
|
||||
|
||||
pub const RTA_UNSPEC: libc::c_ushort = 0;
|
||||
pub const RTA_DST: libc::c_ushort = 1;
|
||||
pub const RTA_SRC: libc::c_ushort = 2;
|
||||
pub const RTA_IIF: libc::c_ushort = 3;
|
||||
pub const RTA_OIF: libc::c_ushort = 4;
|
||||
pub const RTA_GATEWAY: libc::c_ushort = 5;
|
||||
pub const RTA_PRIORITY: libc::c_ushort = 6;
|
||||
pub const RTA_PREFSRC: libc::c_ushort = 7;
|
||||
pub const RTA_METRICS: libc::c_ushort = 8;
|
||||
pub const RTA_MULTIPATH: libc::c_ushort = 9;
|
||||
pub const RTA_PROTOINFO: libc::c_ushort = 10; // No longer used
|
||||
pub const RTA_FLOW: libc::c_ushort = 11;
|
||||
pub const RTA_CACHEINFO: libc::c_ushort = 12;
|
||||
pub const RTA_SESSION: libc::c_ushort = 13; // No longer used
|
||||
pub const RTA_MP_ALGO: libc::c_ushort = 14; // No longer used
|
||||
pub const RTA_TABLE: libc::c_ushort = 15;
|
||||
pub const RTA_MARK: libc::c_ushort = 16;
|
||||
pub const RTA_MFC_STATS: libc::c_ushort = 17;
|
||||
|
||||
pub const RTN_UNSPEC: libc::c_uchar = 0;
|
||||
pub const RTN_UNICAST: libc::c_uchar = 1;
|
||||
pub const RTN_LOCAL: libc::c_uchar = 2;
|
||||
pub const RTN_BROADCAST: libc::c_uchar = 3;
|
||||
pub const RTN_ANYCAST: libc::c_uchar = 4;
|
||||
pub const RTN_MULTICAST: libc::c_uchar = 5;
|
||||
pub const RTN_BLACKHOLE: libc::c_uchar = 6;
|
||||
pub const RTN_UNREACHABLE: libc::c_uchar = 7;
|
||||
pub const RTN_PROHIBIT: libc::c_uchar = 8;
|
||||
pub const RTN_THROW: libc::c_uchar = 9;
|
||||
pub const RTN_NAT: libc::c_uchar = 10;
|
||||
pub const RTN_XRESOLVE: libc::c_uchar = 11;
|
||||
|
||||
pub const RTPROT_UNSPEC: libc::c_uchar = 0;
|
||||
pub const RTPROT_REDIRECT: libc::c_uchar = 1;
|
||||
pub const RTPROT_KERNEL: libc::c_uchar = 2;
|
||||
pub const RTPROT_BOOT: libc::c_uchar = 3;
|
||||
pub const RTPROT_STATIC: libc::c_uchar = 4;
|
||||
|
||||
pub const RT_SCOPE_UNIVERSE: libc::c_uchar = 0;
|
||||
pub const RT_SCOPE_SITE: libc::c_uchar = 200;
|
||||
pub const RT_SCOPE_LINK: libc::c_uchar = 253;
|
||||
pub const RT_SCOPE_HOST: libc::c_uchar = 254;
|
||||
pub const RT_SCOPE_NOWHERE: libc::c_uchar = 255;
|
||||
|
||||
pub const RT_TABLE_UNSPEC: libc::c_uchar = 0;
|
||||
pub const RT_TABLE_COMPAT: libc::c_uchar = 252;
|
||||
pub const RT_TABLE_DEFAULT: libc::c_uchar = 253;
|
||||
pub const RT_TABLE_MAIN: libc::c_uchar = 254;
|
||||
pub const RT_TABLE_LOCAL: libc::c_uchar = 255;
|
||||
|
||||
pub const RTMSG_OVERRUN: u32 = libc::NLMSG_OVERRUN as u32;
|
||||
pub const RTMSG_NEWDEVICE: u32 = 0x11;
|
||||
pub const RTMSG_DELDEVICE: u32 = 0x12;
|
||||
pub const RTMSG_NEWROUTE: u32 = 0x21;
|
||||
pub const RTMSG_DELROUTE: u32 = 0x22;
|
||||
pub const RTMSG_NEWRULE: u32 = 0x31;
|
||||
pub const RTMSG_DELRULE: u32 = 0x32;
|
||||
pub const RTMSG_CONTROL: u32 = 0x40;
|
||||
pub const RTMSG_AR_FAILED: u32 = 0x51;
|
||||
|
||||
pub const MAX_ADDR_LEN: usize = 7;
|
||||
pub const ARPD_UPDATE: libc::c_ushort = 0x01;
|
||||
pub const ARPD_LOOKUP: libc::c_ushort = 0x02;
|
||||
pub const ARPD_FLUSH: libc::c_ushort = 0x03;
|
||||
pub const ATF_MAGIC: libc::c_int = 0x80;
|
||||
|
||||
// From https://docs.rs/libc/0.2.98/src/libc/unix/linux_like/linux/gnu/mod.rs.html#938
|
||||
// linux/rtnetlink.h
|
||||
pub const TCA_PAD: libc::c_ushort = 9;
|
||||
pub const TCA_DUMP_INVISIBLE: libc::c_ushort = 10;
|
||||
pub const TCA_CHAIN: libc::c_ushort = 11;
|
||||
pub const TCA_HW_OFFLOAD: libc::c_ushort = 12;
|
||||
|
||||
pub const RTM_DELNETCONF: u16 = 81;
|
||||
pub const RTM_NEWSTATS: u16 = 92;
|
||||
pub const RTM_GETSTATS: u16 = 94;
|
||||
pub const RTM_NEWCACHEREPORT: u16 = 96;
|
||||
|
||||
pub const RTM_F_LOOKUP_TABLE: libc::c_uint = 0x1000;
|
||||
pub const RTM_F_FIB_MATCH: libc::c_uint = 0x2000;
|
||||
|
||||
pub const RTA_VIA: libc::c_ushort = 18;
|
||||
pub const RTA_NEWDST: libc::c_ushort = 19;
|
||||
pub const RTA_PREF: libc::c_ushort = 20;
|
||||
pub const RTA_ENCAP_TYPE: libc::c_ushort = 21;
|
||||
pub const RTA_ENCAP: libc::c_ushort = 22;
|
||||
pub const RTA_EXPIRES: libc::c_ushort = 23;
|
||||
pub const RTA_PAD: libc::c_ushort = 24;
|
||||
pub const RTA_UID: libc::c_ushort = 25;
|
||||
pub const RTA_TTL_PROPAGATE: libc::c_ushort = 26;
|
||||
18
netlink/src/lib.rs
Normal file
18
netlink/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod constants;
|
||||
pub mod netlink_interface;
|
||||
pub mod packet;
|
||||
pub mod traits;
|
||||
109
netlink/src/main.rs
Normal file
109
netlink/src/main.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This is just a small test program for testing the netlink integration.
|
||||
|
||||
use bytes::BytesMut;
|
||||
use libc::c_void;
|
||||
use netlink::packet::parse_netlink_message;
|
||||
use netlink::packet::RouteAttribute;
|
||||
use netlink::traits::NetlinkAttribute;
|
||||
use netlink::traits::Serializable;
|
||||
|
||||
use netlink::packet::NetlinkHeader;
|
||||
use netlink::packet::RouteMessage;
|
||||
use std::convert::TryInto;
|
||||
|
||||
fn main() {
|
||||
println!("Starting netlink dump!");
|
||||
|
||||
let nl_fd: libc::c_int;
|
||||
unsafe {
|
||||
// Establish a Netlink socket to the kernel.
|
||||
nl_fd = libc::socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE);
|
||||
if nl_fd < 0 {
|
||||
println!("Failed to create netlink socket: {}", nl_fd);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let sockaddr = libc::sockaddr {
|
||||
sa_family: libc::AF_NETLINK as u16,
|
||||
sa_data: [0i8; 14],
|
||||
};
|
||||
let bind_result = libc::bind(
|
||||
nl_fd,
|
||||
&sockaddr,
|
||||
std::mem::size_of::<libc::sockaddr>().try_into().unwrap(),
|
||||
);
|
||||
if bind_result < 0 {
|
||||
println!("Failed to create netlink socket: {}", nl_fd);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Build a route dump message and send it to the kernel.
|
||||
let mut nl_hdr = NetlinkHeader {
|
||||
nlmsg_type: libc::RTM_NEWROUTE,
|
||||
nlmsg_flags: (libc::NLM_F_REQUEST) as u16,
|
||||
nlmsg_seq: 0xcafe,
|
||||
nlmsg_pid: 0,
|
||||
nlmsg_len: 0,
|
||||
};
|
||||
|
||||
println!("message type: {}", nl_hdr.nlmsg_type);
|
||||
let rt_msg = RouteMessage {
|
||||
af: libc::AF_INET6 as u8,
|
||||
dst_len: 32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let dst_attr = RouteAttribute::Dst(vec![0x20, 0x01, 0xdb, 0x8]);
|
||||
let gateway_addr = RouteAttribute::Gateway(vec![
|
||||
0x2a, 0x0d, 0xd7, 0x40, 0x1, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01,
|
||||
]);
|
||||
|
||||
let len = std::mem::size_of::<NetlinkHeader>()
|
||||
+ std::mem::size_of::<RouteMessage>()
|
||||
+ 4_usize
|
||||
+ dst_attr.payload_len() as usize
|
||||
+ 4_usize
|
||||
+ gateway_addr.payload_len() as usize;
|
||||
nl_hdr.nlmsg_len = len as u32;
|
||||
println!("Length of netlink message: {}", len);
|
||||
|
||||
let mut buf = BytesMut::with_capacity(4096);
|
||||
nl_hdr.to_wire(&mut buf).unwrap();
|
||||
rt_msg.to_wire(&mut buf).unwrap();
|
||||
dst_attr.to_wire(&mut buf).unwrap();
|
||||
gateway_addr.to_wire(&mut buf).unwrap();
|
||||
|
||||
unsafe {
|
||||
let bytes_written = libc::write(nl_fd, buf.as_ptr() as *const c_void, buf.len());
|
||||
println!("bytes_written: {}", bytes_written);
|
||||
}
|
||||
|
||||
let mut resp = BytesMut::with_capacity(4096);
|
||||
|
||||
unsafe {
|
||||
let bytes_read = libc::read(nl_fd, resp.as_mut_ptr() as *mut c_void, 4096);
|
||||
resp.set_len(bytes_read.try_into().unwrap());
|
||||
};
|
||||
|
||||
println!("Read bytes from netlink: {:?}", resp);
|
||||
|
||||
while resp.len() > 3 {
|
||||
let (header, response) = parse_netlink_message(&mut resp).unwrap();
|
||||
println!("Header: {:?} response: {:?}", header, response);
|
||||
}
|
||||
}
|
||||
211
netlink/src/netlink_interface.rs
Normal file
211
netlink/src/netlink_interface.rs
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::packet::parse_netlink_message;
|
||||
use crate::packet::NetlinkHeader;
|
||||
use crate::packet::NetlinkPayload;
|
||||
use crate::packet::RouteAttribute;
|
||||
use crate::packet::RouteMessage;
|
||||
use crate::traits::NetlinkAttribute;
|
||||
use crate::traits::Serializable;
|
||||
use bytes::BytesMut;
|
||||
use libc::c_void;
|
||||
use log::info;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
pub struct NetlinkInterface {
|
||||
nl_fd: libc::c_int,
|
||||
seqno: u32,
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetlinkError {
|
||||
reason: String,
|
||||
}
|
||||
|
||||
impl NetlinkError {
|
||||
fn new(reason: String) -> NetlinkError {
|
||||
NetlinkError { reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NetlinkError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{}", self.reason)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NetlinkError {}
|
||||
|
||||
impl NetlinkInterface {
|
||||
/// # Safety
|
||||
/// This function is unsafe as it manually creates a netlink socket with the socket
|
||||
/// system call.
|
||||
pub unsafe fn new() -> Result<NetlinkInterface, Box<dyn std::error::Error>> {
|
||||
let nl_fd = libc::socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE);
|
||||
if nl_fd < 0 {
|
||||
return Err(Box::new(NetlinkError::new(format!(
|
||||
"Error creating netlink socket: {}",
|
||||
nl_fd
|
||||
))));
|
||||
}
|
||||
let sockaddr = libc::sockaddr {
|
||||
sa_family: libc::AF_NETLINK as u16,
|
||||
sa_data: [0i8; 14],
|
||||
};
|
||||
let bind_result = libc::bind(
|
||||
nl_fd,
|
||||
&sockaddr,
|
||||
std::mem::size_of::<libc::sockaddr>().try_into()?,
|
||||
);
|
||||
if bind_result < 0 {
|
||||
return Err(Box::new(NetlinkError::new(format!(
|
||||
"Failed to bind to netlink socket: {}",
|
||||
bind_result
|
||||
))));
|
||||
}
|
||||
Ok(NetlinkInterface {
|
||||
nl_fd,
|
||||
seqno: 0,
|
||||
buf: BytesMut::with_capacity(4096),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mutate_route(
|
||||
&mut self,
|
||||
add: bool,
|
||||
address_family: u8,
|
||||
dst_prefix: Vec<u8>,
|
||||
prefix_len: u8,
|
||||
gateway: Vec<u8>,
|
||||
table: Option<u32>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!(
|
||||
"Mutate route: {:x?}/{prefix_len} via {:x?}",
|
||||
dst_prefix, gateway
|
||||
);
|
||||
// XXX: Fix this we should reuse the buffer instead of allocating a new one
|
||||
// each time. But there's some bug with how the size is being manipulated
|
||||
// below that causes the buffer to get exhausted.
|
||||
self.buf = BytesMut::with_capacity(4096);
|
||||
|
||||
let msg_type = match add {
|
||||
true => libc::RTM_NEWROUTE,
|
||||
false => libc::RTM_DELROUTE,
|
||||
};
|
||||
self.seqno += 1;
|
||||
let mut nl_hdr = NetlinkHeader {
|
||||
nlmsg_type: msg_type,
|
||||
nlmsg_flags: (libc::NLM_F_REQUEST | libc::NLM_F_ACK) as u16,
|
||||
nlmsg_seq: self.seqno,
|
||||
nlmsg_pid: 0,
|
||||
nlmsg_len: 0, // Filled in later.
|
||||
};
|
||||
|
||||
let rt_msg = RouteMessage {
|
||||
af: address_family,
|
||||
dst_len: prefix_len,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let dst_attr = RouteAttribute::Dst(dst_prefix);
|
||||
let gateway_addr = RouteAttribute::Gateway(gateway);
|
||||
|
||||
nl_hdr.nlmsg_len = std::mem::size_of::<NetlinkHeader>() as u32
|
||||
+ std::mem::size_of::<RouteMessage>() as u32
|
||||
+ 4 // Attribute header
|
||||
+ dst_attr.payload_len() as u32
|
||||
+ 4 // Attribute header
|
||||
+ gateway_addr.payload_len() as u32;
|
||||
|
||||
let mut table_attr: Option<RouteAttribute> = None;
|
||||
if let Some(table_id) = table {
|
||||
table_attr = Some(RouteAttribute::Table(table_id));
|
||||
nl_hdr.nlmsg_len += 4 + table_attr.as_ref().unwrap().payload_len() as u32;
|
||||
}
|
||||
|
||||
// self.buf.clear();
|
||||
nl_hdr.to_wire(&mut self.buf)?;
|
||||
rt_msg.to_wire(&mut self.buf)?;
|
||||
dst_attr.to_wire(&mut self.buf)?;
|
||||
gateway_addr.to_wire(&mut self.buf)?;
|
||||
if let Some(table_attr) = table_attr {
|
||||
table_attr.to_wire(&mut self.buf)?;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let bytes_written = libc::write(
|
||||
self.nl_fd,
|
||||
self.buf.as_ptr() as *const c_void,
|
||||
self.buf.len(),
|
||||
);
|
||||
if bytes_written < 0 {
|
||||
return Err(Box::new(NetlinkError::new(format!(
|
||||
"Failed to write to netlink: {}",
|
||||
bytes_written
|
||||
))));
|
||||
}
|
||||
if bytes_written != self.buf.len() as isize {
|
||||
return Err(Box::new(NetlinkError::new(
|
||||
"Failed to write full message to netlink".to_string(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Read the response back from netlink, should be a ACK or Error.
|
||||
self.buf.clear();
|
||||
|
||||
unsafe {
|
||||
let bytes_read = libc::read(self.nl_fd, self.buf.as_mut_ptr() as *mut c_void, 4906);
|
||||
if bytes_read < 0 {
|
||||
return Err(Box::new(NetlinkError::new(format!(
|
||||
"Failed to read from netlink: {}",
|
||||
bytes_read
|
||||
))));
|
||||
}
|
||||
println!(
|
||||
"bytes_read: {} (usz) {}, cap: {}",
|
||||
bytes_read,
|
||||
(bytes_read as usize),
|
||||
self.buf.capacity()
|
||||
);
|
||||
|
||||
// let read_view = self.buf.clone();
|
||||
self.buf.set_len(bytes_read as usize);
|
||||
|
||||
let (_header, response) = parse_netlink_message(&mut self.buf)?;
|
||||
match response {
|
||||
NetlinkPayload::Error(e) => {
|
||||
if e.error == 0 {
|
||||
// Successful ACK of the route add.
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Box::new(NetlinkError::new(format!(
|
||||
"Got netlink error: {:?}",
|
||||
e
|
||||
))))
|
||||
}
|
||||
}
|
||||
_ => Err(Box::new(NetlinkError::new(format!(
|
||||
"Got unexpected netlink message: {:?}",
|
||||
response
|
||||
)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
614
netlink/src/packet.rs
Normal file
614
netlink/src/packet.rs
Normal file
@ -0,0 +1,614 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::constants;
|
||||
use crate::traits::NetlinkAttribute;
|
||||
use crate::traits::Serializable;
|
||||
use byteorder::ByteOrder;
|
||||
use byteorder::NativeEndian;
|
||||
use byteorder::ReadBytesExt;
|
||||
use byteorder::WriteBytesExt;
|
||||
use bytes::Buf;
|
||||
use bytes::BufMut;
|
||||
use bytes::BytesMut;
|
||||
use log::info;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
|
||||
// XXX: Hack to make libc:: constants the right type.
|
||||
const CONST_NETLINK_ROUTE: u16 = libc::NETLINK_ROUTE as u16;
|
||||
const CONST_NETLINK_NOOP: u16 = libc::NLMSG_NOOP as u16;
|
||||
const CONST_NETLINK_ERR: u16 = libc::NLMSG_ERROR as u16;
|
||||
|
||||
macro_rules! check_vec_len {
|
||||
($payload:expr, $len:expr) => {
|
||||
if $payload.len() != $len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"expected {} bytes of payload, instead got {}",
|
||||
$len,
|
||||
$payload.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NetlinkPayload {
|
||||
Route(RouteMessage, Vec<RouteAttribute>),
|
||||
Error(NetlinkError),
|
||||
Noop(),
|
||||
Done(),
|
||||
}
|
||||
|
||||
pub fn parse_netlink_message(
|
||||
buf: &mut BytesMut,
|
||||
) -> Result<(NetlinkHeader, NetlinkPayload), std::io::Error> {
|
||||
let header = NetlinkHeader::from_wire(buf)?;
|
||||
let payload_len = header.nlmsg_len - std::mem::size_of::<NetlinkHeader>() as u32;
|
||||
if payload_len > buf.len().try_into().unwrap() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
format!(
|
||||
"Requested payload_len > buffer len: {} > {}",
|
||||
payload_len,
|
||||
buf.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
info!(
|
||||
"Calling split_to with payload_len={}, buf.len()={}",
|
||||
payload_len,
|
||||
buf.len()
|
||||
);
|
||||
let payload: &mut BytesMut = &mut buf.split_to(payload_len as usize);
|
||||
|
||||
match header.nlmsg_type {
|
||||
CONST_NETLINK_ERR => {
|
||||
let error = NetlinkError::from_wire(payload)?;
|
||||
Ok((header, NetlinkPayload::Error(error)))
|
||||
}
|
||||
CONST_NETLINK_NOOP => Ok((header, NetlinkPayload::Noop())),
|
||||
CONST_NETLINK_ROUTE => {
|
||||
let (rt_msg, attrs) = take_route_message(payload)?;
|
||||
Ok((header, NetlinkPayload::Route(rt_msg, attrs)))
|
||||
}
|
||||
libc::RTM_NEWROUTE | libc::RTM_GETROUTE | libc::RTM_DELROUTE => {
|
||||
let (rt_msg, attrs) = take_route_message(payload)?;
|
||||
Ok((header, NetlinkPayload::Route(rt_msg, attrs)))
|
||||
}
|
||||
unknown => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
format!("Unknown netlink message type: {}", unknown),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// take_route_messaage attemts to parse a route message and attributes from the
|
||||
/// provided buffer. It expects the header is already removed and the buffer is
|
||||
/// trimmed of any padding.
|
||||
pub fn take_route_message(
|
||||
buf: &mut BytesMut,
|
||||
) -> Result<(RouteMessage, Vec<RouteAttribute>), std::io::Error> {
|
||||
let rt_msg = RouteMessage::from_wire(buf)?;
|
||||
let mut attributes = Vec::<RouteAttribute>::new();
|
||||
|
||||
while buf.len() > 3 {
|
||||
let attr = RouteAttribute::from_wire(buf)?;
|
||||
attributes.push(attr);
|
||||
}
|
||||
|
||||
Ok((rt_msg, attributes))
|
||||
}
|
||||
|
||||
// NetlinkHeader is equivalent to nlmsghdr from the kernel.
|
||||
// https://man7.org/linux/man-pages/man7/netlink.7.html
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct NetlinkHeader {
|
||||
pub nlmsg_len: u32,
|
||||
pub nlmsg_type: u16,
|
||||
pub nlmsg_flags: u16,
|
||||
pub nlmsg_seq: u32,
|
||||
pub nlmsg_pid: u32,
|
||||
}
|
||||
|
||||
impl Serializable<NetlinkHeader> for NetlinkHeader {
|
||||
fn to_wire(&self, buf: &mut BytesMut) -> Result<(), std::io::Error> {
|
||||
let mut writer = buf.writer();
|
||||
writer.write_u32::<NativeEndian>(self.nlmsg_len)?;
|
||||
writer.write_u16::<NativeEndian>(self.nlmsg_type)?;
|
||||
writer.write_u16::<NativeEndian>(self.nlmsg_flags)?;
|
||||
writer.write_u32::<NativeEndian>(self.nlmsg_seq)?;
|
||||
writer.write_u32::<NativeEndian>(self.nlmsg_pid)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn from_wire(buf: &mut BytesMut) -> Result<NetlinkHeader, std::io::Error> {
|
||||
let mut reader = buf.reader();
|
||||
let nlmsg_len = reader.read_u32::<NativeEndian>()?;
|
||||
let nlmsg_type = reader.read_u16::<NativeEndian>()?;
|
||||
let nlmsg_flags = reader.read_u16::<NativeEndian>()?;
|
||||
let nlmsg_seq = reader.read_u32::<NativeEndian>()?;
|
||||
let nlmsg_pid = reader.read_u32::<NativeEndian>()?;
|
||||
|
||||
Ok(NetlinkHeader {
|
||||
nlmsg_len,
|
||||
nlmsg_type,
|
||||
nlmsg_flags,
|
||||
nlmsg_seq,
|
||||
nlmsg_pid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NetlinkHeader {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"NetlinkHeader [ len: {}, type: {}, flags: {}, seq: {}, pid: {} ]",
|
||||
self.nlmsg_len, self.nlmsg_type, self.nlmsg_flags, self.nlmsg_seq, self.nlmsg_pid
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct NetlinkError {
|
||||
pub error: i32,
|
||||
pub msg: NetlinkHeader,
|
||||
// Other attributes that we're not parsing right now.
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Serializable<NetlinkError> for NetlinkError {
|
||||
fn to_wire(&self, buf: &mut BytesMut) -> Result<(), std::io::Error> {
|
||||
buf.writer().write_i32::<NativeEndian>(self.error)?;
|
||||
self.msg.to_wire(buf)?;
|
||||
buf.writer().write_all(&self.payload)?;
|
||||
Ok(())
|
||||
}
|
||||
fn from_wire(buf: &mut BytesMut) -> Result<NetlinkError, std::io::Error> {
|
||||
let mut reader = buf.reader();
|
||||
let error = reader.read_i32::<NativeEndian>()?;
|
||||
let msg = NetlinkHeader::from_wire(buf)?;
|
||||
let payload: Vec<u8> = buf.to_owned().to_vec();
|
||||
Ok(NetlinkError {
|
||||
error,
|
||||
msg,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RouteMessage {
|
||||
// address family
|
||||
pub af: u8,
|
||||
pub dst_len: u8,
|
||||
pub src_len: u8,
|
||||
pub tos: u8,
|
||||
pub table: u8,
|
||||
pub protocol: u8,
|
||||
pub scope: u8,
|
||||
pub r#type: u8,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
impl Serializable<RouteMessage> for RouteMessage {
|
||||
fn to_wire(&self, buf: &mut BytesMut) -> Result<(), std::io::Error> {
|
||||
let mut writer = buf.writer();
|
||||
writer.write_u8(self.af)?;
|
||||
writer.write_u8(self.dst_len)?;
|
||||
writer.write_u8(self.src_len)?;
|
||||
writer.write_u8(self.tos)?;
|
||||
writer.write_u8(self.table)?;
|
||||
writer.write_u8(self.protocol)?;
|
||||
writer.write_u8(self.scope)?;
|
||||
writer.write_u8(self.r#type)?;
|
||||
writer.write_u32::<NativeEndian>(self.flags)?;
|
||||
Ok(())
|
||||
}
|
||||
fn from_wire(buf: &mut BytesMut) -> Result<RouteMessage, std::io::Error> {
|
||||
// Check that the length is at least the size of a RouteMessage
|
||||
if buf.len() < std::mem::size_of::<RouteMessage>() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Buffer not large enough to read RouteMessage".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut reader = buf.reader();
|
||||
let af = reader.read_u8()?;
|
||||
let dst_len = reader.read_u8()?;
|
||||
let src_len = reader.read_u8()?;
|
||||
let tos = reader.read_u8()?;
|
||||
let table = reader.read_u8()?;
|
||||
let protocol = reader.read_u8()?;
|
||||
let scope = reader.read_u8()?;
|
||||
let r#type = reader.read_u8()?;
|
||||
let flags = reader.read_u32::<NativeEndian>()?;
|
||||
|
||||
Ok(RouteMessage {
|
||||
af,
|
||||
dst_len,
|
||||
src_len,
|
||||
tos,
|
||||
table,
|
||||
protocol,
|
||||
scope,
|
||||
r#type,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum RouteAttribute {
|
||||
Dst(Vec<u8>),
|
||||
Src(Vec<u8>),
|
||||
Iif(u32),
|
||||
Oif(u32),
|
||||
Gateway(Vec<u8>),
|
||||
Priority(u32),
|
||||
Prefsrc(u32),
|
||||
Metrics(u32),
|
||||
// TODO: support multipath attribute properly
|
||||
Multipath(Vec<u8>),
|
||||
Flow(u32),
|
||||
// TODO: support cacheinfo properly
|
||||
CacheInfo(Vec<u8>),
|
||||
Table(u32),
|
||||
Mark(u32),
|
||||
// TODO: support mfc_stats properly
|
||||
MfcStats(Vec<u8>),
|
||||
// TODO: support via properly
|
||||
Via(Vec<u8>),
|
||||
NewDst(Vec<u8>),
|
||||
Pref(u8),
|
||||
EnacpType(u16),
|
||||
Encap(Vec<u8>),
|
||||
}
|
||||
|
||||
impl NetlinkAttribute for RouteAttribute {
|
||||
fn attr_type(&self) -> u16 {
|
||||
match self {
|
||||
RouteAttribute::Dst(_) => constants::RTA_DST,
|
||||
RouteAttribute::Src(_) => constants::RTA_SRC,
|
||||
RouteAttribute::Iif(_) => constants::RTA_IIF,
|
||||
RouteAttribute::Oif(_) => constants::RTA_OIF,
|
||||
RouteAttribute::Gateway(_) => constants::RTA_GATEWAY,
|
||||
RouteAttribute::Priority(_) => constants::RTA_PRIORITY,
|
||||
RouteAttribute::Prefsrc(_) => constants::RTA_PREFSRC,
|
||||
RouteAttribute::Metrics(_) => constants::RTA_METRICS,
|
||||
RouteAttribute::Multipath(_) => constants::RTA_MULTIPATH,
|
||||
RouteAttribute::Flow(_) => constants::RTA_FLOW,
|
||||
RouteAttribute::CacheInfo(_) => constants::RTA_CACHEINFO,
|
||||
RouteAttribute::Table(_) => constants::RTA_TABLE,
|
||||
RouteAttribute::Mark(_) => constants::RTA_MARK,
|
||||
RouteAttribute::MfcStats(_) => constants::RTA_MFC_STATS,
|
||||
RouteAttribute::Via(_) => constants::RTA_VIA,
|
||||
RouteAttribute::NewDst(_) => constants::RTA_NEWDST,
|
||||
RouteAttribute::Pref(_) => constants::RTA_PREF,
|
||||
RouteAttribute::EnacpType(_) => constants::RTA_ENCAP_TYPE,
|
||||
RouteAttribute::Encap(_) => constants::RTA_ENCAP,
|
||||
}
|
||||
}
|
||||
fn payload_len(&self) -> u16 {
|
||||
match self {
|
||||
RouteAttribute::Dst(dst) => dst.len() as u16,
|
||||
RouteAttribute::Src(src) => src.len() as u16,
|
||||
RouteAttribute::Iif(_) => 4,
|
||||
RouteAttribute::Oif(_) => 4,
|
||||
RouteAttribute::Gateway(gateway) => gateway.len() as u16,
|
||||
RouteAttribute::Priority(_) => 4,
|
||||
RouteAttribute::Prefsrc(_) => 4,
|
||||
RouteAttribute::Metrics(_) => 4,
|
||||
RouteAttribute::Multipath(multipath) => multipath.len() as u16,
|
||||
RouteAttribute::Flow(_) => 4,
|
||||
RouteAttribute::CacheInfo(cacheinfo) => cacheinfo.len() as u16,
|
||||
RouteAttribute::Table(_) => 4,
|
||||
RouteAttribute::Mark(_) => 4,
|
||||
RouteAttribute::MfcStats(stats) => stats.len() as u16,
|
||||
RouteAttribute::Via(via) => via.len() as u16,
|
||||
RouteAttribute::NewDst(newdst) => newdst.len() as u16,
|
||||
RouteAttribute::Pref(_) => 1,
|
||||
RouteAttribute::EnacpType(_) => 2,
|
||||
RouteAttribute::Encap(encap) => encap.len() as u16,
|
||||
}
|
||||
}
|
||||
fn write_payload(&self, buf: &mut BytesMut) -> Result<(), std::io::Error> {
|
||||
let mut writer = buf.writer();
|
||||
match self {
|
||||
RouteAttribute::Dst(dst) => buf.put(dst.as_slice()),
|
||||
RouteAttribute::Src(src) => buf.put(src.as_slice()),
|
||||
RouteAttribute::Iif(iif) => writer.write_u32::<NativeEndian>(*iif)?,
|
||||
RouteAttribute::Oif(oif) => writer.write_u32::<NativeEndian>(*oif)?,
|
||||
RouteAttribute::Gateway(gateway) => buf.put(gateway.as_slice()),
|
||||
RouteAttribute::Priority(priority) => writer.write_u32::<NativeEndian>(*priority)?,
|
||||
RouteAttribute::Prefsrc(prefsrc) => writer.write_u32::<NativeEndian>(*prefsrc)?,
|
||||
RouteAttribute::Metrics(metrics) => writer.write_u32::<NativeEndian>(*metrics)?,
|
||||
RouteAttribute::Multipath(multipath) => buf.put(multipath.as_slice()),
|
||||
RouteAttribute::Flow(flow) => writer.write_u32::<NativeEndian>(*flow)?,
|
||||
RouteAttribute::CacheInfo(cacheinfo) => buf.put(cacheinfo.as_slice()),
|
||||
RouteAttribute::Table(table) => writer.write_u32::<NativeEndian>(*table)?,
|
||||
RouteAttribute::Mark(mark) => writer.write_u32::<NativeEndian>(*mark)?,
|
||||
RouteAttribute::MfcStats(stats) => buf.put(stats.as_slice()),
|
||||
RouteAttribute::Via(via) => buf.put(via.as_slice()),
|
||||
RouteAttribute::NewDst(newdst) => buf.put(newdst.as_slice()),
|
||||
RouteAttribute::Pref(pref) => buf.put_u8(*pref),
|
||||
RouteAttribute::EnacpType(encaptype) => writer.write_u16::<NativeEndian>(*encaptype)?,
|
||||
RouteAttribute::Encap(encap) => buf.put(encap.as_slice()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable<RouteAttribute> for RouteAttribute {
|
||||
fn to_wire(&self, buf: &mut BytesMut) -> Result<(), std::io::Error> {
|
||||
// Write Type, Length, Value then pad to 4 byte boundary.
|
||||
let mut writer = buf.writer();
|
||||
writer.write_u16::<NativeEndian>(self.payload_len() + 4)?;
|
||||
writer.write_u16::<NativeEndian>(self.attr_type())?;
|
||||
self.write_payload(buf)?;
|
||||
|
||||
// Align the attribute to a four byte boundary.
|
||||
let padding = (4 + self.payload_len()) % 4;
|
||||
buf.put(vec![0u8; padding.into()].as_slice());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn from_wire(buf: &mut BytesMut) -> Result<RouteAttribute, std::io::Error> {
|
||||
let mut reader = buf.reader();
|
||||
let attr_len: u16 = reader.read_u16::<NativeEndian>()?;
|
||||
let attr_type: u16 = reader.read_u16::<NativeEndian>()?;
|
||||
let padding = attr_len % 4;
|
||||
|
||||
if attr_len < 4 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"route attr cannot have length < 4",
|
||||
));
|
||||
}
|
||||
let payload_len = attr_len - 4;
|
||||
if buf.remaining() < payload_len.into() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Route attribute length was {} but buf has {} remaining",
|
||||
payload_len,
|
||||
buf.remaining()
|
||||
),
|
||||
));
|
||||
}
|
||||
let mut payload: Vec<u8> = vec![0u8; payload_len.into()];
|
||||
let bytes_read = buf.reader().read(&mut payload)?;
|
||||
if bytes_read != payload_len.into() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Failed to read {} bytes of payload, instead got {}",
|
||||
payload_len, bytes_read
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Move buf past padding bytes.
|
||||
buf.advance(padding.into());
|
||||
|
||||
match attr_type {
|
||||
constants::RTA_DST => Ok(RouteAttribute::Dst(payload)),
|
||||
constants::RTA_SRC => Ok(RouteAttribute::Src(payload)),
|
||||
constants::RTA_IIF => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Iif(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_OIF => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Oif(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_GATEWAY => Ok(RouteAttribute::Gateway(payload)),
|
||||
constants::RTA_PRIORITY => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Priority(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_PREFSRC => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Prefsrc(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_METRICS => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Metrics(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_MULTIPATH => Ok(RouteAttribute::Multipath(payload)),
|
||||
constants::RTA_FLOW => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Flow(NativeEndian::read_u32(buf)))
|
||||
}
|
||||
constants::RTA_CACHEINFO => Ok(RouteAttribute::CacheInfo(payload)),
|
||||
constants::RTA_TABLE => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Table(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_MARK => {
|
||||
check_vec_len!(payload, 4);
|
||||
Ok(RouteAttribute::Mark(NativeEndian::read_u32(&payload)))
|
||||
}
|
||||
constants::RTA_MFC_STATS => Ok(RouteAttribute::MfcStats(payload)),
|
||||
constants::RTA_VIA => Ok(RouteAttribute::CacheInfo(payload)),
|
||||
constants::RTA_NEWDST => Ok(RouteAttribute::CacheInfo(payload)),
|
||||
constants::RTA_PREF => {
|
||||
check_vec_len!(payload, 1);
|
||||
Ok(RouteAttribute::Pref(payload[0]))
|
||||
}
|
||||
constants::RTA_ENCAP_TYPE => {
|
||||
check_vec_len!(payload, 2);
|
||||
Ok(RouteAttribute::EnacpType(NativeEndian::read_u16(&payload)))
|
||||
}
|
||||
constants::RTA_ENCAP => Ok(RouteAttribute::Encap(payload)),
|
||||
_ => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("Unknown attribute type: {}", attr_type),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::RouteAttribute;
|
||||
use crate::packet::parse_netlink_message;
|
||||
use crate::traits::Serializable;
|
||||
use bytes::BytesMut;
|
||||
|
||||
#[test]
|
||||
fn routemessage_roundtrip() {
|
||||
let _payload = &[
|
||||
0x74, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x35, 0x86, 0x00, 0x00, 0x31, 0x2f,
|
||||
0x05, 0x00, 0x0a, 0x80, 0x00, 0x00, 0xfe, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x08, 0x00, 0x0f, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x08, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x24, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rta_table() {
|
||||
let payload: &[u8] = &[0x08, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x00];
|
||||
let attr = RouteAttribute::from_wire(&mut BytesMut::from(payload));
|
||||
assert_eq!(RouteAttribute::Table(0xff), attr.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rta_dst() {
|
||||
let payload: &[u8] = &[
|
||||
0x14, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
let attr = RouteAttribute::from_wire(&mut BytesMut::from(payload));
|
||||
assert_eq!(
|
||||
RouteAttribute::Dst(vec![255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,]),
|
||||
attr.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_netlink_example_error() {
|
||||
let payload_str = "58000000020000000319000022311300edffffff410000001800050003190000000000000a280000000000000000000009000100200116b8170014000500200108e009ff2000000000000000000208000f00c90000000000";
|
||||
let payload = hex::decode(payload_str).expect("Test data hex decode failed");
|
||||
let mut buf = BytesMut::from(payload.as_slice());
|
||||
let res = parse_netlink_message(&mut buf).unwrap();
|
||||
println!("Parsed netlink message: {:?}", res);
|
||||
// assert_eq!(initial_capacity, buf.capacity());
|
||||
}
|
||||
|
||||
// TODO: Clean this up to test only rtnetlink messages.
|
||||
// This blob contains link add meessages.
|
||||
// #[test]
|
||||
// fn parse_netlink_message_invariants() {
|
||||
// let payload: &[u8] = &[
|
||||
// /* Netlink route*/ 0xf4, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x65, 0xbf,
|
||||
// 0xe2, 0x61, 0xe8, 0x48, 0x85, 0xaa, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
// 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x77, 0x6c,
|
||||
// 0x70, 0x30, 0x73, 0x32, 0x30, 0x66, 0x33, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00,
|
||||
// 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00,
|
||||
// 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x8c, 0x05, 0x00, 0x00,
|
||||
// 0x08, 0x00, 0x32, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0x00, 0x09,
|
||||
// 0x00, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1e, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
// 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
// 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00,
|
||||
// 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00,
|
||||
// 0x08, 0x00, 0x23, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x2f, 0x00,
|
||||
// 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
|
||||
// 0x01, 0x00, 0x1c, 0x99, 0x57, 0xd9, 0x60, 0xa2, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00,
|
||||
// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc4, 0x00, 0x17, 0x00, 0x9a, 0x2d,
|
||||
// 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x62, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xb6, 0x31, 0x63, 0x29, 0x03, 0x00, 0x00, 0x00, 0x15, 0xff, 0x5f, 0x7e, 0x01, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07, 0x00, 0x9a, 0x2d,
|
||||
// 0xbd, 0x00, 0xc2, 0x62, 0x53, 0x00, 0xb6, 0x31, 0x63, 0x29, 0x15, 0xff, 0x5f, 0x7e,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x15, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x0a, 0x00, 0x36, 0x00, 0x1c, 0x99, 0x57, 0xd9, 0x60, 0xa2, 0x00, 0x00,
|
||||
// 0x90, 0x01, 0x1a, 0x00, 0x88, 0x00, 0x02, 0x00, 0x84, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x04, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x14, 0x00,
|
||||
// 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0xf2, 0xea, 0xf6, 0x00, 0x44, 0x66, 0x00, 0x00,
|
||||
// 0xe8, 0x03, 0x00, 0x00, 0xe4, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
// 0x00, 0x00, 0x8c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f,
|
||||
// 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, 0x09, 0x00,
|
||||
// 0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee,
|
||||
// 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x11, 0x00, 0x38, 0x00, 0x30, 0x30,
|
||||
// 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x31, 0x34, 0x2e, 0x33, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x08, 0x00, 0x39, 0x00, 0x70, 0x63, 0x69, 0x00,
|
||||
// ];
|
||||
|
||||
// let mut buf = BytesMut::from(payload);
|
||||
// let initial_capacity = buf.capacity();
|
||||
// let res = parse_netlink_message(&mut buf);
|
||||
// println!("Parsed netlink message: {:?}", res);
|
||||
// assert_eq!(initial_capacity, buf.capacity());
|
||||
// }
|
||||
}
|
||||
26
netlink/src/traits.rs
Normal file
26
netlink/src/traits.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2021 Rayhaan Jaufeerally.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use bytes::BytesMut;
|
||||
|
||||
pub trait Serializable<A> {
|
||||
fn to_wire(&self, buf: &mut BytesMut) -> Result<(), std::io::Error>;
|
||||
fn from_wire(buf: &mut BytesMut) -> Result<A, std::io::Error>;
|
||||
}
|
||||
|
||||
pub trait NetlinkAttribute {
|
||||
fn attr_type(&self) -> u16;
|
||||
fn payload_len(&self) -> u16;
|
||||
fn write_payload(&self, buf: &mut BytesMut) -> Result<(), std::io::Error>;
|
||||
}
|
||||
Reference in New Issue
Block a user