1
1
// SPDX-License-Identifier: MIT
2
2
3
- use std:: { fmt, io, mem:: size_of} ;
3
+ use std:: { fmt, io, mem:: size_of, num :: NonZeroI32 } ;
4
4
5
5
use byteorder:: { ByteOrder , NativeEndian } ;
6
6
use netlink_packet_utils:: DecodeError ;
@@ -46,10 +46,14 @@ impl<T: AsRef<[u8]>> ErrorBuffer<T> {
46
46
}
47
47
}
48
48
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 > {
51
55
let data = self . buffer . as_ref ( ) ;
52
- NativeEndian :: read_i32 ( & data[ CODE ] )
56
+ NonZeroI32 :: new ( NativeEndian :: read_i32 ( & data[ CODE ] ) )
53
57
}
54
58
}
55
59
@@ -77,22 +81,36 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> ErrorBuffer<T> {
77
81
}
78
82
}
79
83
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
80
90
#[ derive( Debug , Default , Clone , PartialEq , Eq ) ]
81
91
#[ non_exhaustive]
82
92
pub 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.
84
104
pub header : Vec < u8 > ,
85
105
}
86
106
87
- pub type AckMessage = ErrorMessage ;
88
-
89
107
impl Emitable for ErrorMessage {
90
108
fn buffer_len ( & self ) -> usize {
91
109
size_of :: < i32 > ( ) + self . header . len ( )
92
110
}
93
111
fn emit ( & self , buffer : & mut [ u8 ] ) {
94
112
let mut buffer = ErrorBuffer :: new ( buffer) ;
95
- buffer. set_code ( self . code ) ;
113
+ buffer. set_code ( self . raw_code ( ) ) ;
96
114
buffer. payload_mut ( ) . copy_from_slice ( & self . header )
97
115
}
98
116
}
@@ -119,13 +137,18 @@ impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable<ErrorBuffer<&'buffer T>>
119
137
}
120
138
121
139
impl ErrorMessage {
140
+ /// Returns the raw error code.
141
+ pub fn raw_code ( & self ) -> i32 {
142
+ self . code . map_or ( 0 , NonZeroI32 :: get)
143
+ }
144
+
122
145
/// According to [`netlink(7)`](https://linux.die.net/man/7/netlink)
123
146
/// the `NLMSG_ERROR` return Negative errno or 0 for acknowledgements.
124
147
///
125
148
/// convert into [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
126
149
/// using the absolute value from errno code
127
150
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 ( ) )
129
152
}
130
153
}
131
154
@@ -149,7 +172,7 @@ mod tests {
149
172
fn into_io_error ( ) {
150
173
let io_err = io:: Error :: from_raw_os_error ( 95 ) ;
151
174
let err_msg = ErrorMessage {
152
- code : -95 ,
175
+ code : NonZeroI32 :: new ( -95 ) ,
153
176
header : vec ! [ ] ,
154
177
} ;
155
178
@@ -158,4 +181,40 @@ mod tests {
158
181
assert_eq ! ( err_msg. to_string( ) , io_err. to_string( ) ) ;
159
182
assert_eq ! ( to_io. raw_os_error( ) , io_err. raw_os_error( ) ) ;
160
183
}
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
+ }
161
220
}
0 commit comments