Skip to content

Commit 5bd5cc9

Browse files
Aperencecathay4t
authored andcommitted
use buffer! + changed some comments
1 parent 1a33cd3 commit 5bd5cc9

File tree

1 file changed

+93
-50
lines changed

1 file changed

+93
-50
lines changed

src/route/seg6.rs

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
// SPDX-License-Identifier: MIT
22

33
use anyhow::Context;
4-
use byteorder::{ByteOrder, NativeEndian};
54
use netlink_packet_utils::{
65
nla::{DefaultNla, Nla, NlaBuffer},
7-
Parseable,
6+
DecodeError, Parseable,
87
};
9-
use std::net::Ipv6Addr;
8+
use std::net::{IpAddr, Ipv6Addr};
9+
10+
use crate::ip::{emit_ip_addr, parse_ipv6_addr};
11+
12+
const SEG6_IPTUN_MODE_INLINE: u32 = 0;
13+
const SEG6_IPTUN_MODE_ENCAP: u32 = 1;
14+
//const SEG6_IPTUN_MODE_L2ENCAP: u32 = 2;
15+
//const SEG6_IPTUN_MODE_ENCAP_RED: u32 = 3;
16+
//const SEG6_IPTUN_MODE_L2ENCAP_RED: u32 = 4;
1017

