1 use core::borrow::{Borrow, BorrowMut}; 2 3 /// Options present in an [`crate::Ipv4Header`]. 4 /// 5 /// IPv4 header options can only have a length that 6 /// is a multiple of 4 bytes (meaning 4, 8, 12, ...) and 7 /// a maximum length of 40 bytes (40 bytes the maximum length is 8 /// limited by maximum value of the "intra header length" field 9 /// in the IPv4 header). 10 /// 11 /// # Examples 12 /// 13 /// ``` 14 /// use etherparse::Ipv4Options; 15 /// 16 /// { 17 /// // static sized arrays of size 4,8,... 40 can directly be converted 18 /// let options: Ipv4Options = [1,2,3,4].into(); 19 /// assert_eq!(&options[..], &[1,2,3,4]); 20 /// } 21 /// 22 /// { 23 /// // slices can also be "try_from" converted 24 /// let some_data = vec![1,2,3,4,5,6,7,8]; 25 /// let options: Ipv4Options = (&some_data[..]).try_into().unwrap(); 26 /// assert_eq!(options.as_slice(), &[1,2,3,4,5,6,7,8]); 27 /// } 28 /// { 29 /// // only slices with a length that is multiple of 4 and a maximum value of 40 30 /// // can be converted, otherwise you will get an error 31 /// use etherparse::err::ipv4::BadOptionsLen; 32 /// 33 /// let result = Ipv4Options::try_from(&[1,2,3][..]); 34 /// assert_eq!(result, Err(BadOptionsLen { bad_len: 3 })); 35 /// } 36 /// ``` 37 #[derive(Clone)] 38 pub struct Ipv4Options { 39 pub(crate) len: u8, 40 pub(crate) buf: [u8; 40], 41 } 42 43 impl Ipv4Options { 44 /// Maximum length of the IPv4 options in bytes. 45 pub const MAX_LEN: u8 = 40; 46 47 /// Setup an empty options array. 48 #[inline] new() -> Ipv4Options49 pub fn new() -> Ipv4Options { 50 Ipv4Options { 51 len: 0, 52 buf: [0; 40], 53 } 54 } 55 56 /// Returns the slice containing the data of the options. 57 #[inline] as_slice(&self) -> &[u8]58 pub fn as_slice(&self) -> &[u8] { 59 unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len.into()) } 60 } 61 62 /// Returns a mutable slice containing the data of the options. 63 #[inline] as_mut_slice(&mut self) -> &mut [u8]64 pub fn as_mut_slice(&mut self) -> &mut [u8] { 65 unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len.into()) } 66 } 67 68 /// Returns the length of the options in bytes. 69 #[inline] len(&self) -> usize70 pub fn len(&self) -> usize { 71 usize::from(self.len) 72 } 73 74 /// Returns the length of the options in bytes. 75 #[inline] len_u8(&self) -> u876 pub fn len_u8(&self) -> u8 { 77 self.len 78 } 79 80 /// Returns if the length of the options is zero. 81 #[inline] is_empty(&self) -> bool82 pub fn is_empty(&self) -> bool { 83 self.len == 0 84 } 85 } 86 87 impl TryFrom<&[u8]> for Ipv4Options { 88 type Error = crate::err::ipv4::BadOptionsLen; 89 try_from(value: &[u8]) -> Result<Self, Self::Error>90 fn try_from(value: &[u8]) -> Result<Self, Self::Error> { 91 if value.len() <= 40 && value.len() % 4 == 0 { 92 let mut result = Ipv4Options { 93 len: value.len() as u8, 94 buf: [0; 40], 95 }; 96 unsafe { 97 // SAFETY: Safe as value.len() <= 40 and the result buffer size is 40. 98 core::ptr::copy_nonoverlapping( 99 value.as_ptr(), 100 result.buf.as_mut_ptr(), 101 value.len(), 102 ); 103 } 104 Ok(result) 105 } else { 106 Err(Self::Error { 107 bad_len: value.len(), 108 }) 109 } 110 } 111 } 112 113 impl Default for Ipv4Options { 114 #[inline] default() -> Self115 fn default() -> Self { 116 Self { 117 len: 0, 118 buf: [0; 40], 119 } 120 } 121 } 122 123 impl core::fmt::Debug for Ipv4Options { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result124 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 125 self.as_slice().fmt(f) 126 } 127 } 128 129 impl PartialEq for Ipv4Options { eq(&self, other: &Self) -> bool130 fn eq(&self, other: &Self) -> bool { 131 self.as_slice() == other.as_slice() 132 } 133 } 134 impl Eq for Ipv4Options {} 135 136 impl core::hash::Hash for Ipv4Options { hash<H: core::hash::Hasher>(&self, state: &mut H)137 fn hash<H: core::hash::Hasher>(&self, state: &mut H) { 138 self.as_slice().hash(state); 139 } 140 } 141 142 impl core::cmp::PartialOrd for Ipv4Options { partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering>143 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { 144 Some(self.as_slice().cmp(other.as_slice())) 145 } 146 } 147 148 impl core::cmp::Ord for Ipv4Options { cmp(&self, other: &Self) -> core::cmp::Ordering149 fn cmp(&self, other: &Self) -> core::cmp::Ordering { 150 self.as_slice().cmp(other.as_slice()) 151 } 152 } 153 154 impl From<[u8; 0]> for Ipv4Options { 155 #[inline] from(_: [u8; 0]) -> Self156 fn from(_: [u8; 0]) -> Self { 157 Ipv4Options { 158 len: 0, 159 buf: [0; 40], 160 } 161 } 162 } 163 164 macro_rules! from_static_array { 165 ($x:expr) => { 166 impl From<[u8; $x]> for Ipv4Options { 167 #[inline] 168 fn from(values: [u8; $x]) -> Self { 169 let mut result = Ipv4Options { 170 len: $x, 171 buf: [0; 40], 172 }; 173 let r = result.buf.as_mut_ptr() as *mut [u8; $x]; 174 unsafe { 175 *r = values; 176 } 177 result 178 } 179 } 180 }; 181 } 182 183 from_static_array!(4); 184 from_static_array!(8); 185 from_static_array!(12); 186 from_static_array!(16); 187 from_static_array!(20); 188 from_static_array!(24); 189 from_static_array!(28); 190 from_static_array!(32); 191 from_static_array!(36); 192 193 impl From<[u8; 40]> for Ipv4Options { from(values: [u8; 40]) -> Self194 fn from(values: [u8; 40]) -> Self { 195 Ipv4Options { 196 len: 40, 197 buf: values, 198 } 199 } 200 } 201 202 impl AsRef<Ipv4Options> for Ipv4Options { as_ref(&self) -> &Ipv4Options203 fn as_ref(&self) -> &Ipv4Options { 204 self 205 } 206 } 207 208 impl AsRef<[u8]> for Ipv4Options { as_ref(&self) -> &[u8]209 fn as_ref(&self) -> &[u8] { 210 self.as_slice() 211 } 212 } 213 214 impl AsMut<Ipv4Options> for Ipv4Options { as_mut(&mut self) -> &mut Ipv4Options215 fn as_mut(&mut self) -> &mut Ipv4Options { 216 self 217 } 218 } 219 220 impl AsMut<[u8]> for Ipv4Options { as_mut(&mut self) -> &mut [u8]221 fn as_mut(&mut self) -> &mut [u8] { 222 self.as_mut_slice() 223 } 224 } 225 226 impl Borrow<[u8]> for Ipv4Options { borrow(&self) -> &[u8]227 fn borrow(&self) -> &[u8] { 228 self.as_slice() 229 } 230 } 231 232 impl BorrowMut<[u8]> for Ipv4Options { borrow_mut(&mut self) -> &mut [u8]233 fn borrow_mut(&mut self) -> &mut [u8] { 234 self.as_mut_slice() 235 } 236 } 237 238 impl core::ops::Deref for Ipv4Options { 239 type Target = [u8]; 240 241 #[inline] deref(&self) -> &[u8]242 fn deref(&self) -> &[u8] { 243 self.as_slice() 244 } 245 } 246 247 impl core::ops::DerefMut for Ipv4Options { 248 #[inline] deref_mut(&mut self) -> &mut [u8]249 fn deref_mut(&mut self) -> &mut [u8] { 250 self.as_mut_slice() 251 } 252 } 253 254 #[cfg(test)] 255 mod tests { 256 use super::*; 257 use crate::test_gens::*; 258 use proptest::prelude::*; 259 use std::format; 260 261 #[test] new()262 fn new() { 263 let actual = Ipv4Options::new(); 264 assert_eq!(actual.len, 0); 265 assert_eq!(actual.buf, [0; 40]); 266 } 267 268 #[test] is_empty()269 fn is_empty() { 270 { 271 let actual = Ipv4Options::new(); 272 assert!(actual.is_empty()); 273 } 274 { 275 let actual: Ipv4Options = [1, 2, 3, 4].into(); 276 assert_eq!(false, actual.is_empty()); 277 } 278 } 279 280 #[test] try_from()281 fn try_from() { 282 const DATA: [u8; 48] = [ 283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 284 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 285 47, 48, 286 ]; 287 288 // ok cases 289 for len_div_4 in 0usize..=10 { 290 let mut actual = Ipv4Options::try_from(&DATA[..len_div_4 * 4]).unwrap(); 291 assert_eq!(actual.as_slice(), &DATA[..len_div_4 * 4]); 292 assert_eq!(actual.as_mut_slice(), &DATA[..len_div_4 * 4]); 293 assert_eq!(actual.len_u8(), (len_div_4 * 4) as u8); 294 assert_eq!(actual.len(), len_div_4 * 4); 295 } 296 297 // error cases 298 use crate::err::ipv4::BadOptionsLen; 299 for len in 0usize..48 { 300 if (len % 4 != 0) || len > 40 { 301 assert_eq!( 302 Err(BadOptionsLen { bad_len: len }), 303 Ipv4Options::try_from(&DATA[..len]) 304 ) 305 } 306 } 307 } 308 309 #[test] default()310 fn default() { 311 let actual: Ipv4Options = Default::default(); 312 assert_eq!(actual.len, 0); 313 assert_eq!(actual.buf, [0; 40]); 314 } 315 316 proptest! { 317 #[test] 318 fn clone_dbg(options in ipv4_options_any()) { 319 assert_eq!( 320 format!("{:?}", options), 321 format!("{:?}", options.clone().as_slice()) 322 ); 323 } 324 } 325 326 proptest! { 327 #[test] 328 fn eq_partial_eq( 329 a in ipv4_options_any(), 330 b in ipv4_options_any() 331 ) { 332 assert_eq!(a.eq(&b), a.as_slice().eq(b.as_slice())); 333 assert_eq!(a == b, a.as_slice() == b.as_slice()); 334 } 335 } 336 337 proptest! { 338 #[test] 339 fn hash( 340 options in ipv4_options_any() 341 ) { 342 use std::collections::hash_map::DefaultHasher; 343 use core::hash::{Hash, Hasher}; 344 let a = { 345 let mut hasher = DefaultHasher::new(); 346 options.hash(&mut hasher); 347 hasher.finish() 348 }; 349 let b = { 350 let mut hasher = DefaultHasher::new(); 351 options.hash(&mut hasher); 352 hasher.finish() 353 }; 354 assert_eq!(a, b); 355 } 356 } 357 358 proptest! { 359 #[test] 360 fn ord_partial_ord( 361 a in ipv4_options_any(), 362 b in ipv4_options_any() 363 ) { 364 assert_eq!(a.cmp(&b), a.as_slice().cmp(&b.as_slice())); 365 assert_eq!(a.partial_cmp(&b), a.as_slice().partial_cmp(&b.as_slice())); 366 } 367 } 368 369 #[test] from_0_byte_array()370 fn from_0_byte_array() { 371 let options: Ipv4Options = [].into(); 372 assert_eq!(&options[..], &[]); 373 } 374 375 macro_rules! from_static_array_test { 376 ($func_name:ident, $x:expr) => { 377 #[test] 378 fn $func_name() { 379 { 380 let options: Ipv4Options = [$x; $x].into(); 381 assert_eq!(&options[..], &[$x; $x]); 382 } 383 assert_eq!(&Ipv4Options::from([$x; $x])[..], &[$x; $x]); 384 } 385 }; 386 } 387 388 from_static_array_test!(from_arr_4, 4); 389 from_static_array_test!(from_arr_8, 8); 390 from_static_array_test!(from_arr_12, 12); 391 from_static_array_test!(from_arr_16, 16); 392 from_static_array_test!(from_arr_20, 20); 393 from_static_array_test!(from_arr_24, 24); 394 from_static_array_test!(from_arr_28, 28); 395 from_static_array_test!(from_arr_32, 32); 396 from_static_array_test!(from_arr_36, 36); 397 from_static_array_test!(from_arr_40, 40); 398 399 proptest! { 400 #[test] 401 fn as_ref(options in ipv4_options_any()) { 402 // as object reference 403 { 404 let r: &Ipv4Options = options.as_ref(); 405 assert_eq!(r, &options); 406 } 407 // as slice reference 408 { 409 let r: &[u8] = options.as_ref(); 410 assert_eq!(r, options.as_slice()); 411 } 412 } 413 } 414 415 proptest! { 416 #[test] 417 fn as_mut(options in ipv4_options_any()) { 418 // as object reference 419 { 420 let mut o = options.clone(); 421 let r: &mut Ipv4Options = o.as_mut(); 422 if r.len() > 0 { 423 r[0] = 123; 424 assert_eq!(123, o.as_slice()[0]); 425 } 426 } 427 // as slice reference 428 { 429 let mut o = options.clone(); 430 let r: &mut [u8] = o.as_mut(); 431 if r.len() > 0 { 432 r[0] = 123; 433 assert_eq!(123, o.as_slice()[0]); 434 } 435 } 436 } 437 } 438 439 proptest! { 440 #[test] 441 fn borrow(options in ipv4_options_any()) { 442 // as object reference 443 { 444 let r: &Ipv4Options = options.borrow(); 445 assert_eq!(r, &options); 446 } 447 // as slice reference 448 { 449 let r: &[u8] = options.borrow(); 450 assert_eq!(r, options.as_slice()); 451 } 452 } 453 } 454 455 proptest! { 456 #[test] 457 fn borrow_mut(options in ipv4_options_any()) { 458 // as object reference 459 { 460 let mut o = options.clone(); 461 let r: &mut Ipv4Options = o.borrow_mut(); 462 if r.len() > 0 { 463 r[0] = 123; 464 assert_eq!(123, o.as_slice()[0]); 465 } 466 } 467 // as slice reference 468 { 469 let mut o = options.clone(); 470 let r: &mut [u8] = o.borrow_mut(); 471 if r.len() > 0 { 472 r[0] = 123; 473 assert_eq!(123, o.as_slice()[0]); 474 } 475 } 476 } 477 } 478 479 #[test] deref()480 fn deref() { 481 let options: Ipv4Options = [1, 2, 3, 4].into(); 482 let s: &[u8] = &options; 483 assert_eq!(s, &[1, 2, 3, 4]); 484 assert_eq!(&options[..], &[1, 2, 3, 4]); 485 } 486 487 #[test] deref_mut()488 fn deref_mut() { 489 let mut options: Ipv4Options = [1, 2, 3, 4].into(); 490 let s: &mut [u8] = &mut options; 491 assert_eq!(s, &[1, 2, 3, 4]); 492 } 493 } 494