1 /// Helper for calculating the sum of all 16 bit words checksums used in 2 /// in checksum fields in TCP and UDP headers. 3 #[derive(Clone, Debug, Default, Eq, PartialEq)] 4 pub struct Sum16BitWords { 5 /// Partial sum 6 #[cfg(target_pointer_width = "64")] 7 sum: u64, 8 9 /// Partial sum 10 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] 11 sum: u32, 12 } 13 14 impl Sum16BitWords { new() -> Sum16BitWords15 pub fn new() -> Sum16BitWords { 16 Sum16BitWords { sum: 0 } 17 } 18 19 /// Add the given slice to the checksum. In case the slice 20 /// has a length that is not multiple of 2 the last byte 21 /// will be padded with 0. 22 #[inline] 23 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] add_slice(self, slice: &[u8]) -> Sum16BitWords24 pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { 25 Sum16BitWords { 26 sum: u32_16bit_word::add_slice(self.sum, slice), 27 } 28 } 29 30 /// Add the given slice to the checksum. In case the slice 31 /// has a length that is not multiple of 2 the last byte 32 /// will be padded with 0. 33 #[inline] 34 #[cfg(target_pointer_width = "64")] add_slice(self, slice: &[u8]) -> Sum16BitWords35 pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { 36 Sum16BitWords { 37 sum: u64_16bit_word::add_slice(self.sum, slice), 38 } 39 } 40 41 /// Add a 2 byte word. 42 #[inline] 43 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] add_2bytes(self, value: [u8; 2]) -> Sum16BitWords44 pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords { 45 Sum16BitWords { 46 sum: u32_16bit_word::add_2bytes(self.sum, value), 47 } 48 } 49 50 /// Add a 2 byte word. 51 #[inline] 52 #[cfg(target_pointer_width = "64")] add_2bytes(self, value: [u8; 2]) -> Sum16BitWords53 pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords { 54 Sum16BitWords { 55 sum: u64_16bit_word::add_2bytes(self.sum, value), 56 } 57 } 58 59 /// Add a 4 byte word. 60 #[inline] 61 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords62 pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords { 63 Sum16BitWords { 64 sum: u32_16bit_word::add_4bytes(self.sum, value), 65 } 66 } 67 68 /// Add a 4 byte word. 69 #[inline] 70 #[cfg(target_pointer_width = "64")] add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords71 pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords { 72 Sum16BitWords { 73 sum: u64_16bit_word::add_4bytes(self.sum, value), 74 } 75 } 76 77 /// Add a 8 byte word. 78 #[inline] 79 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords80 pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords { 81 self.add_4bytes([value[0], value[1], value[2], value[3]]) 82 .add_4bytes([value[4], value[5], value[6], value[7]]) 83 } 84 85 /// Add a 8 byte word. 86 #[inline] 87 #[cfg(target_pointer_width = "64")] add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords88 pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords { 89 Sum16BitWords { 90 sum: u64_16bit_word::add_8bytes(self.sum, value), 91 } 92 } 93 94 /// Add a 16 bytes. 95 #[inline] add_16bytes(&mut self, value: [u8; 16]) -> Sum16BitWords96 pub fn add_16bytes(&mut self, value: [u8; 16]) -> Sum16BitWords { 97 self.add_8bytes([ 98 value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], 99 ]) 100 .add_8bytes([ 101 value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15], 102 ]) 103 } 104 105 /// Converts summed up words from an u32 to an u16 ones complement 106 /// which can be used in a ipv4 checksum. 107 #[inline] 108 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] ones_complement(&self) -> u16109 pub fn ones_complement(&self) -> u16 { 110 u32_16bit_word::ones_complement(self.sum) 111 } 112 113 /// Converts summed up words from an u32 to an u16 ones complement 114 /// which can be used in a ipv4 checksum. 115 #[inline] 116 #[cfg(target_pointer_width = "64")] ones_complement(&self) -> u16117 pub fn ones_complement(&self) -> u16 { 118 u64_16bit_word::ones_complement(self.sum) 119 } 120 121 /// Converts summed up words from an u32 to an u16 ones complement 122 /// with 0 being replaced by 0xffff (useful for TCP and UDP). 123 /// 124 /// This kind of checksum is used in TCP and UDP headers. 125 #[inline] 126 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] to_ones_complement_with_no_zero(&self) -> u16127 pub fn to_ones_complement_with_no_zero(&self) -> u16 { 128 u32_16bit_word::ones_complement_with_no_zero(self.sum) 129 } 130 131 /// Converts summed up words from an u32 to an u16 ones complement 132 /// with 0 being replaced by 0xffff (useful for TCP and UDP). 133 /// 134 /// This kind of checksum is used in TCP and UDP headers. 135 #[inline] 136 #[cfg(target_pointer_width = "64")] to_ones_complement_with_no_zero(&self) -> u16137 pub fn to_ones_complement_with_no_zero(&self) -> u16 { 138 u64_16bit_word::ones_complement_with_no_zero(self.sum) 139 } 140 } 141 142 #[cfg(test)] 143 mod sum16_bit_words_tests { 144 use super::*; 145 use alloc::format; 146 147 #[test] new()148 fn new() { 149 assert_eq!(0xffff, Sum16BitWords::new().ones_complement()); 150 } 151 152 #[test] add_slice()153 fn add_slice() { 154 assert_eq!( 155 !u16::from_ne_bytes([0x12, 0x34]), 156 Sum16BitWords::new() 157 .add_slice(&[0x12, 0x34]) 158 .ones_complement() 159 ); 160 } 161 162 #[test] add_2bytes()163 fn add_2bytes() { 164 assert_eq!( 165 !u16::from_ne_bytes([0xf0, 0x0f]), 166 Sum16BitWords::new() 167 .add_2bytes([0xf0, 0x0f]) 168 .ones_complement() 169 ); 170 } 171 172 #[test] add_4bytes()173 fn add_4bytes() { 174 assert_eq!( 175 !(u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78])), 176 Sum16BitWords::new() 177 .add_4bytes([0x12, 0x34, 0x56, 0x78]) 178 .ones_complement() 179 ); 180 } 181 182 #[test] add_8bytes()183 fn add_8bytes() { 184 assert_eq!( 185 !(u16::from_ne_bytes([0x12, 0x34]) 186 + u16::from_ne_bytes([0x56, 0x78]) 187 + u16::from_ne_bytes([0x23, 0x22]) 188 + u16::from_ne_bytes([0x34, 0x11])), 189 Sum16BitWords::new() 190 .add_8bytes([0x12, 0x34, 0x56, 0x78, 0x23, 0x22, 0x34, 0x11]) 191 .ones_complement() 192 ); 193 } 194 195 #[test] add_16bytes()196 fn add_16bytes() { 197 assert_eq!( 198 u32_16bit_word::ones_complement(u32_16bit_word::add_4bytes( 199 u32_16bit_word::add_4bytes( 200 u32_16bit_word::add_4bytes( 201 u32_16bit_word::add_4bytes(0, [0x12, 0x34, 0x56, 0x78]), 202 [0x9a, 0xbc, 0xde, 0xf0] 203 ), 204 [0x0f, 0xed, 0xcb, 0xa9] 205 ), 206 [0x87, 0x65, 0x43, 0x21] 207 )), 208 Sum16BitWords::new() 209 .add_16bytes([ 210 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 211 0x65, 0x43, 0x21, 212 ]) 213 .ones_complement() 214 ); 215 } 216 217 #[test] ones_complement()218 fn ones_complement() { 219 assert_eq!( 220 !u16::from_ne_bytes([0xf0, 0x0f]), 221 Sum16BitWords::new() 222 .add_2bytes([0xf0, 0x0f]) 223 .ones_complement() 224 ); 225 } 226 227 #[test] to_ones_complement_with_no_zero()228 fn to_ones_complement_with_no_zero() { 229 // normal case 230 assert_eq!( 231 !u16::from_ne_bytes([0xf0, 0x0f]), 232 Sum16BitWords::new() 233 .add_2bytes([0xf0, 0x0f]) 234 .to_ones_complement_with_no_zero() 235 ); 236 237 // zero case 238 assert_eq!( 239 0xffffu16, 240 Sum16BitWords::new() 241 // ones complement would result in 0 242 // will be converted to 0xffff as 0 243 // is a reserved value 244 .add_2bytes([0xff, 0xff]) 245 .to_ones_complement_with_no_zero() 246 ); 247 } 248 249 #[test] debug()250 fn debug() { 251 let input = Sum16BitWords::new(); 252 assert_eq!( 253 &format!("Sum16BitWords {{ sum: {} }}", input.sum), 254 &format!("{:?}", input) 255 ); 256 } 257 258 #[test] default()259 fn default() { 260 let d: Sum16BitWords = Default::default(); 261 assert_eq!(d.sum, 0); 262 } 263 264 #[test] clone_eq()265 fn clone_eq() { 266 let value = Sum16BitWords::new(); 267 assert_eq!(value.clone(), value) 268 } 269 } 270 271 /// Helper functions for calculating a 16 bit checksum using 272 /// a u32 to sum up all values. 273 pub mod u32_16bit_word { 274 275 /// Add a 4 byte word. 276 #[inline] add_4bytes(start: u32, value: [u8; 4]) -> u32277 pub fn add_4bytes(start: u32, value: [u8; 4]) -> u32 { 278 let (sum, carry) = start.overflowing_add(u32::from_ne_bytes(value)); 279 sum + (carry as u32) 280 } 281 282 /// Add a 2 byte word. 283 #[inline] add_2bytes(start: u32, value: [u8; 2]) -> u32284 pub fn add_2bytes(start: u32, value: [u8; 2]) -> u32 { 285 let (sum, carry) = start.overflowing_add(u32::from(u16::from_ne_bytes(value))); 286 sum + (carry as u32) 287 } 288 289 /// Add the given slice to the checksum. In case the slice 290 /// has a length that is not multiple of 2 the last byte 291 /// will be padded with 0. 292 #[inline] add_slice(start_sum: u32, slice: &[u8]) -> u32293 pub fn add_slice(start_sum: u32, slice: &[u8]) -> u32 { 294 let mut sum: u32 = start_sum; 295 296 // sum up all 4 byte values 297 let end_32 = slice.len() - (slice.len() % 4); 298 for i in (0..end_32).step_by(4) { 299 sum = add_4bytes( 300 sum, 301 // SAFETY: 302 // Guranteed to always have at least 4 bytes to read 303 // from i. As end_32 is gurenateed to be a multiple of 304 // 4 bytes with a size equal or less then slice.len(). 305 unsafe { 306 [ 307 *slice.get_unchecked(i), 308 *slice.get_unchecked(i + 1), 309 *slice.get_unchecked(i + 2), 310 *slice.get_unchecked(i + 3), 311 ] 312 }, 313 ); 314 } 315 316 // in case 2 bytes are left add them as an word 317 if slice.len() - end_32 >= 2 { 318 sum = add_2bytes( 319 sum, 320 // SAFETY: 321 // If check guarantees there to be at least 322 // 2 bytes. 323 unsafe { 324 [ 325 *slice.get_unchecked(end_32), 326 *slice.get_unchecked(end_32 + 1), 327 ] 328 }, 329 ); 330 } 331 332 // unaligned end pad the last byte with 333 if 0 != slice.len() % 2 { 334 sum = add_2bytes( 335 sum, 336 // SAFETY: 337 // If check guarantees there to be at least 338 // 2 bytes. 339 unsafe { [*slice.get_unchecked(slice.len() - 1), 0] }, 340 ); 341 } 342 343 // done 344 sum 345 } 346 347 /// Converts summed up words from an u32 to an u16 with 0 being replaced by 0xffff (useful 348 /// for TCP and UDP headers). 349 /// 350 /// This kind of checksum is used in TCP and udp headers. 351 #[inline] ones_complement_with_no_zero(sum: u32) -> u16352 pub fn ones_complement_with_no_zero(sum: u32) -> u16 { 353 // In case of 0 use the ones complement (zero is reserved 354 // value for no checksum). 355 let u16value = ones_complement(sum); 356 if u16value == 0 { 357 0xffff 358 } else { 359 u16value 360 } 361 } 362 363 /// Converts summed up words from an u32 to an u16 which can be used in a ipv4. 364 #[inline] ones_complement(sum: u32) -> u16365 pub fn ones_complement(sum: u32) -> u16 { 366 // Add the upper 16 bits to the lower 16 bits twice. 367 // 368 // Notes: Two carry adds are needed as the first one could 369 // result in an additional carry add. 370 let first = ((sum >> 16) & 0xffff) + (sum & 0xffff); 371 let u16value = (((first >> 16) & 0xffff) + (first & 0xffff)) as u16; 372 373 // switch back to big endian (allows to use 374 // native endinaess during calculations). 375 !u16value 376 } 377 378 #[cfg(test)] 379 mod tests { 380 use super::*; 381 382 #[test] add_4bytes_test()383 fn add_4bytes_test() { 384 // trivial case 385 assert_eq!(0, add_4bytes(0, [0, 0, 0, 0])); 386 // check that the carry gets added 387 assert_eq!( 388 0xffff_ffff, // normal overflow would result in 0xffff_fffe 389 add_4bytes(0xffff_ffff, [0xff, 0xff, 0xff, 0xff]) 390 ); 391 // non max & min values 392 assert_eq!( 393 0x1234_5678 + u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89]), 394 add_4bytes(0x1234_5678, [0x23, 0x45, 0x67, 0x89]) 395 ); 396 } 397 398 #[test] add_2bytes_test()399 fn add_2bytes_test() { 400 // trivial case 401 assert_eq!(0, add_2bytes(0, [0, 0])); 402 // check that the carry gets added 403 assert_eq!( 404 0x0000_ffff, // normal overflow would result in 0x10000fffe 405 add_2bytes(0xffff_ffff, [0xff, 0xff]) 406 ); 407 // non max & min values 408 assert_eq!( 409 0x1234_5678 + u32::from(u16::from_ne_bytes([0x23, 0x45])), 410 add_2bytes(0x1234_5678, [0x23, 0x45]) 411 ); 412 } 413 414 #[test] add_slice_test()415 fn add_slice_test() { 416 // empty 417 assert_eq!(0x1234, add_slice(0x1234, &[])); 418 419 // aligned 420 assert_eq!( 421 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) 422 + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) 423 + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) 424 + u32::from_ne_bytes([0x1d, 0x1e, 0x1f, 0x10]), 425 add_slice( 426 0x1, 427 &[ 428 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 429 0x1d, 0x1e, 0x1f, 0x10, 430 ] 431 ) 432 ); 433 434 // aligned with carry 435 assert_eq!( 436 0x1 + 437 0x3 + // expected carry 438 u32::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0]).wrapping_add( 439 u32::from_ne_bytes([0xf2, 0x12, 0x11, 0xf1]).wrapping_add( 440 u32::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2]).wrapping_add( 441 u32::from_ne_bytes([0xf4, 0x14, 0x13, 0xf3]) 442 ) 443 ) 444 ), 445 add_slice( 446 0x1, 447 &[ 448 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 449 0xf4, 0x14, 0x13, 0xf3, 450 ] 451 ) 452 ); 453 454 // 1 byte unalgined 455 assert_eq!( 456 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) 457 + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) 458 + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) 459 + u32::from(u16::from_ne_bytes([0x1d, 0x1e])) 460 + u32::from(u16::from_ne_bytes([0x1f, 0x00])), 461 add_slice( 462 0x1, 463 &[ 464 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 465 0x1d, 0x1e, 0x1f, 466 ] 467 ) 468 ); 469 470 // 2 byte unaligned 471 assert_eq!( 472 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) 473 + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) 474 + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) 475 + u32::from(u16::from_ne_bytes([0x1d, 0x1e])), 476 add_slice( 477 0x1, 478 &[ 479 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 480 0x1d, 0x1e, 481 ] 482 ) 483 ); 484 485 // 4 byte unaligned 486 assert_eq!( 487 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) 488 + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) 489 + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) 490 + u32::from(u16::from_ne_bytes([0x1d, 0x00])), 491 add_slice( 492 0x1, 493 &[ 494 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 495 0x1d, 496 ] 497 ) 498 ); 499 } 500 501 #[test] ones_complement_with_no_zero_test()502 fn ones_complement_with_no_zero_test() { 503 // zero case 504 assert_eq!(0xffff, ones_complement_with_no_zero(0)); 505 506 // 0xffff should stay 0xffff (0 is reserved for no checksum) 507 assert_eq!(0xffff, ones_complement_with_no_zero(0xffff)); 508 509 // big endian conversion check 510 assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),); 511 512 // add of the upper and lower 16 bits without a carry 513 assert_eq!( 514 !(0x2345u16 + 0x1234), 515 ones_complement_with_no_zero(0x2345_1234), 516 ); 517 518 // add which in itself will again produce a carry 519 assert_eq!( 520 !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16), 521 ones_complement_with_no_zero(0x1456_f123), 522 ); 523 } 524 525 #[test] ones_complement_test()526 fn ones_complement_test() { 527 // zero case 528 assert_eq!(0xffff, ones_complement(0)); 529 530 // check that zero is not reserved 531 assert_eq!(0, ones_complement(0xffff)); 532 533 // big endian conversion check 534 assert_eq!(!0x1234u16, ones_complement(0x1234),); 535 536 // add of the upper and lower 16 bits without a carry 537 assert_eq!(!(0x2345u16 + 0x1234u16), ones_complement(0x2345_1234),); 538 539 // add which in itself will again produce a carry 540 assert_eq!( 541 !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16), 542 ones_complement(0x1456_f123), 543 ); 544 } 545 } 546 } 547 548 /// Helper functions for calculating a 16 bit checksum using 549 /// a u64 to sum up all values. 550 pub mod u64_16bit_word { 551 552 /// Add a 8 byte word. 553 #[inline] add_8bytes(start: u64, value: [u8; 8]) -> u64554 pub fn add_8bytes(start: u64, value: [u8; 8]) -> u64 { 555 let (sum, carry) = start.overflowing_add(u64::from_ne_bytes(value)); 556 sum + (carry as u64) 557 } 558 559 /// Add a 4 byte word. 560 #[inline] add_4bytes(start: u64, value: [u8; 4]) -> u64561 pub fn add_4bytes(start: u64, value: [u8; 4]) -> u64 { 562 let (sum, carry) = start.overflowing_add(u64::from(u32::from_ne_bytes(value))); 563 sum + (carry as u64) 564 } 565 566 /// Add a 2 byte word. 567 #[inline] add_2bytes(start: u64, value: [u8; 2]) -> u64568 pub fn add_2bytes(start: u64, value: [u8; 2]) -> u64 { 569 let (sum, carry) = start.overflowing_add(u64::from(u16::from_ne_bytes(value))); 570 sum + (carry as u64) 571 } 572 573 /// Add the given slice to the checksum. In case the slice 574 /// has a length that is not multiple of 2 the last byte 575 /// will be padded with 0. 576 #[inline] add_slice(start_sum: u64, slice: &[u8]) -> u64577 pub fn add_slice(start_sum: u64, slice: &[u8]) -> u64 { 578 let mut sum: u64 = start_sum; 579 580 // sum up all 4 byte values 581 let end_64 = slice.len() - (slice.len() % 8); 582 for i in (0..end_64).step_by(8) { 583 sum = add_8bytes( 584 sum, 585 // SAFETY: 586 // Guranteed to always have at least 8 bytes to read 587 // from i. As end_64 is gurenateed to be a multiple of 588 // 8 bytes with a size equal or less then slice.len(). 589 unsafe { 590 [ 591 *slice.get_unchecked(i), 592 *slice.get_unchecked(i + 1), 593 *slice.get_unchecked(i + 2), 594 *slice.get_unchecked(i + 3), 595 *slice.get_unchecked(i + 4), 596 *slice.get_unchecked(i + 5), 597 *slice.get_unchecked(i + 6), 598 *slice.get_unchecked(i + 7), 599 ] 600 }, 601 ); 602 } 603 604 // in case 4 or more bytes are left add the first 4 bytes 605 let end_32 = if slice.len() - end_64 >= 4 { 606 sum = add_4bytes( 607 sum, 608 // SAFETY: 609 // If check guarantees there to be at least 610 // 2 bytes. 611 unsafe { 612 [ 613 *slice.get_unchecked(end_64), 614 *slice.get_unchecked(end_64 + 1), 615 *slice.get_unchecked(end_64 + 2), 616 *slice.get_unchecked(end_64 + 3), 617 ] 618 }, 619 ); 620 621 // shift by 4 622 end_64 + 4 623 } else { 624 end_64 625 }; 626 627 // in case 2 bytes are left add them as an word 628 if slice.len() - end_32 >= 2 { 629 sum = add_2bytes( 630 sum, 631 // SAFETY: 632 // If check guarantees there to be at least 633 // 2 bytes. 634 unsafe { 635 [ 636 *slice.get_unchecked(end_32), 637 *slice.get_unchecked(end_32 + 1), 638 ] 639 }, 640 ); 641 } 642 643 // unaligned end pad the last byte with 644 if 0 != slice.len() % 2 { 645 sum = add_2bytes( 646 sum, 647 // SAFETY: 648 // If check guarantees there to be at least 649 // 2 bytes. 650 unsafe { [*slice.get_unchecked(slice.len() - 1), 0] }, 651 ); 652 } 653 654 // done 655 sum 656 } 657 658 /// Converts summed up words from an u64 to an u16 with 0 being replaced by 0xffff (useful 659 /// for TCP and UDP headers). 660 /// 661 /// This kind of checksum is used in TCP and udp headers. 662 #[inline] ones_complement_with_no_zero(sum: u64) -> u16663 pub fn ones_complement_with_no_zero(sum: u64) -> u16 { 664 // In case of 0 use the ones complement (zero is reserved 665 // value for no checksum). 666 let u16value = ones_complement(sum); 667 if u16value == 0 { 668 0xffff 669 } else { 670 u16value 671 } 672 } 673 674 /// Converts summed up words from an u64 to an u16 which can be used in a ipv4. 675 #[inline] ones_complement(sum: u64) -> u16676 pub fn ones_complement(sum: u64) -> u16 { 677 let first = ((sum >> 48) & 0xffff) 678 + ((sum >> 32) & 0xffff) 679 + ((sum >> 16) & 0xffff) 680 + (sum & 0xffff); 681 // Add the upper 16 bits to the lower 16 bits twice. 682 // 683 // Notes: Two carry adds are needed as the first one could 684 // result in an additional carry add. 685 let second = ((first >> 16) & 0xffff) + (first & 0xffff); 686 let u16value = (((second >> 16) & 0xffff) + (second & 0xffff)) as u16; 687 688 // switch back to big endian (allows to use 689 // native endinaess during calculations). 690 !u16value 691 } 692 693 #[cfg(test)] 694 mod tests { 695 use super::*; 696 use proptest::prelude::*; 697 698 #[test] add_8bytes_test()699 fn add_8bytes_test() { 700 // trivial case 701 assert_eq!(0, add_8bytes(0, [0, 0, 0, 0, 0, 0, 0, 0])); 702 // check that the carry gets added 703 assert_eq!( 704 0xffff_ffff_ffff_ffff, // normal overflow would result in 0xffff_ffff_ffff_fffe 705 add_8bytes( 706 0xffff_ffff_ffff_ffff, 707 [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] 708 ) 709 ); 710 // non max & min values 711 assert_eq!( 712 0x1234_5678_1234_5678 713 + u64::from_ne_bytes([0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44]), 714 add_8bytes( 715 0x1234_5678_1234_5678, 716 [0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44] 717 ) 718 ); 719 } 720 721 #[test] add_4bytes_test()722 fn add_4bytes_test() { 723 // trivial case 724 assert_eq!(0, add_4bytes(0, [0, 0, 0, 0])); 725 // check that the carry gets added 726 assert_eq!( 727 0xffff_ffff, // normal overflow would result in 0xffff_fffe 728 add_4bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff, 0xff, 0xff]) 729 ); 730 // non max & min values 731 assert_eq!( 732 0x1234_5678_1234_5678 + u64::from(u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89])), 733 add_4bytes(0x1234_5678_1234_5678, [0x23, 0x45, 0x67, 0x89]) 734 ); 735 } 736 737 #[test] add_2bytes_test()738 fn add_2bytes_test() { 739 // trivial case 740 assert_eq!(0, add_2bytes(0, [0, 0])); 741 // check that the carry gets added 742 assert_eq!( 743 0xffff, // normal overflow would result in 0xfffe 744 add_2bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff]) 745 ); 746 // non max & min values 747 assert_eq!( 748 0x9876_0123_1234_5678 + u64::from(u16::from_ne_bytes([0x23, 0x45])), 749 add_2bytes(0x9876_0123_1234_5678, [0x23, 0x45]) 750 ); 751 } 752 753 #[test] add_slice_test()754 fn add_slice_test() { 755 // empty 756 assert_eq!(0x1234, add_slice(0x1234, &[])); 757 758 // aligned 759 assert_eq!( 760 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 761 + u64::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10]), 762 add_slice( 763 0x1, 764 &[ 765 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 766 0x1d, 0x1e, 0x1f, 0x10, 767 ] 768 ) 769 ); 770 771 // aligned with carry 772 assert_eq!( 773 0x1 + 774 0x1 + // expected carry 775 u64::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1]).wrapping_add( 776 u64::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3]) 777 ), 778 add_slice( 779 0x1, 780 &[ 781 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 782 0xf4, 0x14, 0x13, 0xf3, 783 ] 784 ) 785 ); 786 787 // unaligned access 788 { 789 let base_data = [ 790 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 791 0x1e, 0x1f, 0x00, 792 ]; 793 794 // 1 byte unaligned 795 assert_eq!( 796 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 797 + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) 798 + u64::from(u16::from_ne_bytes([0x1d, 0x1e])) 799 + u64::from(u16::from_ne_bytes([0x1f, 0x00])), 800 add_slice(0x1, &base_data[..base_data.len() - 1]) 801 ); 802 803 // 2 byte unaligned 804 assert_eq!( 805 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 806 + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) 807 + u64::from(u16::from_ne_bytes([0x1d, 0x1e])), 808 add_slice(0x1, &base_data[..base_data.len() - 2]) 809 ); 810 811 // 3 byte unaligned 812 assert_eq!( 813 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 814 + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) 815 + u64::from(u16::from_ne_bytes([0x1d, 0x00])), 816 add_slice(0x1, &base_data[..base_data.len() - 3]) 817 ); 818 819 // 4 byte unaligned 820 assert_eq!( 821 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 822 + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])), 823 add_slice(0x1, &base_data[..base_data.len() - 4]) 824 ); 825 826 // 5 byte unaligned 827 assert_eq!( 828 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 829 + u64::from(u16::from_ne_bytes([0x19, 0x1a])) 830 + u64::from(u16::from_ne_bytes([0x1b, 0x00])), 831 add_slice(0x1, &base_data[..base_data.len() - 5]) 832 ); 833 834 // 6 byte unaligned 835 assert_eq!( 836 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 837 + u64::from(u16::from_ne_bytes([0x19, 0x1a])), 838 add_slice(0x1, &base_data[..base_data.len() - 6]) 839 ); 840 841 // 6 byte unaligned 842 assert_eq!( 843 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) 844 + u64::from(u16::from_ne_bytes([0x19, 0x00])), 845 add_slice(0x1, &base_data[..base_data.len() - 7]) 846 ); 847 } 848 } 849 850 #[test] ones_complement_with_no_zero_test()851 fn ones_complement_with_no_zero_test() { 852 // zero case 853 assert_eq!(0xffff, ones_complement_with_no_zero(0)); 854 855 // 0xffff should stay 0xffff (0 is reserved for no checksum) 856 assert_eq!(0xffff, ones_complement_with_no_zero(0xffff)); 857 858 // big endian conversion check 859 assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),); 860 861 // add of the upper and lower 16 bits without a carry 862 assert_eq!( 863 !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16), 864 ones_complement_with_no_zero(0x2456_1345_2345_1234), 865 ); 866 867 // add which in itself will again produce two as carry 868 assert_eq!( 869 !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16), 870 ones_complement_with_no_zero(0x1234_f234_1456_f123), 871 ); 872 } 873 874 #[test] ones_complement_test()875 fn ones_complement_test() { 876 // zero case 877 assert_eq!(0xffff, ones_complement(0)); 878 879 // check that zero is not reserved 880 assert_eq!(0, ones_complement(0xffff)); 881 882 // big endian conversion check 883 assert_eq!(!0x1234u16, ones_complement(0x1234),); 884 885 // add of the upper and lower 16 bits without a carry 886 assert_eq!( 887 !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16), 888 ones_complement(0x2456_1345_2345_1234), 889 ); 890 891 // add which in itself will again produce two as carry 892 assert_eq!( 893 !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16), 894 ones_complement(0x1234_f234_1456_f123), 895 ); 896 897 // will result in a first 16bit sum that will have to be 898 // carry added twice 899 assert_eq!(!1, ones_complement(0x02f6_e312_7fd7_9a20),); 900 } 901 902 proptest! { 903 #[test] 904 #[cfg_attr(miri, ignore)] // vec allocation reduces miri runspeed too much 905 fn u32_u16_comparison( 906 data in proptest::collection::vec(any::<u8>(), 0..0xfffusize) 907 ) { 908 use crate::checksum::*; 909 910 let u32_oc = u32_16bit_word::ones_complement( 911 u32_16bit_word::add_slice(0, &data) 912 ); 913 let u64_oc = u64_16bit_word::ones_complement( 914 u64_16bit_word::add_slice(0, &data) 915 ); 916 assert_eq!(u32_oc, u64_oc); 917 918 let struct_oc = Sum16BitWords::new() 919 .add_slice(&data) 920 .ones_complement(); 921 assert_eq!(u32_oc, struct_oc); 922 } 923 } 924 } 925 } 926