11// SPDX-License-Identifier: MIT
22
3- use std:: { fmt, io, mem:: size_of} ;
3+ use std:: { fmt, io, mem:: size_of, num :: NonZeroI32 } ;
44
55use byteorder:: { ByteOrder , NativeEndian } ;
66use netlink_packet_utils:: DecodeError ;
@@ -46,10 +46,14 @@ impl<T: AsRef<[u8]>> ErrorBuffer<T> {
4646 }
4747 }
4848
49- /// Return the error code
50- pub fn code ( & self ) -> i32 {
49+ /// Return the error code.
50+ ///
51+ /// Returns `None` when there is no error to report (the message is an ACK),
52+ /// or a `Some(e)` if there is a non-zero error code `e` to report (the
53+ /// message is a NACK).
54+ pub fn code ( & self ) -> Option < NonZeroI32 > {
5155 let data = self . buffer . as_ref ( ) ;
52- NativeEndian :: read_i32 ( & data[ CODE ] )
56+ NonZeroI32 :: new ( NativeEndian :: read_i32 ( & data[ CODE ] ) )
5357 }
5458}
5559
@@ -77,22 +81,36 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> ErrorBuffer<T> {
7781 }
7882}
7983
84+ /// An `NLMSG_ERROR` message.
85+ ///
86+ /// Per [RFC 3549 section 2.3.2.2], this message carries the return code for a
87+ /// request which will indicate either success (an ACK) or failure (a NACK).
88+ ///
89+ /// [RFC 3549 section 2.3.2.2]: https://datatracker.ietf.org/doc/html/rfc3549#section-2.3.2.2
8090#[ derive( Debug , Default , Clone , PartialEq , Eq ) ]
8191#[ non_exhaustive]
8292pub struct ErrorMessage {
83- pub code : i32 ,
93+ /// The error code.
94+ ///
95+ /// Holds `None` when there is no error to report (the message is an ACK),
96+ /// or a `Some(e)` if there is a non-zero error code `e` to report (the
97+ /// message is a NACK).
98+ ///
99+ /// See [Netlink message types] for details.
100+ ///
101+ /// [Netlink message types]: https://kernel.org/doc/html/next/userspace-api/netlink/intro.html#netlink-message-types
102+ pub code : Option < NonZeroI32 > ,
103+ /// The original request's header.
84104 pub header : Vec < u8 > ,
85105}
86106
87- pub type AckMessage = ErrorMessage ;
88-
89107impl Emitable for ErrorMessage {
90108 fn buffer_len ( & self ) -> usize {
91109 size_of :: < i32 > ( ) + self . header . len ( )
92110 }
93111 fn emit ( & self , buffer : & mut [ u8 ] ) {
94112 let mut buffer = ErrorBuffer :: new ( buffer) ;
95- buffer. set_code ( self . code ) ;
113+ buffer. set_code ( self . raw_code ( ) ) ;
96114 buffer. payload_mut ( ) . copy_from_slice ( & self . header )
97115 }
98116}
@@ -119,13 +137,18 @@ impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable<ErrorBuffer<&'buffer T>>
119137}
120138
121139impl ErrorMessage {
140+ /// Returns the raw error code.
141+ pub fn raw_code ( & self ) -> i32 {
142+ self . code . map_or ( 0 , NonZeroI32 :: get)
143+ }
144+
122145 /// According to [`netlink(7)`](https://linux.die.net/man/7/netlink)
123146 /// the `NLMSG_ERROR` return Negative errno or 0 for acknowledgements.
124147 ///
125148 /// convert into [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
126149 /// using the absolute value from errno code
127150 pub fn to_io ( & self ) -> io:: Error {
128- io:: Error :: from_raw_os_error ( self . code . abs ( ) )
151+ io:: Error :: from_raw_os_error ( self . raw_code ( ) . abs ( ) )
129152 }
130153}
131154
@@ -149,7 +172,7 @@ mod tests {
149172 fn into_io_error ( ) {
150173 let io_err = io:: Error :: from_raw_os_error ( 95 ) ;
151174 let err_msg = ErrorMessage {
152- code : -95 ,
175+ code : NonZeroI32 :: new ( -95 ) ,
153176 header : vec ! [ ] ,
154177 } ;
155178
@@ -158,4 +181,40 @@ mod tests {
158181 assert_eq ! ( err_msg. to_string( ) , io_err. to_string( ) ) ;
159182 assert_eq ! ( to_io. raw_os_error( ) , io_err. raw_os_error( ) ) ;
160183 }
184+
185+ #[ test]
186+ fn parse_ack ( ) {
187+ let bytes = vec ! [ 0 , 0 , 0 , 0 ] ;
188+ let msg = ErrorBuffer :: new_checked ( & bytes)
189+ . and_then ( |buf| ErrorMessage :: parse ( & buf) )
190+ . expect ( "failed to parse NLMSG_ERROR" ) ;
191+ assert_eq ! (
192+ ErrorMessage {
193+ code: None ,
194+ header: Vec :: new( )
195+ } ,
196+ msg
197+ ) ;
198+ assert_eq ! ( msg. raw_code( ) , 0 ) ;
199+ }
200+
201+ #[ test]
202+ fn parse_nack ( ) {
203+ // SAFETY: value is non-zero.
204+ const ERROR_CODE : NonZeroI32 =
205+ unsafe { NonZeroI32 :: new_unchecked ( -1234 ) } ;
206+ let mut bytes = vec ! [ 0 , 0 , 0 , 0 ] ;
207+ NativeEndian :: write_i32 ( & mut bytes, ERROR_CODE . get ( ) ) ;
208+ let msg = ErrorBuffer :: new_checked ( & bytes)
209+ . and_then ( |buf| ErrorMessage :: parse ( & buf) )
210+ . expect ( "failed to parse NLMSG_ERROR" ) ;
211+ assert_eq ! (
212+ ErrorMessage {
213+ code: Some ( ERROR_CODE ) ,
214+ header: Vec :: new( )
215+ } ,
216+ msg
217+ ) ;
218+ assert_eq ! ( msg. raw_code( ) , ERROR_CODE . get( ) ) ;
219+ }
161220}
0 commit comments