1118
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1219
#[non_exhaustive]
@@ -24,8 +31,8 @@ pub enum Seg6Mode {
2431
impl From<Seg6Mode> for u32 {
2532
fn from(value: Seg6Mode) -> Self {
2633
match value {
27-
Seg6Mode::Inline => 0,
28-
Seg6Mode::Encap => 1,
34+
Seg6Mode::Inline => SEG6_IPTUN_MODE_INLINE,
35+
Seg6Mode::Encap => SEG6_IPTUN_MODE_ENCAP,
2936
Seg6Mode::Other(i) => i,
3037
}
3138
}
@@ -34,8 +41,8 @@ impl From<Seg6Mode> for u32 {
3441
impl From<u32> for Seg6Mode {
3542
fn from(value: u32) -> Self {
3643
match value {
37-
0 => Seg6Mode::Inline,
38-
1 => Seg6Mode::Encap,
44+
SEG6_IPTUN_MODE_INLINE => Seg6Mode::Inline,
45+
SEG6_IPTUN_MODE_ENCAP => Seg6Mode::Encap,
3946
v => Seg6Mode::Other(v),
4047
}
4148
}
@@ -76,6 +83,27 @@ impl Nla for RouteSeg6IpTunnel {
7683
}
7784
}
7885

86+
const SEG6_HEADER_LEN: usize = 12;
87+
88+
buffer!(Seg6MessageBuffer(SEG6_HEADER_LEN) {
89+
mode: (u32, 0..4),
90+
nexthdr: (u8, 4),
91+
hdrlen: (u8, 5),
92+
seg_type: (u8, 6),
93+
segments_left: (u8, 7),
94+
first_segment: (u8, 8),
95+
flags: (u8, 9),
96+
tag: (u16, 10..12),
97+
segments: (slice, SEG6_HEADER_LEN..),
98+
});
99+
100+
const SEG6_SEGMENT_LEN: usize = 16;
101+
102+
buffer!(Seg6SegmentBuffer(SEG6_SEGMENT_LEN) {
103+
segment: (slice, 0..SEG6_SEGMENT_LEN),
104+
rest: (slice, SEG6_SEGMENT_LEN..)
105+
});
106+
79107
/// Netlink attributes for `RTA_ENCAP` with `RTA_ENCAP_TYPE` set to
80108
/// `LWTUNNEL_ENCAP_SEG6`.
81109
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -87,9 +115,36 @@ pub struct Seg6Header {
87115
pub segments: Vec<Ipv6Addr>,
88116
}
89117

118+
impl Seg6Header {
119+
fn push_segments(buf: &mut [u8], mut segments: Vec<Ipv6Addr>) {
120+
if let Some(segment) = segments.pop() {
121+
let mut segment_buffer = Seg6SegmentBuffer::new(buf);
122+
emit_ip_addr(&IpAddr::V6(segment), segment_buffer.segment_mut());
123+
Self::push_segments(segment_buffer.rest_mut(), segments);
124+
}
125+
}
126+
127+
fn get_segments(
128+
buf: &[u8],
129+
segments: &mut Vec<Ipv6Addr>,
130+
) -> Result<(), DecodeError> {
131+
// are there any remaining segments ?
132+
if buf.len() >= SEG6_SEGMENT_LEN {
133+
let segment_buffer = Seg6SegmentBuffer::new(buf);
134+
let segment = parse_ipv6_addr(segment_buffer.segment())?;
135+
segments.push(segment);
136+
Self::get_segments(segment_buffer.rest(), segments)?;
137+
}
138+
Ok(())
139+
}
140+
}
141+
90142
impl Nla for Seg6Header {
91143
fn value_len(&self) -> usize {
92144
let segments = match self.mode {
145+
// in inline mode, seg6 add an additional segment (::) at the
146+
// end of the segment list, thus must have one additional
147+
// segment slot in the payload
93148
Seg6Mode::Inline => self.segments.len() + 1,
94149
Seg6Mode::Encap => self.segments.len(),
95150
Seg6Mode::Other(_) => self.segments.len(),
@@ -103,9 +158,15 @@ impl Nla for Seg6Header {
103158

104159
fn emit_value(&self, buffer: &mut [u8]) {
105160
// Some sources for understanding the format of Seg6 in Netlink
106-
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/seg6.h
107-
// https://github.com/iproute2/iproute2/blob/main/include/uapi/linux/seg6_iptunnel.h#L27
108-
// https://github.com/iproute2/iproute2/blob/e3f9681d4a777fb2595a322b421abf0036ab1aae/ip/iproute_lwtunnel.c#L952
161+
//
162+
// torvalds/linux:
163+
// include/uapi/linux/seg6.h
164+
// include/uapi/linux/seg6_iptunnel.h
165+
//
166+
// iproute2/iproute2
167+
// ip/iproute_lwtunnel.c parse_encap_seg6()
168+
169+
let mut seg6_header = Seg6MessageBuffer::new(buffer);
109170

110171
let mut number_segments = self.segments.len();
111172
if matches!(self.mode, Seg6Mode::Inline) {
@@ -114,37 +175,23 @@ impl Nla for Seg6Header {
114175

115176
let srhlen = 8 + 16 * number_segments;
116177

117-
// mode : 4 bytes
118-
NativeEndian::write_u32(&mut buffer[..4], self.mode.into());
119-
// nexthdr : 1 bytes
120-
buffer[4] = 0;
121-
// hdrlen : 1 bytes
122-
buffer[5] = ((srhlen >> 3) - 1) as u8;
123-
// type : 1 byte
124-
buffer[6] = 4;
125-
// segments_left : 1 byte
126-
buffer[7] = (number_segments - 1) as u8;
127-
// first_segment : 1 byte
128-
buffer[8] = (number_segments - 1) as u8;
129-
// flags : 1 byte
130-
buffer[9] = 0;
131-
// tag : 2 bytes
132-
NativeEndian::write_u16(&mut buffer[10..12], 0);
133-
134-
let mut offset = 12;
178+
seg6_header.set_mode(self.mode.into());
179+
seg6_header.set_nexthdr(0);
180+
seg6_header.set_hdrlen(((srhlen >> 3) - 1) as u8);
181+
seg6_header.set_seg_type(4);
182+
seg6_header.set_segments_left((number_segments - 1) as u8);
183+
seg6_header.set_first_segment((number_segments - 1) as u8);
184+
seg6_header.set_flags(0);
185+
seg6_header.set_tag(0);
186+
187+
let mut segments = self.segments.clone();
135188

136189
// Add the last segment (::) if working in inline mode
137190
if matches!(self.mode, Seg6Mode::Inline) {
138-
let addr: Ipv6Addr = "::".parse().expect("Impossible error");
139-
buffer[offset..offset + 16].copy_from_slice(&addr.octets());
140-
offset += 16;
191+
segments.push("::".parse().expect("Impossible error"))
141192
}
142193

143-
// Add all segments in reverse order
144-
for addr in self.segments.iter().rev() {
145-
buffer[offset..offset + 16].copy_from_slice(&addr.octets());
146-
offset += 16;
147-
}
194+
Seg6Header::push_segments(seg6_header.segments_mut(), segments);
148195
}
149196
}
150197

@@ -157,29 +204,25 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
157204
let payload = buf.value();
158205
Ok(match buf.kind() {
159206
SEG6_IPTUNNEL_SRH => {
160-
let mode = NativeEndian::read_u32(payload).into();
207+
let seg6_header = Seg6MessageBuffer::new(payload);
161208

162-
let number_segments = payload[7];
163-
164-
let mut offset = 12;
165209
let mut segments: Vec<Ipv6Addr> = vec![];
166-
for _ in 0..number_segments + 1 {
167-
let slice: [u8; 16] = payload[offset..offset + 16]
168-
.try_into()
169-
.expect("Impossible to fail");
170-
let ip_addr = Ipv6Addr::from(slice);
171-
segments.push(ip_addr);
172-
offset += 16;
173-
}
210+
Seg6Header::get_segments(
211+
seg6_header.segments(),
212+
&mut segments,
213+
)?;
174214

175215
let mut segments: Vec<Ipv6Addr> =
176216
segments.into_iter().rev().collect();
177217

178-
if matches!(mode, Seg6Mode::Inline) {
218+
if matches!(seg6_header.mode().into(), Seg6Mode::Inline) {
179219
segments.pop(); // remove last inline segment
180220
}
181221

182-
RouteSeg6IpTunnel::Seg6(Seg6Header { mode, segments })
222+
RouteSeg6IpTunnel::Seg6(Seg6Header {
223+
mode: seg6_header.mode().into(),
224+
segments,
225+
})
183226
}
184227
_ => Self::Other(
185228
DefaultNla::parse(buf)

0 commit comments

Comments
 (0)