Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
bridge vlan info: expand VlanInfo of AfSpecBridge
Browse files Browse the repository at this point in the history
To simplify the efforts on parsing linux bridge vlan filter, this patch
is introducing `struct BridgeVlanInfo` to hold VlanInfo of AfSpecBridge.

This C struct is defined in `linux/if_bridge.h` as `struct
bridge_vlan_info`.

Example code included as `dump_packet_link_bridge_vlan.rs`.
To try out of linux bridge VLAN filtering, you may:
```
sudo ip link add eth1 type veth peer name eth1.ep
sudo ip link add br0 type bridge
sudo ip link set eth1 master br0
sudo ip link set eth1 up
sudo ip link set eth1.ep up
sudo ip link set br0 up
sudo bridge vlan add vid 10 pvid untagged dev eth1
sudo bridge vlan add vid 20-4094 dev eth1
sudo ip link set br0 type bridge vlan_filtering 1
cargo run --example dump_packet_link_bridge_vlan
```

The example code is manually tested on kernel 5.14.0-80.el9.x86_64 and
4.18.0-383.el8.x86_64.

Signed-off-by: Gris Ge <cnfourt@gmail.com>
  • Loading branch information
cathay4t committed May 10, 2022
1 parent 3815c22 commit f21ddb2
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 5 deletions.
82 changes: 82 additions & 0 deletions netlink-packet-route/examples/dump_packet_link_bridge_vlan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT

use netlink_packet_route::{
nlas::link::Nla,
LinkMessage,
NetlinkHeader,
NetlinkMessage,
NetlinkPayload,
RtnlMessage,
AF_BRIDGE,
NLM_F_DUMP,
NLM_F_REQUEST,
RTEXT_FILTER_BRVLAN_COMPRESSED,
};
use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};

fn main() {
let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
let _port_number = socket.bind_auto().unwrap().port_number();
socket.connect(&SocketAddr::new(0, 0)).unwrap();

let mut message = LinkMessage::default();
message.header.interface_family = AF_BRIDGE as u8;
message
.nlas
.push(Nla::ExtMask(RTEXT_FILTER_BRVLAN_COMPRESSED));
let mut packet = NetlinkMessage {
header: NetlinkHeader::default(),
payload: NetlinkPayload::from(RtnlMessage::GetLink(message)),
};
packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST;
packet.header.sequence_number = 1;
packet.finalize();

let mut buf = vec![0; packet.header.length as usize];

// Before calling serialize, it is important to check that the buffer in which we're emitting is big
// enough for the packet, other `serialize()` panics.
assert!(buf.len() == packet.buffer_len());
packet.serialize(&mut buf[..]);

println!(">>> {:?}", packet);
socket.send(&buf[..], 0).unwrap();

let mut receive_buffer = vec![0; 4096];
let mut offset = 0;

// we set the NLM_F_DUMP flag so we expect a multipart rx_packet in response.
loop {
let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap();

loop {
let bytes = &receive_buffer[offset..];
// Note that we're parsing a NetlinkBuffer<&&[u8]>, NOT a NetlinkBuffer<&[u8]> here.
// This is important because Parseable<NetlinkMessage> is only implemented for
// NetlinkBuffer<&'a T>, where T implements AsRef<[u8] + 'a. This is not
// particularly user friendly, but this is a low level library anyway.
//
// Note also that the same could be written more explicitely with:
//
// let rx_packet =
// <NetlinkBuffer<_> as Parseable<NetlinkMessage>>::parse(NetlinkBuffer::new(&bytes))
// .unwrap();
//
let rx_packet: NetlinkMessage<RtnlMessage> =
NetlinkMessage::deserialize(bytes).unwrap();

println!("<<< {:?}", rx_packet);

if rx_packet.payload == NetlinkPayload::Done {
println!("Done!");
return;
}

offset += rx_packet.header.length as usize;
if offset == size || rx_packet.header.length == 0 {
offset = 0;
break;
}
}
}
}
49 changes: 44 additions & 5 deletions netlink-packet-route/src/rtnl/link/nlas/af_spec_bridge.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// SPDX-License-Identifier: MIT

use std::convert::TryFrom;

use anyhow::Context;

use crate::{
Expand All @@ -14,15 +17,15 @@ use byteorder::{ByteOrder, NativeEndian};
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum AfSpecBridge {
Flags(u16),
VlanInfo(Vec<u8>),
VlanInfo(BridgeVlanInfo),
Other(DefaultNla),
}

impl nlas::Nla for AfSpecBridge {
fn value_len(&self) -> usize {
use self::AfSpecBridge::*;
match *self {
VlanInfo(ref bytes) => bytes.len(),
VlanInfo(_) => 4,
Flags(_) => 2,
Other(ref nla) => nla.value_len(),
}
Expand All @@ -32,8 +35,8 @@ impl nlas::Nla for AfSpecBridge {
use self::AfSpecBridge::*;
match *self {
Flags(value) => NativeEndian::write_u16(buffer, value),
VlanInfo(ref bytes) => {
(&mut buffer[..bytes.len()]).copy_from_slice(bytes.as_slice());
VlanInfo(ref info) => {
(&mut buffer[..4]).copy_from_slice(<[u8; 4]>::from(info).as_slice())
}
Other(ref nla) => nla.emit_value(buffer),
}
Expand All @@ -55,11 +58,47 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for AfSpecBridge {

let payload = buf.value();
Ok(match buf.kind() {
IFLA_BRIDGE_VLAN_INFO => VlanInfo(payload.to_vec()),
IFLA_BRIDGE_VLAN_INFO => VlanInfo(
BridgeVlanInfo::try_from(payload).context("Invalid IFLA_BRIDGE_VLAN_INFO value")?,
),
IFLA_BRIDGE_FLAGS => {
Flags(parse_u16(payload).context("invalid IFLA_BRIDGE_FLAGS value")?)
}
kind => Other(DefaultNla::parse(buf).context(format!("Unknown NLA type {}", kind))?),
})
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BridgeVlanInfo {
pub flags: u16,
pub vid: u16,
}

impl From<&BridgeVlanInfo> for [u8; 4] {
fn from(d: &BridgeVlanInfo) -> Self {
let mut ret = [0u8; 4];
NativeEndian::write_u16(&mut ret[0..2], d.flags);
NativeEndian::write_u16(&mut ret[2..4], d.vid);
ret
}
}

impl TryFrom<&[u8]> for BridgeVlanInfo {
type Error = DecodeError;
fn try_from(raw: &[u8]) -> Result<Self, DecodeError> {
if raw.len() == 4 {
Ok(Self {
flags: parse_u16(&raw[0..2])
.context(format!("Invalid IFLA_BRIDGE_VLAN_INFO value: {:?}", raw))?,
vid: parse_u16(&raw[2..4])
.context(format!("Invalid IFLA_BRIDGE_VLAN_INFO value: {:?}", raw))?,
})
} else {
Err(DecodeError::from(format!(
"Invalid IFLA_BRIDGE_VLAN_INFO value, expecting [u8;4], but got {:?}",
raw
)))
}
}
}

0 comments on commit f21ddb2

Please sign in to comment.