Re-import repository.
This commit is contained in:
11
netlink/.gitignore
vendored
Normal file
11
netlink/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
15
netlink/Cargo.toml
Normal file
15
netlink/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "netlink"
|
||||
version = "0.1.0"
|
||||
authors = ["rayhaan"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
bytes = "1"
|
||||
byteorder = "1.4.3"
|
||||
log = "0.4"
|
||||
hex = "0.4.3"
|
||||
neli = "0.6.2"
|
||||
33
netlink/README.md
Normal file
33
netlink/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Netlink
|
||||
|
||||
This project was created to have an easy way to manipulate routes in the Linux kernel using the Netlink protocol.
|
||||
|
||||
There are some other libraries which provide similar functionality, but which were not offering the exact API which was desired to quickly modify routing state from control plane routing protocol daemons.
|
||||
|
||||
The API that this crate provides is (currently) specifically only for mutating routes using the following function:
|
||||
|
||||
```rust
|
||||
// Create a handle which opens up a socket to the kernel.
|
||||
let nl_iface = NetlinkInterface::new().unwrap();
|
||||
|
||||
// Modify a route
|
||||
let af: u8 = 2; // Address family 1 is IPv6.
|
||||
let dst_prefix = vec![0x20, 0x01, 0xdb, 0x8]; // 2001:db8::.
|
||||
let dst_prefix_len = 32; // Specifying the prefix length is 32 bits.
|
||||
let gateway_addr = vec![ // Nexthop / gateway to send packets to.
|
||||
0x2a, 0x0d, 0xd7, 0x40, 0x01, 0x05, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
];
|
||||
let rt_table = 200; // Install this route into table 200.
|
||||
|
||||
self.nl_iface.mutate_route(
|
||||
true, // Add a route, false would be for removing a route.
|
||||
af,
|
||||
dst_prefix,
|
||||
dst_prefix_len,
|
||||
gateway_addr,
|
||||
Some(rt_table)).unwrap();
|
||||
|
||||
```
|
||||
|
||||
Internally `RouteMessage` is used to represent a [rtmsg](https://man7.org/linux/man-pages/man7/rtnetlink.7.html) to the kernel, with a set of `RouteAttributes` that's attached to a particular `rtmsg`.
|
||||
BIN
netlink/netlink.pcap
Normal file
BIN
netlink/netlink.pcap
Normal file
Binary file not shown.
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