Skip to content

Commit 40d090b

Browse files
committed
bridge port: Introduce BridgeFlag, BridgeMode and BridgeVlanTunnelInfo
* Change `AfSpecBridge::Flags(u16)` to `AfSpecBridge::Flags(BridgeFlag)` * Add `AfSpecBridge::Mode(BridgeMode)` for `IFLA_BRIDGE_MODE` * Add `AfSpecBridge::VlanTunnelInfo(Vec<BridgeVlanTunnelInfo>)` for `IFLA_BRIDGE_VLAN_TUNNEL_INFO` Unit test cases included. Signed-off-by: Gris Ge <[email protected]>
1 parent c91b2e9 commit 40d090b

File tree

4 files changed

+341
-45
lines changed

4 files changed

+341
-45
lines changed

src/link/af_spec/bridge.rs

Lines changed: 251 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,98 @@
33
use anyhow::Context;
44
use byteorder::{ByteOrder, NativeEndian};
55
use netlink_packet_utils::{
6-
nla::{self, DefaultNla, NlaBuffer},
7-
parsers::parse_u16,
6+
nla::{DefaultNla, Nla, NlaBuffer, NlasIterator},
7+
parsers::{parse_u16, parse_u32},
88
traits::Parseable,
9-
DecodeError,
9+
DecodeError, Emitable,
1010
};
1111

1212
const IFLA_BRIDGE_FLAGS: u16 = 0;
13+
const IFLA_BRIDGE_MODE: u16 = 1;
1314
const IFLA_BRIDGE_VLAN_INFO: u16 = 2;
15+
const IFLA_BRIDGE_VLAN_TUNNEL_INFO: u16 = 3;
16+
// const IFLA_BRIDGE_MRP: u16 = 4;
17+
// const IFLA_BRIDGE_CFM: u16 = 5;
18+
// const IFLA_BRIDGE_MST: u16 = 6;
1419

1520
#[derive(Clone, Eq, PartialEq, Debug)]
1621
#[non_exhaustive]
1722
pub enum AfSpecBridge {
18-
Flags(u16),
23+
Flags(BridgeFlag),
24+
Mode(BridgeMode),
1925
VlanInfo(BridgeVlanInfo),
26+
VlanTunnelInfo(Vec<BridgeVlanTunnelInfo>),
2027
Other(DefaultNla),
2128
}
2229

