11// SPDX-License-Identifier: MIT
22
33use anyhow:: Context ;
4- use byteorder:: { ByteOrder , NativeEndian } ;
54use 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 {
2431impl 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 {
3441impl 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+
90142impl 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