Skip to content

Include NLA header on mcast groups and ops. #8

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

Merged
merged 5 commits into from
Jul 9, 2023
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
48 changes: 47 additions & 1 deletion src/ctrl/nlas/mcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,53 @@ use netlink_packet_utils::{
traits::*,
DecodeError,
};
use std::mem::size_of_val;
use std::{mem::size_of_val, ops::Deref};

pub struct McastGroupList(Vec<McastGroup>);

impl Deref for McastGroupList {
type Target = Vec<McastGroup>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<&Vec<Vec<McastGrpAttrs>>> for McastGroupList {
fn from(groups: &Vec<Vec<McastGrpAttrs>>) -> Self {
Self(
groups
.iter()
.cloned()
.enumerate()
.map(|(index, nlas)| McastGroup {
index: index as u16,
nlas,
})
.collect(),
)
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct McastGroup {
pub index: u16,
pub nlas: Vec<McastGrpAttrs>,
}

impl Nla for McastGroup {
fn value_len(&self) -> usize {
self.nlas.as_slice().buffer_len()
}

fn kind(&self) -> u16 {
self.index + 1
}

fn emit_value(&self, buffer: &mut [u8]) {
self.nlas.as_slice().emit(buffer);
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum McastGrpAttrs {
Expand Down
145 changes: 130 additions & 15 deletions src/ctrl/nlas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ impl Nla for GenlCtrlAttrs {
Version(v) => size_of_val(v),
HdrSize(v) => size_of_val(v),
MaxAttr(v) => size_of_val(v),
Ops(nlas) => nlas.iter().map(|op| op.as_slice().buffer_len()).sum(),
Ops(nlas) => OpList::from(nlas).as_slice().buffer_len(),
McastGroups(nlas) => {
nlas.iter().map(|op| op.as_slice().buffer_len()).sum()
McastGroupList::from(nlas).as_slice().buffer_len()
}
Policy(nla) => nla.buffer_len(),
OpPolicy(nla) => nla.buffer_len(),
Expand Down Expand Up @@ -82,18 +82,10 @@ impl Nla for GenlCtrlAttrs {
HdrSize(v) => NativeEndian::write_u32(buffer, *v),
MaxAttr(v) => NativeEndian::write_u32(buffer, *v),
Ops(nlas) => {
let mut len = 0;
for op in nlas {
op.as_slice().emit(&mut buffer[len..]);
len += op.as_slice().buffer_len();
}
OpList::from(nlas).as_slice().emit(buffer);
}
McastGroups(nlas) => {
let mut len = 0;
for op in nlas {
op.as_slice().emit(&mut buffer[len..]);
len += op.as_slice().buffer_len();
}
McastGroupList::from(nlas).as_slice().emit(buffer);
}
Policy(nla) => nla.emit_value(buffer),
OpPolicy(nla) => nla.emit_value(buffer),
Expand Down Expand Up @@ -140,8 +132,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
})
})
.collect::<Result<Vec<Vec<_>>, _>>()
.context("failed to parse CTRL_ATTR_OPS")?;

.context("failed to parse CTRL_ATTR_MCAST_GROUPS")?;
Self::Ops(ops)
}
CTRL_ATTR_MCAST_GROUPS => {
Expand All @@ -159,7 +150,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
})
.collect::<Result<Vec<Vec<_>>, _>>()
.context("failed to parse CTRL_ATTR_MCAST_GROUPS")?;

Self::McastGroups(groups)
}
CTRL_ATTR_POLICY => Self::Policy(
Expand All @@ -179,3 +169,128 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn mcast_groups_parse() {
let mcast_bytes: [u8; 24] = [
24, 0, // Netlink header length
7, 0, // Netlink header kind (Mcast groups)
20, 0, // Mcast group nested NLA length
1, 0, // Mcast group kind
8, 0, // Id length
2, 0, // Id kind
1, 0, 0, 0, // Id
8, 0, // Name length
1, 0, // Name kind
b't', b'e', b's', b't', // Name
];
let nla_buffer = NlaBuffer::new_checked(&mcast_bytes[..])
.expect("Failed to create NlaBuffer");
let result_attr = GenlCtrlAttrs::parse(&nla_buffer)
.expect("Failed to parse encoded McastGroups");
let expected_attr = GenlCtrlAttrs::McastGroups(vec![vec![
McastGrpAttrs::Id(1),
McastGrpAttrs::Name("test".to_string()),
]]);
assert_eq!(expected_attr, result_attr);
}

#[test]
fn mcast_groups_emit() {
let mcast_attr = GenlCtrlAttrs::McastGroups(vec![
vec![
McastGrpAttrs::Id(7),
McastGrpAttrs::Name("group1".to_string()),
],
vec![
McastGrpAttrs::Id(8),
McastGrpAttrs::Name("group2".to_string()),
],
]);
let expected_bytes: [u8; 52] = [
52, 0, // Netlink header length
7, 0, // Netlink header kind (Mcast groups)
24, 0, // Mcast group nested NLA length
1, 0, // Mcast group kind (index 1)
8, 0, // Id length
2, 0, // Id kind
7, 0, 0, 0, // Id
11, 0, // Name length
1, 0, // Name kind
b'g', b'r', b'o', b'u', b'p', b'1', 0, // Name
0, // mcast group padding
24, 0, // Mcast group nested NLA length
2, 0, // Mcast group kind (index 2)
8, 0, // Id length
2, 0, // Id kind
8, 0, 0, 0, // Id
11, 0, // Name length
1, 0, // Name kind
b'g', b'r', b'o', b'u', b'p', b'2', 0, // Name
0, // padding
];
let mut buf = vec![0u8; 100];
mcast_attr.emit(&mut buf);

assert_eq!(&expected_bytes[..], &buf[..expected_bytes.len()]);
}

#[test]
fn ops_parse() {
let ops_bytes: [u8; 24] = [
24, 0, // Netlink header length
6, 0, // Netlink header kind (Ops)
20, 0, // Op nested NLA length
0, 0, // Op kind
8, 0, // Id length
1, 0, // Id kind
1, 0, 0, 0, // Id
8, 0, // Flags length
2, 0, // Flags kind
123, 0, 0, 0, // Flags
];
let nla_buffer = NlaBuffer::new_checked(&ops_bytes[..])
.expect("Failed to create NlaBuffer");
let result_attr = GenlCtrlAttrs::parse(&nla_buffer)
.expect("Failed to parse encoded McastGroups");
let expected_attr =
GenlCtrlAttrs::Ops(vec![vec![OpAttrs::Id(1), OpAttrs::Flags(123)]]);
assert_eq!(expected_attr, result_attr);
}

#[test]
fn ops_emit() {
let ops = GenlCtrlAttrs::Ops(vec![
vec![OpAttrs::Id(1), OpAttrs::Flags(11)],
vec![OpAttrs::Id(3), OpAttrs::Flags(33)],
]);
let expected_bytes: [u8; 44] = [
44, 0, // Netlink header length
6, 0, // Netlink header kind (Ops)
20, 0, // Op nested NLA length
1, 0, // Op kind
8, 0, // Id length
1, 0, // Id kind
1, 0, 0, 0, // Id
8, 0, // Flags length
2, 0, // Flags kind
11, 0, 0, 0, // Flags
20, 0, // Op nested NLA length
2, 0, // Op kind
8, 0, // Id length
1, 0, // Id kind
3, 0, 0, 0, // Id
8, 0, // Flags length
2, 0, // Flags kind
33, 0, 0, 0, // Flags
];
let mut buf = vec![0u8; 100];
ops.emit(&mut buf);

assert_eq!(&expected_bytes[..], &buf[..expected_bytes.len()]);
}
}
47 changes: 46 additions & 1 deletion src/ctrl/nlas/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,52 @@ use netlink_packet_utils::{
traits::*,
DecodeError,
};
use std::mem::size_of_val;
use std::{mem::size_of_val, ops::Deref};

pub struct OpList(Vec<Op>);

impl Deref for OpList {
type Target = Vec<Op>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<&Vec<Vec<OpAttrs>>> for OpList {
fn from(ops: &Vec<Vec<OpAttrs>>) -> Self {
Self(
ops.iter()
.cloned()
.enumerate()
.map(|(index, nlas)| Op {
index: index as u16,
nlas,
})
.collect(),
)
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Op {
pub index: u16,
pub nlas: Vec<OpAttrs>,
}

impl Nla for Op {
fn value_len(&self) -> usize {
self.nlas.as_slice().buffer_len()
}

fn kind(&self) -> u16 {
self.index + 1
}

fn emit_value(&self, buffer: &mut [u8]) {
self.nlas.as_slice().emit(buffer);
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OpAttrs {
Expand Down