23-
impl nla::Nla for AfSpecBridge {
30+
impl Nla for AfSpecBridge {
2431
fn value_len(&self) -> usize {
25-
use self::AfSpecBridge::*;
26-
match *self {
27-
VlanInfo(_) => 4,
28-
Flags(_) => 2,
29-
Other(ref nla) => nla.value_len(),
32+
match self {
33+
Self::Flags(_) => BridgeFlag::LENGTH,
34+
Self::Mode(_) => BridgeMode::LENGTH,
35+
Self::VlanInfo(_) => BridgeVlanInfo::LENGTH,
36+
Self::VlanTunnelInfo(s) => s.as_slice().buffer_len(),
37+
Self::Other(nla) => nla.value_len(),
3038
}
3139
}
3240

3341
fn emit_value(&self, buffer: &mut [u8]) {
34-
use self::AfSpecBridge::*;
35-
match *self {
36-
Flags(value) => NativeEndian::write_u16(buffer, value),
37-
VlanInfo(ref info) => {
42+
match self {
43+
Self::Flags(value) => {
44+
NativeEndian::write_u16(buffer, u16::from(*value))
45+
}
46+
Self::Mode(value) => {
47+
NativeEndian::write_u16(buffer, u16::from(*value))
48+
}
49+
Self::VlanInfo(info) => {
3850
buffer[..4].copy_from_slice(<[u8; 4]>::from(info).as_slice())
3951
}
40-
Other(ref nla) => nla.emit_value(buffer),
52+
Self::VlanTunnelInfo(s) => s.as_slice().emit(buffer),
53+
Self::Other(nla) => nla.emit_value(buffer),
4154
}
4255
}
4356

4457
fn kind(&self) -> u16 {
45-
use self::AfSpecBridge::*;
46-
match *self {
47-
Flags(_) => IFLA_BRIDGE_FLAGS,
48-
VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO,
49-
Other(ref nla) => nla.kind(),
58+
match self {
59+
Self::Flags(_) => IFLA_BRIDGE_FLAGS,
60+
Self::Mode(_) => IFLA_BRIDGE_MODE,
61+
Self::VlanInfo(_) => IFLA_BRIDGE_VLAN_INFO,
62+
Self::VlanTunnelInfo(_) => IFLA_BRIDGE_VLAN_TUNNEL_INFO,
63+
Self::Other(nla) => nla.kind(),
5064
}
5165
}
5266
}
5367

5468
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for AfSpecBridge {
5569
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
56-
use self::AfSpecBridge::*;
57-
5870
let payload = buf.value();
5971
Ok(match buf.kind() {
60-
IFLA_BRIDGE_VLAN_INFO => VlanInfo(
61-
BridgeVlanInfo::try_from(payload)
62-
.context("Invalid IFLA_BRIDGE_VLAN_INFO value")?,
72+
IFLA_BRIDGE_FLAGS => Self::Flags(
73+
parse_u16(payload)
74+
.context("Invalid IFLA_BRIDGE_FLAGS value")?
75+
.into(),
6376
),
64-
IFLA_BRIDGE_FLAGS => Flags(
77+
IFLA_BRIDGE_MODE => Self::Mode(
6578
parse_u16(payload)
66-
.context("invalid IFLA_BRIDGE_FLAGS value")?,
79+
.context("Invalid IFLA_BRIDGE_MODE value")?
80+
.into(),
6781
),
68-
kind => Other(
82+
IFLA_BRIDGE_VLAN_INFO => Self::VlanInfo(
83+
BridgeVlanInfo::try_from(payload)
84+
.context("Invalid IFLA_BRIDGE_VLAN_INFO value")?,
85+
),
86+
IFLA_BRIDGE_VLAN_TUNNEL_INFO => {
87+
let mut nlas = Vec::new();
88+
for nla in NlasIterator::new(payload) {
89+
let nla = &nla.context(format!(
90+
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_INFO for {payload:?}"
91+
))?;
92+
let parsed = BridgeVlanTunnelInfo::parse(nla)?;
93+
nlas.push(parsed);
94+
}
95+
Self::VlanTunnelInfo(nlas)
96+
}
97+
kind => Self::Other(
6998
DefaultNla::parse(buf)
7099
.context(format!("Unknown NLA type {kind}"))?,
71100
),
@@ -83,27 +112,62 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
83112
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
84113
let mut nlas = vec![];
85114
let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_BRIDGE)";
86-
for nla in
87-
netlink_packet_utils::nla::NlasIterator::new(buf.into_inner())
88-
{
115+
for nla in NlasIterator::new(buf.into_inner()) {
89116
let nla = nla.context(err)?;
90117
nlas.push(AfSpecBridge::parse(&nla).context(err)?);
91118
}
92119
Ok(Self(nlas))
93120
}
94121
}
95122

96-
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
97-
#[non_exhaustive]
123+
const BRIDGE_VLAN_INFO_CONTROLLER: u16 = 1 << 0;
124+
const BRIDGE_VLAN_INFO_PVID: u16 = 1 << 1;
125+
const BRIDGE_VLAN_INFO_UNTAGGED: u16 = 1 << 2;
126+
const BRIDGE_VLAN_INFO_RANGE_BEGIN: u16 = 1 << 3;
127+
const BRIDGE_VLAN_INFO_RANGE_END: u16 = 1 << 4;
128+
const BRIDGE_VLAN_INFO_BRENTRY: u16 = 1 << 5;
129+
const BRIDGE_VLAN_INFO_ONLY_OPTS: u16 = 1 << 6;
130+
131+
bitflags! {
132+
#[non_exhaustive]
133+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
134+
pub struct BridgeVlanInfoFlags: u16 {
135+
/// Operate on Bridge device as well
136+
const Controller = BRIDGE_VLAN_INFO_CONTROLLER;
137+
/// VLAN is PVID, ingress untagged
138+
const Pvid = BRIDGE_VLAN_INFO_PVID;
139+
/// VLAN egresses untagged
140+
const Untagged= BRIDGE_VLAN_INFO_UNTAGGED;
141+
/// VLAN is start of vlan range
142+
const RangeBegin = BRIDGE_VLAN_INFO_RANGE_BEGIN;
143+
/// VLAN is end of vlan range
144+
const RangeEnd = BRIDGE_VLAN_INFO_RANGE_END;
145+
/// Global bridge VLAN entry
146+
const Brentry = BRIDGE_VLAN_INFO_BRENTRY;
147+
/// Skip create/delete/flags
148+
const OnlyOpts= BRIDGE_VLAN_INFO_ONLY_OPTS;
149+
const _ = !0;
150+
}
151+
}
152+
153+
impl BridgeVlanInfoFlags {
154+
pub const LENGTH: usize = 2;
155+
}
156+
157+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
98158
pub struct BridgeVlanInfo {
99-
pub flags: u16,
159+
pub flags: BridgeVlanInfoFlags,
100160
pub vid: u16,
101161
}
102162

163+
impl BridgeVlanInfo {
164+
pub const LENGTH: usize = 4;
165+
}
166+
103167
impl From<&BridgeVlanInfo> for [u8; 4] {
104168
fn from(d: &BridgeVlanInfo) -> Self {
105169
let mut ret = [0u8; 4];
106-
NativeEndian::write_u16(&mut ret[0..2], d.flags);
170+
NativeEndian::write_u16(&mut ret[0..2], d.flags.bits());
107171
NativeEndian::write_u16(&mut ret[2..4], d.vid);
108172
ret
109173
}
@@ -114,9 +178,11 @@ impl TryFrom<&[u8]> for BridgeVlanInfo {
114178
fn try_from(raw: &[u8]) -> Result<Self, DecodeError> {
115179
if raw.len() == 4 {
116180
Ok(Self {
117-
flags: parse_u16(&raw[0..2]).context(format!(
118-
"Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}"
119-
))?,
181+
flags: BridgeVlanInfoFlags::from_bits_retain(
182+
parse_u16(&raw[0..2]).context(format!(
183+
"Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}"
184+
))?,
185+
),
120186
vid: parse_u16(&raw[2..4]).context(format!(
121187
"Invalid IFLA_BRIDGE_VLAN_INFO value: {raw:?}"
122188
))?,
@@ -129,3 +195,150 @@ impl TryFrom<&[u8]> for BridgeVlanInfo {
129195
}
130196
}
131197
}
198+
199+
// kernel constant name is BRIDGE_FLAGS_MASTER
200+
const BRIDGE_FLAGS_CONTROLLER: u16 = 1;
201+
const BRIDGE_FLAGS_SELF: u16 = 2;
202+
203+
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
204+
#[non_exhaustive]
205+
pub enum BridgeFlag {
206+
/// Bridge command to/from controller
207+
Controller,
208+
/// Bridge command to/from lowerdev
209+
LowerDev,
210+
Other(u16),
211+
}
212+
213+
impl From<u16> for BridgeFlag {
214+
fn from(d: u16) -> Self {
215+
match d {
216+
BRIDGE_FLAGS_CONTROLLER => Self::Controller,
217+
BRIDGE_FLAGS_SELF => Self::LowerDev,
218+
_ => Self::Other(d),
219+
}
220+
}
221+
}
222+
223+
impl From<BridgeFlag> for u16 {
224+
fn from(v: BridgeFlag) -> u16 {
225+
match v {
226+
BridgeFlag::Controller => BRIDGE_FLAGS_CONTROLLER,
227+
BridgeFlag::LowerDev => BRIDGE_FLAGS_SELF,
228+
BridgeFlag::Other(d) => d,
229+
}
230+
}
231+
}
232+
233+
impl BridgeFlag {
234+
pub const LENGTH: usize = 2;
235+
}
236+
237+
const BRIDGE_MODE_VEB: u16 = 0;
238+
const BRIDGE_MODE_VEPA: u16 = 1;
239+
240+
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
241+
#[non_exhaustive]
242+
pub enum BridgeMode {
243+
/// Default loopback mode
244+
Veb,
245+
/// 802.1Qbg defined VEPA mode
246+
Vepa,
247+
Other(u16),
248+
}
249+
250+
impl From<u16> for BridgeMode {
251+
fn from(d: u16) -> Self {
252+
match d {
253+
BRIDGE_MODE_VEB => Self::Veb,
254+
BRIDGE_MODE_VEPA => Self::Vepa,
255+
_ => Self::Other(d),
256+
}
257+
}
258+
}
259+
260+
impl From<BridgeMode> for u16 {
261+
fn from(v: BridgeMode) -> u16 {
262+
match v {
263+
BridgeMode::Veb => BRIDGE_MODE_VEB,
264+
BridgeMode::Vepa => BRIDGE_MODE_VEPA,
265+
BridgeMode::Other(d) => d,
266+
}
267+
}
268+
}
269+
270+
impl BridgeMode {
271+
pub const LENGTH: usize = 2;
272+
}
273+
274+
const IFLA_BRIDGE_VLAN_TUNNEL_ID: u16 = 1;
275+
const IFLA_BRIDGE_VLAN_TUNNEL_VID: u16 = 2;
276+
const IFLA_BRIDGE_VLAN_TUNNEL_FLAGS: u16 = 3;
277+
278+
#[derive(Clone, Eq, PartialEq, Debug)]
279+
#[non_exhaustive]
280+
pub enum BridgeVlanTunnelInfo {
281+
Id(u32),
282+
Vid(u16),
283+
Flags(BridgeVlanInfoFlags),
284+
Other(DefaultNla),
285+
}
286+
287+
impl Nla for BridgeVlanTunnelInfo {
288+
fn value_len(&self) -> usize {
289+
match self {
290+
Self::Id(_) => 4,
291+
Self::Vid(_) => 2,
292+
Self::Flags(_) => BridgeVlanInfoFlags::LENGTH,
293+
Self::Other(nla) => nla.value_len(),
294+
}
295+
}
296+
297+
fn emit_value(&self, buffer: &mut [u8]) {
298+
match self {
299+
Self::Id(v) => NativeEndian::write_u32(buffer, *v),
300+
Self::Vid(v) => NativeEndian::write_u16(buffer, *v),
301+
Self::Flags(value) => NativeEndian::write_u16(buffer, value.bits()),
302+
Self::Other(nla) => nla.emit_value(buffer),
303+
}
304+
}
305+
306+
fn kind(&self) -> u16 {
307+
match self {
308+
Self::Id(_) => IFLA_BRIDGE_VLAN_TUNNEL_ID,
309+
Self::Vid(_) => IFLA_BRIDGE_VLAN_TUNNEL_VID,
310+
Self::Flags(_) => IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
311+
Self::Other(nla) => nla.kind(),
312+
}
313+
}
314+
}
315+
316+
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
317+
for BridgeVlanTunnelInfo
318+
{
319+
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
320+
let payload = buf.value();
321+
Ok(match buf.kind() {
322+
IFLA_BRIDGE_VLAN_TUNNEL_ID => {
323+
Self::Id(parse_u32(payload).context(format!(
324+
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_ID {payload:?}"
325+
))?)
326+
}
327+
IFLA_BRIDGE_VLAN_TUNNEL_VID => {
328+
Self::Vid(parse_u16(payload).context(format!(
329+
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}"
330+
))?)
331+
}
332+
IFLA_BRIDGE_VLAN_TUNNEL_FLAGS => {
333+
Self::Flags(BridgeVlanInfoFlags::from_bits_retain(
334+
parse_u16(payload).context(format!(
335+
"Invalid IFLA_BRIDGE_VLAN_TUNNEL_VID {payload:?}"
336+
))?,
337+
))
338+
}
339+
_ => {
340+
Self::Other(DefaultNla::parse(buf).context("Unknown NLA type")?)
341+
}
342+
})
343+
}
344+
}

src/link/af_spec/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ mod inet6_iface_flag;
1010
mod inet6_stats;
1111
mod unspec;
1212

13-
pub use self::bridge::{AfSpecBridge, BridgeVlanInfo};
13+
pub use self::bridge::{
14+
AfSpecBridge, BridgeFlag, BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags,
15+
BridgeVlanTunnelInfo,
16+
};
1417
pub use self::inet::{AfSpecInet, InetDevConf};
1518
pub use self::inet6::AfSpecInet6;
1619
pub use self::inet6_cache::{Inet6CacheInfo, Inet6CacheInfoBuffer};

src/link/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ mod xdp;
2626
mod tests;
2727

2828
pub use self::af_spec::{
29-
AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeVlanInfo,
29+
AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeFlag,
30+
BridgeMode, BridgeVlanInfo, BridgeVlanInfoFlags, BridgeVlanTunnelInfo,
3031
Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer,
3132
Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlags, Inet6Stats,
3233
Inet6StatsBuffer, InetDevConf,

0 commit comments

Comments
 (0)