-
Notifications
You must be signed in to change notification settings - Fork 74
add nexthop support #225
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
base: main
Are you sure you want to change the base?
add nexthop support #225
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,230 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| use netlink_packet_core::{ | ||
| DecodeError, Emitable, Nla, Parseable, ParseableParametrized, | ||
| }; | ||
|
|
||
| #[derive(Debug, PartialEq, Eq, Clone)] | ||
| #[non_exhaustive] | ||
| pub enum NexthopAttribute { | ||
| Id(u32), | ||
| Group(Vec<NexthopGroupEntry>), | ||
| GroupType(u16), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enum instead of plain integer |
||
| Blackhole, | ||
| Oif(u32), | ||
| Gateway(Vec<u8>), // Can be IPv4 or IPv6 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| EncapType(u16), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enum instead of plain integer. |
||
| Encap(Vec<u8>), // TODO: Parse encap attributes properly | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Finish the TODO or remove it. |
||
| Fdb(Vec<u8>), // TODO: Parse FDB | ||
| ResGroup(Vec<u8>), // TODO: Parse ResGroup | ||
| Other(u16, Vec<u8>), | ||
| } | ||
|
|
||
| impl Nla for NexthopAttribute { | ||
| fn value_len(&self) -> usize { | ||
| use self::NexthopAttribute::*; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not use |
||
| match self { | ||
| Id(_) => 4, | ||
| Group(entries) => entries.len() * 8, // Each entry is 8 bytes | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No magic number please. Use |
||
| GroupType(_) => 2, | ||
| Blackhole => 0, | ||
| Oif(_) => 4, | ||
| Gateway(bytes) => bytes.len(), | ||
| EncapType(_) => 2, | ||
| Encap(bytes) => bytes.len(), | ||
| Fdb(bytes) => bytes.len(), | ||
| ResGroup(bytes) => bytes.len(), | ||
| Other(_, bytes) => bytes.len(), | ||
| } | ||
| } | ||
|
|
||
| #[rustfmt::skip] | ||
| fn emit_value(&self, buffer: &mut [u8]) { | ||
| use self::NexthopAttribute::*; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not use |
||
| match self { | ||
| | Id(value) | ||
| | Oif(value) | ||
| => buffer[0..4].copy_from_slice(&value.to_ne_bytes()), | ||
|
|
||
| | GroupType(value) | ||
| | EncapType(value) | ||
| => buffer[0..2].copy_from_slice(&value.to_ne_bytes()), | ||
|
|
||
| Group(entries) => { | ||
| for (i, entry) in entries.iter().enumerate() { | ||
| entry.emit(&mut buffer[i * 8..]); | ||
| } | ||
| } | ||
| Blackhole => {}, | ||
| Gateway(bytes) | ||
| | Encap(bytes) | ||
| | Fdb(bytes) | ||
| | ResGroup(bytes) | ||
| | Other(_, bytes) | ||
| => buffer.copy_from_slice(bytes), | ||
| } | ||
| } | ||
|
|
||
| fn kind(&self) -> u16 { | ||
| use self::NexthopAttribute::*; | ||
| match self { | ||
| Id(_) => NHA_ID, | ||
| Group(_) => NHA_GROUP, | ||
| GroupType(_) => NHA_GROUP_TYPE, | ||
| Blackhole => NHA_BLACKHOLE, | ||
| Oif(_) => NHA_OIF, | ||
| Gateway(_) => NHA_GATEWAY, | ||
| EncapType(_) => NHA_ENCAP_TYPE, | ||
| Encap(_) => NHA_ENCAP, | ||
| Fdb(_) => NHA_FDB, | ||
| ResGroup(_) => NHA_RES_GROUP, | ||
| Other(kind, _) => *kind, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub struct NexthopAttributeType; | ||
|
|
||
| impl NexthopAttributeType { | ||
| pub const ID: u16 = NHA_ID; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not expose constant, use enum. |
||
| pub const GROUP: u16 = NHA_GROUP; | ||
| pub const GROUP_TYPE: u16 = NHA_GROUP_TYPE; | ||
| pub const BLACKHOLE: u16 = NHA_BLACKHOLE; | ||
| pub const OIF: u16 = NHA_OIF; | ||
| pub const GATEWAY: u16 = NHA_GATEWAY; | ||
| pub const ENCAP_TYPE: u16 = NHA_ENCAP_TYPE; | ||
| pub const ENCAP: u16 = NHA_ENCAP; | ||
| pub const FDB: u16 = NHA_FDB; | ||
| pub const RES_GROUP: u16 = NHA_RES_GROUP; | ||
| } | ||
|
|
||
| impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized<(&'a T, u16), ()> | ||
| for NexthopAttribute | ||
| { | ||
| fn parse_with_param( | ||
| input: &(&'a T, u16), | ||
| _params: (), | ||
| ) -> Result<Self, DecodeError> { | ||
| let (payload, kind) = input; | ||
| let payload = payload.as_ref(); | ||
|
|
||
| Ok(match *kind { | ||
| NHA_ID => { | ||
| if payload.len() != 4 { | ||
| return Err(DecodeError::from("Invalid NHA_ID length")); | ||
| } | ||
| NexthopAttribute::Id(u32::from_ne_bytes( | ||
| payload.try_into().map_err(|_| { | ||
| DecodeError::from("Invalid NHA_ID length") | ||
| })?, | ||
| )) | ||
|
Comment on lines
+113
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parsing logic for fixed-size attributes like NexthopAttribute::Id(u32::from_ne_bytes(
payload.try_into().map_err(|_| {
DecodeError::from("Invalid NHA_ID length")
})?,
)) |
||
| } | ||
| NHA_GROUP => { | ||
| if payload.len() % 8 != 0 { | ||
| return Err(DecodeError::from("Invalid NHA_GROUP length")); | ||
| } | ||
| let mut entries = Vec::new(); | ||
| for chunk in payload.chunks(8) { | ||
| if let Ok(entry) = NexthopGroupEntry::parse(&chunk) { | ||
| entries.push(entry); | ||
| } else { | ||
| return Err(DecodeError::from( | ||
| "Failed to parse group entry", | ||
| )); | ||
| } | ||
| } | ||
| NexthopAttribute::Group(entries) | ||
|
Comment on lines
+123
to
+136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation for parsing if payload.len() % 8 != 0 {
return Err(DecodeError::from("Invalid NHA_GROUP length"));
}
NexthopAttribute::Group(
payload
.chunks_exact(8)
.map(NexthopGroupEntry::parse)
.collect::<Result<Vec<_, _>>>()
.map_err(|_|
// This should be unreachable given the length check above.
DecodeError::from("Failed to parse group entry"))?,
) |
||
| } | ||
| NHA_GROUP_TYPE => { | ||
| if payload.len() != 2 { | ||
| return Err(DecodeError::from( | ||
| "Invalid NHA_GROUP_TYPE length", | ||
| )); | ||
| } | ||
| NexthopAttribute::GroupType(u16::from_ne_bytes( | ||
| payload.try_into().map_err(|_| { | ||
| DecodeError::from("Invalid NHA_GROUP_TYPE length") | ||
| })?, | ||
| )) | ||
|
Comment on lines
+139
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| NHA_BLACKHOLE => NexthopAttribute::Blackhole, | ||
| NHA_OIF => { | ||
| if payload.len() != 4 { | ||
| return Err(DecodeError::from("Invalid NHA_OIF length")); | ||
| } | ||
| NexthopAttribute::Oif(u32::from_ne_bytes( | ||
| payload.try_into().map_err(|_| { | ||
| DecodeError::from("Invalid NHA_OIF length") | ||
| })?, | ||
| )) | ||
|
Comment on lines
+152
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| NHA_GATEWAY => NexthopAttribute::Gateway(payload.to_vec()), | ||
| NHA_ENCAP_TYPE => { | ||
| if payload.len() != 2 { | ||
| return Err(DecodeError::from( | ||
| "Invalid NHA_ENCAP_TYPE length", | ||
| )); | ||
| } | ||
| NexthopAttribute::EncapType(u16::from_ne_bytes( | ||
| payload.try_into().map_err(|_| { | ||
| DecodeError::from("Invalid NHA_ENCAP_TYPE length") | ||
| })?, | ||
| )) | ||
|
Comment on lines
+163
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| NHA_ENCAP => NexthopAttribute::Encap(payload.to_vec()), | ||
| NHA_FDB => NexthopAttribute::Fdb(payload.to_vec()), | ||
| NHA_RES_GROUP => NexthopAttribute::ResGroup(payload.to_vec()), | ||
| _ => NexthopAttribute::Other(*kind, payload.to_vec()), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // Constants | ||
| const NHA_ID: u16 = 1; | ||
| const NHA_GROUP: u16 = 2; | ||
| const NHA_GROUP_TYPE: u16 = 3; | ||
| const NHA_BLACKHOLE: u16 = 4; | ||
| const NHA_OIF: u16 = 5; | ||
| const NHA_GATEWAY: u16 = 6; | ||
| const NHA_ENCAP_TYPE: u16 = 7; | ||
| const NHA_ENCAP: u16 = 8; | ||
| // const NHA_GROUPS: u16 = 9; // Not implementing NHA_GROUPS as it seems deprecated or complex | ||
| // const NHA_MASTER: u16 = 10; | ||
| const NHA_FDB: u16 = 11; | ||
| const NHA_RES_GROUP: u16 = 12; | ||
|
|
||
| #[derive(Debug, PartialEq, Eq, Clone)] | ||
| pub struct NexthopGroupEntry { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| pub id: u32, | ||
| pub weight: u8, | ||
| pub resvd1: u8, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If reserved, then don't expose as public. When kernel change its names, we have to break our API. Marking this struct as Use |
||
| pub resvd2: u16, | ||
| } | ||
|
|
||
| impl Emitable for NexthopGroupEntry { | ||
| fn buffer_len(&self) -> usize { | ||
| 8 | ||
| } | ||
|
|
||
| fn emit(&self, buffer: &mut [u8]) { | ||
| buffer[0..4].copy_from_slice(&self.id.to_ne_bytes()); | ||
| buffer[4] = self.weight; | ||
| buffer[5] = self.resvd1; | ||
| buffer[6..8].copy_from_slice(&self.resvd2.to_ne_bytes()); | ||
| } | ||
| } | ||
|
|
||
| impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<T> for NexthopGroupEntry { | ||
| fn parse(buf: &T) -> Result<Self, DecodeError> { | ||
| let buf = buf.as_ref(); | ||
| if buf.len() < 8 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return Err(DecodeError::from("Invalid NexthopGroupEntry length")); | ||
| } | ||
| Ok(NexthopGroupEntry { | ||
| id: u32::from_ne_bytes(buf[0..4].try_into().unwrap()), | ||
| weight: buf[4], | ||
| resvd1: buf[5], | ||
| resvd2: u16::from_ne_bytes(buf[6..8].try_into().unwrap()), | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| use netlink_packet_core::{NlaBuffer, NlasIterator}; | ||
|
|
||
| use super::NexthopFlags; | ||
|
|
||
| buffer!(NexthopMessageBuffer(8) { | ||
| family: (u8, 0), | ||
| scope: (u8, 1), | ||
| protocol: (u8, 2), | ||
| resvd: (u8, 3), | ||
| flags_raw: (u32, 4..8), | ||
| payload: (slice, 8..), | ||
| }); | ||
|
|
||
| impl<T: AsRef<[u8]>> NexthopMessageBuffer<T> { | ||
| pub fn flags(&self) -> NexthopFlags { | ||
| NexthopFlags::from_bits_truncate(self.flags_raw()) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| } | ||
|
|
||
| impl<T: AsRef<[u8]> + AsMut<[u8]>> NexthopMessageBuffer<T> { | ||
| pub fn set_flags(&mut self, flags: NexthopFlags) { | ||
| self.set_flags_raw(flags.bits()); | ||
| } | ||
| } | ||
|
|
||
| impl<'a, T: AsRef<[u8]> + ?Sized> NexthopMessageBuffer<&'a T> { | ||
| pub fn attributes( | ||
| &self, | ||
| ) -> impl Iterator< | ||
| Item = Result<NlaBuffer<&'a [u8]>, netlink_packet_core::DecodeError>, | ||
| > { | ||
| NlasIterator::new(self.payload()) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not unreachable. In the future, kernel might add new type or hacker can craft invalid netlink packet to crash the whole project.
Please check other struct on how to handle this properly.