Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bridge port: Introduce BridgeFlag, BridgeMode and BridgeVlanTunnelInfo #132

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 251 additions & 38 deletions src/link/af_spec/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,98 @@
use anyhow::Context;
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
nla::{self, DefaultNla, NlaBuffer},
parsers::parse_u16,
nla::{DefaultNla, Nla, NlaBuffer, NlasIterator},
parsers::{parse_u16, parse_u32},
traits::Parseable,
DecodeError,
DecodeError, Emitable,
};

const IFLA_BRIDGE_FLAGS: u16 = 0;
const IFLA_BRIDGE_MODE: u16 = 1;
const IFLA_BRIDGE_VLAN_INFO: u16 = 2;
const IFLA_BRIDGE_VLAN_TUNNEL_INFO: u16 = 3;
// const IFLA_BRIDGE_MRP: u16 = 4;
// const IFLA_BRIDGE_CFM: u16 = 5;
// const IFLA_BRIDGE_MST: u16 = 6;

#[derive(Clone, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum AfSpecBridge {
Flags(u16),
Flags(BridgeFlag),
Mode(BridgeMode),
VlanInfo(BridgeVlanInfo),
VlanTunnelInfo(Vec<BridgeVlanTunnelInfo>),
Other(DefaultNla),
}

impl nla::Nla for AfSpecBridge {
impl Nla for AfSpecBridge {
fn value_len(&self) -> usize {
use self::AfSpecBridge::*;
match *self {
VlanInfo(_) => 4,
Flags(_) => 2,
Other(ref nla) => nla.value_len(),
match self {
Self::Flags(_) => BridgeFlag::LENGTH,
Self::Mode(_) => BridgeMode::LENGTH,
Self::VlanInfo(_) => BridgeVlanInfo::LENGTH,
Self::VlanTunnelInfo(s) => s.as_slice().buffer_len(),
Self::Other(nla) => nla.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
use self::AfSpecBridge::*;
match *self {
Flags(value) => NativeEndian::write_u16(buffer, value),
VlanInfo(ref info) => {
match self {
Self::Flags(value) => {
NativeEndian::write_u16(buffer, u16::from(*value))
}
Self::Mode(value) => {
NativeEndian::write_u16(buffer, u16::from(*value))
}
Self::VlanInfo(info) => {
buffer[..4].copy_from_slice(<[u8; 4]>::from(info).as_slice())
}
Other(ref nla) => nla.emit_value(buffer),
Self::VlanTunnelInfo(s) => s.as_slice().emit(buffer),
Self::Other(nla) => nla.emit_value(buffer),
}
}

fn kind(&self) -> u16 {
use self::AfSpecBridge::*;
match *self {
Flags(_) => IFLA_BRIDGE_FLAGS,
VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO,
Other(ref nla) => nla.kind(),
match self {
Self::Flags(_) => IFLA_BRIDGE_FLAGS,
Self::Mode(_) => IFLA_BRIDGE_MODE,
Self::VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO,
Self::VlanTunnelInfo(_) => IFLA_BRIDGE_VLAN_TUNNEL_INFO,
Self::Other(nla) => nla.kind(),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for AfSpecBridge {
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
use self::AfSpecBridge::*;

let payload = buf.value();
Ok(match buf.kind() {
IFLA_BRIDGE_VLAN_INFO => VlanInfo(
BridgeVlanInfo::try_from(payload)
.context("Invalid IFLA_BRIDGE_VLAN_INFO value")?,
IFLA_BRIDGE_FLAGS => Self::Flags(
parse_u16(payload)
.context("Invalid IFLA_BRIDGE_FLAGS value")?
.into(),
),
IFLA_BRIDGE_FLAGS => Flags(
IFLA_BRIDGE_MODE => Self::Mode(
parse_u16(payload)
.context("invalid IFLA_BRIDGE_FLAGS value")?,
.context("Invalid IFLA_BRIDGE_MODE value")?
.into(),
),
kind => Other(
IFLA_BRIDGE_VLAN_INFO => Self::VlanInfo(
BridgeVlanInfo::try_from(payload)
.context("Invalid IFLA_BRIDGE_VLAN_INFO value")?,
),
IFLA_BRIDGE_VLAN_TUNNEL_INFO => {
let mut nlas = Vec::new();
for nla in NlasIterator::new(payload) {
let nla = &nla.context(format!(
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_INFO for {payload:?}"
))?;
let parsed = BridgeVlanTunnelInfo::parse(nla)?;
nlas.push(parsed);
}
Self::VlanTunnelInfo(nlas)
}
kind => Self::Other(
DefaultNla::parse(buf)
.context(format!("Unknown NLA type {kind}"))?,
),
Expand All @@ -83,27 +112,62 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let mut nlas = vec![];
let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_BRIDGE)";
for nla in
netlink_packet_utils::nla::NlasIterator::new(buf.into_inner())
{
for nla in NlasIterator::new(buf.into_inner()) {
let nla = nla.context(err)?;
nlas.push(AfSpecBridge::parse(&nla).context(err)?);
}
Ok(Self(nlas))
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
#[non_exhaustive]
const BRIDGE_VLAN_INFO_CONTROLLER: u16 = 1 << 0;
const BRIDGE_VLAN_INFO_PVID: u16 = 1 << 1;
const BRIDGE_VLAN_INFO_UNTAGGED: u16 = 1 << 2;
const BRIDGE_VLAN_INFO_RANGE_BEGIN: u16 = 1 << 3;
const BRIDGE_VLAN_INFO_RANGE_END: u16 = 1 << 4;
const BRIDGE_VLAN_INFO_BRENTRY: u16 = 1 << 5;
const BRIDGE_VLAN_INFO_ONLY_OPTS: u16 = 1 << 6;

bitflags! {
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BridgeVlanInfoFlags: u16 {
/// Operate on Bridge device as well
const Controller = BRIDGE_VLAN_INFO_CONTROLLER;
/// VLAN is PVID, ingress untagged
const Pvid = BRIDGE_VLAN_INFO_PVID;
/// VLAN egresses untagged
const Untagged= BRIDGE_VLAN_INFO_UNTAGGED;
/// VLAN is start of vlan range
const RangeBegin = BRIDGE_VLAN_INFO_RANGE_BEGIN;
/// VLAN is end of vlan range
const RangeEnd = BRIDGE_VLAN_INFO_RANGE_END;
/// Global bridge VLAN entry
const Brentry = BRIDGE_VLAN_INFO_BRENTRY;
/// Skip create/delete/flags
const OnlyOpts= BRIDGE_VLAN_INFO_ONLY_OPTS;
const _ = !0;
}
}

impl BridgeVlanInfoFlags {
pub const LENGTH: usize = 2;
}

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

impl BridgeVlanInfo {
pub const LENGTH: usize = 4;
}

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[0..2], d.flags.bits());
NativeEndian::write_u16(&mut ret[2..4], d.vid);
ret
}
Expand All @@ -114,9 +178,11 @@ impl TryFrom<&[u8]> for BridgeVlanInfo {
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:?}"
))?,
flags: BridgeVlanInfoFlags::from_bits_retain(
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:?}"
))?,
Expand All @@ -129,3 +195,150 @@ impl TryFrom<&[u8]> for BridgeVlanInfo {
}
}
}

// kernel constant name is BRIDGE_FLAGS_MASTER
const BRIDGE_FLAGS_CONTROLLER: u16 = 1;
const BRIDGE_FLAGS_SELF: u16 = 2;

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum BridgeFlag {
/// Bridge command to/from controller
Controller,
/// Bridge command to/from lowerdev
LowerDev,
Other(u16),
}

impl From<u16> for BridgeFlag {
fn from(d: u16) -> Self {
match d {
BRIDGE_FLAGS_CONTROLLER => Self::Controller,
BRIDGE_FLAGS_SELF => Self::LowerDev,
_ => Self::Other(d),
}
}
}

impl From<BridgeFlag> for u16 {
fn from(v: BridgeFlag) -> u16 {
match v {
BridgeFlag::Controller => BRIDGE_FLAGS_CONTROLLER,
BridgeFlag::LowerDev => BRIDGE_FLAGS_SELF,
BridgeFlag::Other(d) => d,
}
}
}

impl BridgeFlag {
pub const LENGTH: usize = 2;
}

const BRIDGE_MODE_VEB: u16 = 0;
const BRIDGE_MODE_VEPA: u16 = 1;

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum BridgeMode {
/// Default loopback mode
Veb,
/// 802.1Qbg defined VEPA mode
Vepa,
Other(u16),
}

impl From<u16> for BridgeMode {
fn from(d: u16) -> Self {
match d {
BRIDGE_MODE_VEB => Self::Veb,
BRIDGE_MODE_VEPA => Self::Vepa,
_ => Self::Other(d),
}
}
}

impl From<BridgeMode> for u16 {
fn from(v: BridgeMode) -> u16 {
match v {
BridgeMode::Veb => BRIDGE_MODE_VEB,
BridgeMode::Vepa => BRIDGE_MODE_VEPA,
BridgeMode::Other(d) => d,
}
}
}

impl BridgeMode {
pub const LENGTH: usize = 2;
}

const IFLA_BRIDGE_VLAN_TUNNEL_ID: u16 = 1;
const IFLA_BRIDGE_VLAN_TUNNEL_VID: u16 = 2;
const IFLA_BRIDGE_VLAN_TUNNEL_FLAGS: u16 = 3;

#[derive(Clone, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum BridgeVlanTunnelInfo {
Id(u32),
Vid(u16),
Flags(BridgeVlanInfoFlags),
Other(DefaultNla),
}

impl Nla for BridgeVlanTunnelInfo {
fn value_len(&self) -> usize {
match self {
Self::Id(_) => 4,
Self::Vid(_) => 2,
Self::Flags(_) => BridgeVlanInfoFlags::LENGTH,
Self::Other(nla) => nla.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Id(v) => NativeEndian::write_u32(buffer, *v),
Self::Vid(v) => NativeEndian::write_u16(buffer, *v),
Self::Flags(value) => NativeEndian::write_u16(buffer, value.bits()),
Self::Other(nla) => nla.emit_value(buffer),
}
}

fn kind(&self) -> u16 {
match self {
Self::Id(_) => IFLA_BRIDGE_VLAN_TUNNEL_ID,
Self::Vid(_) => IFLA_BRIDGE_VLAN_TUNNEL_VID,
Self::Flags(_) => IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
Self::Other(nla) => nla.kind(),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
for BridgeVlanTunnelInfo
{
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
IFLA_BRIDGE_VLAN_TUNNEL_ID => {
Self::Id(parse_u32(payload).context(format!(
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_ID {payload:?}"
))?)
}
IFLA_BRIDGE_VLAN_TUNNEL_VID => {
Self::Vid(parse_u16(payload).context(format!(
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}"
))?)
}
IFLA_BRIDGE_VLAN_TUNNEL_FLAGS => {
Self::Flags(BridgeVlanInfoFlags::from_bits_retain(
parse_u16(payload).context(format!(
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}"
))?,
))
}
_ => {
Self::Other(DefaultNla::parse(buf).context("Unknown NLA type")?)
}
})
}
}
5 changes: 4 additions & 1 deletion src/link/af_spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ mod inet6_iface_flag;
mod inet6_stats;
mod unspec;

pub use self::bridge::{AfSpecBridge, BridgeVlanInfo};
pub use self::bridge::{
AfSpecBridge, BridgeFlag, BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags,
BridgeVlanTunnelInfo,
};
pub use self::inet::{AfSpecInet, InetDevConf};
pub use self::inet6::AfSpecInet6;
pub use self::inet6_cache::{Inet6CacheInfo, Inet6CacheInfoBuffer};
Expand Down
3 changes: 2 additions & 1 deletion src/link/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ mod xdp;
mod tests;

pub use self::af_spec::{
AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeVlanInfo,
AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeFlag,
BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags, BridgeVlanTunnelInfo,
Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer,
Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlags, Inet6Stats,
Inet6StatsBuffer, InetDevConf,
Expand Down
Loading
Loading