use super::{
attr::AttrIter,
builder::MessageBuilder,
connection::Connection,
error::{Error, Result},
interface_ref::InterfaceRef,
message::{MessageIter, NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST, NlMsgType},
protocol::Route,
types::link::{
BridgeVlanInfo, IfInfoMsg, IflaAttr, bridge_af, bridge_vlan_flags, bridge_vlan_tunnel,
rtext_filter,
},
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct BridgeVlanFlags {
pub pvid: bool,
pub untagged: bool,
}
impl BridgeVlanFlags {
pub fn from_raw(flags: u16) -> Self {
Self {
pvid: flags & bridge_vlan_flags::PVID != 0,
untagged: flags & bridge_vlan_flags::UNTAGGED != 0,
}
}
}
#[derive(Debug, Clone)]
pub struct BridgeVlanEntry {
pub ifindex: u32,
pub vid: u16,
pub flags: BridgeVlanFlags,
}
impl BridgeVlanEntry {
pub fn is_pvid(&self) -> bool {
self.flags.pvid
}
pub fn is_untagged(&self) -> bool {
self.flags.untagged
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BridgeVlanTunnelEntry {
pub ifindex: u32,
pub vid: u16,
pub tunnel_id: u32,
}
#[derive(Debug, Clone, Default)]
#[must_use = "builders do nothing unless used"]
pub struct BridgeVlanTunnelBuilder {
dev: Option<InterfaceRef>,
vid: u16,
vid_end: Option<u16>,
tunnel_id: u32,
}
impl BridgeVlanTunnelBuilder {
pub const MAX_TUNNEL_ID: u32 = (1 << 24) - 1;
pub fn new(vid: u16, tunnel_id: u32) -> Self {
Self {
vid,
tunnel_id,
..Default::default()
}
}
pub fn dev(mut self, dev: impl Into<String>) -> Self {
self.dev = Some(InterfaceRef::Name(dev.into()));
self
}
pub fn ifindex(mut self, ifindex: u32) -> Self {
self.dev = Some(InterfaceRef::Index(ifindex));
self
}
pub fn device_ref(&self) -> Option<&InterfaceRef> {
self.dev.as_ref()
}
pub fn range(mut self, vid_end: u16) -> Self {
self.vid_end = Some(vid_end);
self
}
pub(crate) fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
self.write_message(builder, NlMsgType::RTM_SETLINK, ifindex)
}
pub(crate) fn write_del(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
self.write_message(builder, NlMsgType::RTM_DELLINK, ifindex)
}
fn write_message(
&self,
builder: &mut MessageBuilder,
_msg_type: u16,
ifindex: u32,
) -> Result<()> {
if self.tunnel_id > Self::MAX_TUNNEL_ID {
return Err(Error::InvalidMessage(format!(
"tunnel_id {} exceeds maximum {}",
self.tunnel_id,
Self::MAX_TUNNEL_ID
)));
}
let ifinfo = IfInfoMsg::new()
.with_family(libc::AF_BRIDGE as u8)
.with_index(ifindex as i32);
builder.append(&ifinfo);
let af_spec = builder.nest_start(IflaAttr::AfSpec as u16);
if let Some(vid_end) = self.vid_end {
self.add_tunnel_entry(
builder,
self.vid,
self.tunnel_id,
bridge_vlan_flags::RANGE_BEGIN,
);
let tunnel_id_end = self.tunnel_id + (vid_end - self.vid) as u32;
if tunnel_id_end > Self::MAX_TUNNEL_ID {
return Err(Error::InvalidMessage(format!(
"tunnel_id range end {} exceeds maximum {}",
tunnel_id_end,
Self::MAX_TUNNEL_ID
)));
}
self.add_tunnel_entry(
builder,
vid_end,
tunnel_id_end,
bridge_vlan_flags::RANGE_END,
);
} else {
self.add_tunnel_entry(builder, self.vid, self.tunnel_id, 0);
}
builder.nest_end(af_spec);
Ok(())
}
fn add_tunnel_entry(&self, builder: &mut MessageBuilder, vid: u16, tunnel_id: u32, flags: u16) {
let tunnel_info = builder.nest_start(bridge_af::IFLA_BRIDGE_VLAN_TUNNEL_INFO);
builder.append_attr_u32(bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_ID, tunnel_id);
builder.append_attr_u16(bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_VID, vid);
if flags != 0 {
builder.append_attr_u16(bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags);
}
builder.nest_end(tunnel_info);
}
}
#[derive(Debug, Clone, Default)]
#[must_use = "builders do nothing unless used"]
pub struct BridgeVlanBuilder {
dev: Option<InterfaceRef>,
vid: u16,
vid_end: Option<u16>,
pvid: bool,
untagged: bool,
master: bool,
}
impl BridgeVlanBuilder {
pub fn new(vid: u16) -> Self {
Self {
vid,
..Default::default()
}
}
pub fn dev(mut self, dev: impl Into<String>) -> Self {
self.dev = Some(InterfaceRef::Name(dev.into()));
self
}
pub fn ifindex(mut self, ifindex: u32) -> Self {
self.dev = Some(InterfaceRef::Index(ifindex));
self
}
pub fn device_ref(&self) -> Option<&InterfaceRef> {
self.dev.as_ref()
}
pub fn range(mut self, vid_end: u16) -> Self {
self.vid_end = Some(vid_end);
self
}
pub fn pvid(mut self) -> Self {
self.pvid = true;
self
}
pub fn untagged(mut self) -> Self {
self.untagged = true;
self
}
pub fn master(mut self) -> Self {
self.master = true;
self
}
fn build_flags(&self) -> u16 {
let mut flags = 0u16;
if self.pvid {
flags |= bridge_vlan_flags::PVID;
}
if self.untagged {
flags |= bridge_vlan_flags::UNTAGGED;
}
if self.master {
flags |= bridge_vlan_flags::MASTER;
}
flags
}
pub(crate) fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) {
self.write_message(builder, ifindex);
}
pub(crate) fn write_del(&self, builder: &mut MessageBuilder, ifindex: u32) {
self.write_message(builder, ifindex);
}
fn write_message(&self, builder: &mut MessageBuilder, ifindex: u32) {
let ifinfo = IfInfoMsg::new()
.with_family(libc::AF_BRIDGE as u8)
.with_index(ifindex as i32);
builder.append(&ifinfo);
let af_spec = builder.nest_start(IflaAttr::AfSpec as u16);
if let Some(vid_end) = self.vid_end {
let mut begin_flags = self.build_flags();
begin_flags |= bridge_vlan_flags::RANGE_BEGIN;
let vlan_begin = BridgeVlanInfo::new(self.vid).with_flags(begin_flags);
builder.append_attr(bridge_af::IFLA_BRIDGE_VLAN_INFO, vlan_begin.as_bytes());
let mut end_flags = self.build_flags();
end_flags |= bridge_vlan_flags::RANGE_END;
let vlan_end = BridgeVlanInfo::new(vid_end).with_flags(end_flags);
builder.append_attr(bridge_af::IFLA_BRIDGE_VLAN_INFO, vlan_end.as_bytes());
} else {
let flags = self.build_flags();
let vlan_info = BridgeVlanInfo::new(self.vid).with_flags(flags);
builder.append_attr(bridge_af::IFLA_BRIDGE_VLAN_INFO, vlan_info.as_bytes());
}
builder.nest_end(af_spec);
}
}
impl Connection<Route> {
async fn resolve_bridge_vlan_interface(&self, config: &BridgeVlanBuilder) -> Result<u32> {
match config.device_ref() {
Some(iface) => self.resolve_interface(iface).await,
None => Err(Error::InvalidMessage(
"device name or ifindex required".into(),
)),
}
}
async fn resolve_bridge_vlan_tunnel_interface(
&self,
config: &BridgeVlanTunnelBuilder,
) -> Result<u32> {
match config.device_ref() {
Some(iface) => self.resolve_interface(iface).await,
None => Err(Error::InvalidMessage(
"device name or ifindex required".into(),
)),
}
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "get_bridge_vlans"))]
pub async fn get_bridge_vlans(
&self,
dev: impl Into<InterfaceRef>,
) -> Result<Vec<BridgeVlanEntry>> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.get_bridge_vlans_by_index(ifindex).await
}
#[tracing::instrument(
level = "debug",
skip_all,
fields(method = "get_bridge_vlans_by_index")
)]
pub async fn get_bridge_vlans_by_index(&self, ifindex: u32) -> Result<Vec<BridgeVlanEntry>> {
let mut builder = MessageBuilder::new(NlMsgType::RTM_GETLINK, NLM_F_REQUEST);
let ifinfo = IfInfoMsg::new()
.with_family(libc::AF_BRIDGE as u8)
.with_index(ifindex as i32);
builder.append(&ifinfo);
builder.append_attr_u32(IflaAttr::ExtMask as u16, rtext_filter::BRVLAN);
let response = self.send_request(builder).await?;
parse_vlan_entries(&response, ifindex)
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "get_bridge_vlans_all"))]
pub async fn get_bridge_vlans_all(
&self,
bridge: impl Into<InterfaceRef>,
) -> Result<Vec<BridgeVlanEntry>> {
let bridge_idx = self.resolve_interface(&bridge.into()).await?;
self.get_bridge_vlans_all_by_index(bridge_idx).await
}
#[tracing::instrument(
level = "debug",
skip_all,
fields(method = "get_bridge_vlans_all_by_index")
)]
pub async fn get_bridge_vlans_all_by_index(
&self,
bridge_idx: u32,
) -> Result<Vec<BridgeVlanEntry>> {
let mut builder = MessageBuilder::new(NlMsgType::RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP);
let ifinfo = IfInfoMsg::new().with_family(libc::AF_BRIDGE as u8);
builder.append(&ifinfo);
builder.append_attr_u32(IflaAttr::ExtMask as u16, rtext_filter::BRVLAN);
let responses = self.send_dump(builder).await?;
let mut entries = Vec::new();
for response in responses {
if let Ok(msg_entries) = parse_vlan_entries_from_dump(&response) {
for entry in msg_entries {
if entry.ifindex == bridge_idx {
entries.push(entry);
}
}
}
}
let links = self.get_links().await?;
let port_indices: Vec<u32> = links
.iter()
.filter(|l| l.master == Some(bridge_idx))
.map(|l| l.ifindex())
.collect();
let mut filtered = Vec::new();
for entry in entries {
if entry.ifindex == bridge_idx || port_indices.contains(&entry.ifindex) {
filtered.push(entry);
}
}
Ok(filtered)
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_bridge_vlan"))]
pub async fn add_bridge_vlan(&self, config: BridgeVlanBuilder) -> Result<()> {
let ifindex = self.resolve_bridge_vlan_interface(&config).await?;
let mut builder = MessageBuilder::new(NlMsgType::RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_add(&mut builder, ifindex);
self.send_ack(builder)
.await
.map_err(|e| e.with_context("add_bridge_vlan"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_bridge_vlan"))]
pub async fn del_bridge_vlan(&self, dev: impl Into<InterfaceRef>, vid: u16) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.del_bridge_vlan_by_index(ifindex, vid).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_bridge_vlan_by_index"))]
pub async fn del_bridge_vlan_by_index(&self, ifindex: u32, vid: u16) -> Result<()> {
let config = BridgeVlanBuilder::new(vid).ifindex(ifindex);
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_del(&mut builder, ifindex);
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_bridge_vlan"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_bridge_vlan_range"))]
pub async fn del_bridge_vlan_range(
&self,
dev: impl Into<InterfaceRef>,
vid_start: u16,
vid_end: u16,
) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
let config = BridgeVlanBuilder::new(vid_start)
.ifindex(ifindex)
.range(vid_end);
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_del(&mut builder, ifindex);
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_bridge_vlan_range"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "set_bridge_pvid"))]
pub async fn set_bridge_pvid(&self, dev: impl Into<InterfaceRef>, vid: u16) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.add_bridge_vlan(
BridgeVlanBuilder::new(vid)
.ifindex(ifindex)
.pvid()
.untagged(),
)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "set_bridge_pvid_by_index"))]
pub async fn set_bridge_pvid_by_index(&self, ifindex: u32, vid: u16) -> Result<()> {
self.add_bridge_vlan(
BridgeVlanBuilder::new(vid)
.ifindex(ifindex)
.pvid()
.untagged(),
)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_bridge_vlan_tagged"))]
pub async fn add_bridge_vlan_tagged(
&self,
dev: impl Into<InterfaceRef>,
vid: u16,
) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.add_bridge_vlan(BridgeVlanBuilder::new(vid).ifindex(ifindex))
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_bridge_vlan_range"))]
pub async fn add_bridge_vlan_range(
&self,
dev: impl Into<InterfaceRef>,
vid_start: u16,
vid_end: u16,
) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.add_bridge_vlan(
BridgeVlanBuilder::new(vid_start)
.ifindex(ifindex)
.range(vid_end),
)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "get_vlan_tunnels"))]
pub async fn get_vlan_tunnels(
&self,
dev: impl Into<InterfaceRef>,
) -> Result<Vec<BridgeVlanTunnelEntry>> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.get_vlan_tunnels_by_index(ifindex).await
}
#[tracing::instrument(
level = "debug",
skip_all,
fields(method = "get_vlan_tunnels_by_index")
)]
pub async fn get_vlan_tunnels_by_index(
&self,
ifindex: u32,
) -> Result<Vec<BridgeVlanTunnelEntry>> {
let mut builder = MessageBuilder::new(NlMsgType::RTM_GETLINK, NLM_F_REQUEST);
let ifinfo = IfInfoMsg::new()
.with_family(libc::AF_BRIDGE as u8)
.with_index(ifindex as i32);
builder.append(&ifinfo);
builder.append_attr_u32(IflaAttr::ExtMask as u16, rtext_filter::BRVLAN);
let response = self.send_request(builder).await?;
parse_tunnel_entries(&response, ifindex)
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_vlan_tunnel"))]
pub async fn add_vlan_tunnel(&self, config: BridgeVlanTunnelBuilder) -> Result<()> {
let ifindex = self.resolve_bridge_vlan_tunnel_interface(&config).await?;
let mut builder = MessageBuilder::new(NlMsgType::RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_add(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("add_vlan_tunnel"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_vlan_tunnel"))]
pub async fn del_vlan_tunnel(&self, dev: impl Into<InterfaceRef>, vid: u16) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
self.del_vlan_tunnel_by_index(ifindex, vid).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_vlan_tunnel_by_index"))]
pub async fn del_vlan_tunnel_by_index(&self, ifindex: u32, vid: u16) -> Result<()> {
let config = BridgeVlanTunnelBuilder::new(vid, 0).ifindex(ifindex);
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_del(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_vlan_tunnel"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_vlan_tunnel_range"))]
pub async fn del_vlan_tunnel_range(
&self,
dev: impl Into<InterfaceRef>,
vid_start: u16,
vid_end: u16,
) -> Result<()> {
let ifindex = self.resolve_interface(&dev.into()).await?;
let config = BridgeVlanTunnelBuilder::new(vid_start, 0)
.ifindex(ifindex)
.range(vid_end);
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
config.write_del(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_vlan_tunnel_range"))
}
}
fn parse_vlan_entries(data: &[u8], ifindex: u32) -> Result<Vec<BridgeVlanEntry>> {
let mut entries = Vec::new();
if data.len() < IfInfoMsg::SIZE {
return Ok(entries);
}
let attrs_data = &data[IfInfoMsg::SIZE..];
for (attr_type, payload) in AttrIter::new(attrs_data) {
if attr_type == IflaAttr::AfSpec as u16 {
parse_af_spec_vlans(payload, ifindex, &mut entries);
}
}
Ok(entries)
}
fn parse_vlan_entries_from_dump(data: &[u8]) -> Result<Vec<BridgeVlanEntry>> {
let mut entries = Vec::new();
for msg_result in MessageIter::new(data) {
let (_header, payload) = match msg_result {
Ok(msg) => msg,
Err(_) => continue,
};
if payload.len() < IfInfoMsg::SIZE {
continue;
}
if let Ok(ifinfo) = IfInfoMsg::from_bytes(payload) {
let ifindex = ifinfo.ifi_index as u32;
let attrs_data = &payload[IfInfoMsg::SIZE..];
for (attr_type, attr_payload) in AttrIter::new(attrs_data) {
if attr_type == IflaAttr::AfSpec as u16 {
parse_af_spec_vlans(attr_payload, ifindex, &mut entries);
}
}
}
}
Ok(entries)
}
fn parse_af_spec_vlans(data: &[u8], ifindex: u32, entries: &mut Vec<BridgeVlanEntry>) {
let mut range_start: Option<(u16, u16)> = None;
for (attr_type, payload) in AttrIter::new(data) {
if attr_type == bridge_af::IFLA_BRIDGE_VLAN_INFO
&& let Some(vlan_info) = BridgeVlanInfo::from_bytes(payload)
{
let flags = BridgeVlanFlags::from_raw(vlan_info.flags);
if vlan_info.is_range_begin() {
range_start = Some((vlan_info.vid, vlan_info.flags));
} else if vlan_info.is_range_end() {
if let Some((start_vid, start_flags)) = range_start.take() {
let range_flags = BridgeVlanFlags::from_raw(start_flags);
for vid in start_vid..=vlan_info.vid {
entries.push(BridgeVlanEntry {
ifindex,
vid,
flags: range_flags,
});
}
}
} else {
entries.push(BridgeVlanEntry {
ifindex,
vid: vlan_info.vid,
flags,
});
}
}
}
}
fn parse_tunnel_entries(data: &[u8], ifindex: u32) -> Result<Vec<BridgeVlanTunnelEntry>> {
let mut entries = Vec::new();
if data.len() < IfInfoMsg::SIZE {
return Ok(entries);
}
let attrs_data = &data[IfInfoMsg::SIZE..];
for (attr_type, payload) in AttrIter::new(attrs_data) {
if attr_type == IflaAttr::AfSpec as u16 {
parse_af_spec_tunnels(payload, ifindex, &mut entries);
}
}
Ok(entries)
}
fn parse_af_spec_tunnels(data: &[u8], ifindex: u32, entries: &mut Vec<BridgeVlanTunnelEntry>) {
let mut range_start: Option<(u16, u32)> = None;
for (attr_type, payload) in AttrIter::new(data) {
if attr_type == bridge_af::IFLA_BRIDGE_VLAN_TUNNEL_INFO {
let mut vid: Option<u16> = None;
let mut tunnel_id: Option<u32> = None;
let mut flags: u16 = 0;
for (tunnel_attr, tunnel_payload) in AttrIter::new(payload) {
match tunnel_attr {
t if t == bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_ID
&& tunnel_payload.len() >= 4 =>
{
tunnel_id =
Some(u32::from_ne_bytes(tunnel_payload[..4].try_into().unwrap()));
}
t if t == bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_VID
&& tunnel_payload.len() >= 2 =>
{
vid = Some(u16::from_ne_bytes(tunnel_payload[..2].try_into().unwrap()));
}
t if t == bridge_vlan_tunnel::IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
&& tunnel_payload.len() >= 2 =>
{
flags = u16::from_ne_bytes(tunnel_payload[..2].try_into().unwrap());
}
_ => {}
}
}
if let (Some(v), Some(t)) = (vid, tunnel_id) {
let is_range_begin = flags & bridge_vlan_flags::RANGE_BEGIN != 0;
let is_range_end = flags & bridge_vlan_flags::RANGE_END != 0;
if is_range_begin {
range_start = Some((v, t));
} else if is_range_end {
if let Some((start_vid, start_tunnel_id)) = range_start.take() {
for i in 0..=(v - start_vid) {
entries.push(BridgeVlanTunnelEntry {
ifindex,
vid: start_vid + i,
tunnel_id: start_tunnel_id + i as u32,
});
}
}
} else {
entries.push(BridgeVlanTunnelEntry {
ifindex,
vid: v,
tunnel_id: t,
});
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_vlan_flags_from_raw() {
let flags =
BridgeVlanFlags::from_raw(bridge_vlan_flags::PVID | bridge_vlan_flags::UNTAGGED);
assert!(flags.pvid);
assert!(flags.untagged);
let flags = BridgeVlanFlags::from_raw(0);
assert!(!flags.pvid);
assert!(!flags.untagged);
let flags = BridgeVlanFlags::from_raw(bridge_vlan_flags::PVID);
assert!(flags.pvid);
assert!(!flags.untagged);
}
#[test]
fn test_bridge_vlan_entry_helpers() {
let entry = BridgeVlanEntry {
ifindex: 1,
vid: 100,
flags: BridgeVlanFlags {
pvid: true,
untagged: true,
},
};
assert!(entry.is_pvid());
assert!(entry.is_untagged());
let entry = BridgeVlanEntry {
ifindex: 1,
vid: 200,
flags: BridgeVlanFlags::default(),
};
assert!(!entry.is_pvid());
assert!(!entry.is_untagged());
}
#[test]
fn test_builder_default() {
let builder = BridgeVlanBuilder::new(100);
assert_eq!(builder.vid, 100);
assert!(!builder.pvid);
assert!(!builder.untagged);
assert!(!builder.master);
assert!(builder.vid_end.is_none());
}
#[test]
fn test_builder_chain() {
let builder = BridgeVlanBuilder::new(100)
.dev("eth0")
.pvid()
.untagged()
.master();
assert_eq!(builder.dev, Some(InterfaceRef::Name("eth0".to_string())));
assert!(builder.pvid);
assert!(builder.untagged);
assert!(builder.master);
}
#[test]
fn test_builder_range() {
let builder = BridgeVlanBuilder::new(100).dev("eth0").range(110);
assert_eq!(builder.vid, 100);
assert_eq!(builder.vid_end, Some(110));
}
#[test]
fn test_builder_ifindex() {
let builder = BridgeVlanBuilder::new(100).ifindex(5);
assert_eq!(builder.dev, Some(InterfaceRef::Index(5)));
}
#[test]
fn test_build_flags() {
let builder = BridgeVlanBuilder::new(100).pvid().untagged();
let flags = builder.build_flags();
assert_eq!(flags, bridge_vlan_flags::PVID | bridge_vlan_flags::UNTAGGED);
let builder = BridgeVlanBuilder::new(100).master();
let flags = builder.build_flags();
assert_eq!(flags, bridge_vlan_flags::MASTER);
}
#[test]
fn test_tunnel_builder_new() {
let builder = BridgeVlanTunnelBuilder::new(100, 10000);
assert_eq!(builder.vid, 100);
assert_eq!(builder.tunnel_id, 10000);
assert!(builder.dev.is_none());
assert!(builder.vid_end.is_none());
}
#[test]
fn test_tunnel_builder_chain() {
let builder = BridgeVlanTunnelBuilder::new(100, 10000)
.dev("vxlan0")
.range(110);
assert_eq!(builder.dev, Some(InterfaceRef::Name("vxlan0".to_string())));
assert_eq!(builder.vid, 100);
assert_eq!(builder.vid_end, Some(110));
assert_eq!(builder.tunnel_id, 10000);
}
#[test]
fn test_tunnel_builder_ifindex() {
let builder = BridgeVlanTunnelBuilder::new(100, 10000).ifindex(5);
assert_eq!(builder.dev, Some(InterfaceRef::Index(5)));
}
#[test]
fn test_tunnel_entry_equality() {
let entry1 = BridgeVlanTunnelEntry {
ifindex: 1,
vid: 100,
tunnel_id: 10000,
};
let entry2 = BridgeVlanTunnelEntry {
ifindex: 1,
vid: 100,
tunnel_id: 10000,
};
let entry3 = BridgeVlanTunnelEntry {
ifindex: 1,
vid: 100,
tunnel_id: 10001,
};
assert_eq!(entry1, entry2);
assert_ne!(entry1, entry3);
}
#[test]
fn test_tunnel_max_id() {
assert_eq!(BridgeVlanTunnelBuilder::MAX_TUNNEL_ID, 0xFFFFFF);
}
}