1pub const V4_CONFIG_STATIC_LEN: usize = 10;
17pub const V6_CONFIG_STATIC_LEN: usize = 34;
19pub const V4_ADDRESS_LEN: usize = 9;
22pub const V6_ADDRESS_ITEM_LEN: usize = 18;
24pub const V6_ROUTER_ITEM_LEN: usize = 17;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum IpError {
30 Truncated,
32 Malformed,
34 NotWellFormed,
39 BufferTooSmall,
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub enum FamilyState {
47 Disabled = 0,
49 #[default]
51 NoLink = 1,
52 Waiting = 2,
55 Ready = 3,
58 Conflict = 4,
62}
63
64impl FamilyState {
65 pub const fn code(self) -> u8 {
66 self as u8
67 }
68
69 pub const fn from_code(code: u8) -> Option<Self> {
70 match code {
71 0 => Some(Self::Disabled),
72 1 => Some(Self::NoLink),
73 2 => Some(Self::Waiting),
74 3 => Some(Self::Ready),
75 4 => Some(Self::Conflict),
76 _ => None,
77 }
78 }
79
80 pub const fn is_ready(self) -> bool {
82 matches!(self, Self::Ready)
83 }
84}
85
86#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88pub enum Method {
89 Disabled = 0,
91 #[default]
94 Auto = 1,
95 Static = 2,
98}
99
100impl Method {
101 pub const fn code(self) -> u8 {
102 self as u8
103 }
104
105 pub const fn from_code(code: u8) -> Option<Self> {
106 match code {
107 0 => Some(Self::Disabled),
108 1 => Some(Self::Auto),
109 2 => Some(Self::Static),
110 _ => None,
111 }
112 }
113}
114
115pub const fn ipv4_is_usable_host(address: &[u8; 4]) -> bool {
123 let [a, b, _, _] = *address;
124 if a == 0 && b == 0 && address[2] == 0 && address[3] == 0 {
125 return false; }
127 if a == 127 {
128 return false; }
130 if a == 169 && b == 254 {
131 return false; }
133 if a >= 224 && a <= 239 {
134 return false; }
136 if a == 255 && b == 255 && address[2] == 255 && address[3] == 255 {
137 return false; }
139 true
140}
141
142pub fn ipv6_is_usable_host(address: &[u8; 16]) -> bool {
145 if address.iter().all(|&b| b == 0) {
146 return false; }
148 if address[..15].iter().all(|&b| b == 0) && address[15] == 1 {
149 return false; }
151 if address[0] == 0xFF {
152 return false; }
154 if address[0] == 0xFE && (address[1] & 0xC0) == 0x80 {
155 return false; }
157 true
158}
159
160pub const fn ipv6_is_link_local(address: &[u8; 16]) -> bool {
162 address[0] == 0xFE && (address[1] & 0xC0) == 0x80
163}
164
165pub const fn ipv4_gateway_is_valid(gateway: &[u8; 4]) -> bool {
168 if gateway[0] == 0 && gateway[1] == 0 && gateway[2] == 0 && gateway[3] == 0 {
169 return true;
170 }
171 ipv4_is_usable_host(gateway)
172}
173
174pub fn ipv6_gateway_is_valid(gateway: &[u8; 16]) -> bool {
180 if gateway.iter().all(|&b| b == 0) {
181 return true;
182 }
183 ipv6_is_usable_host(gateway) || ipv6_is_link_local(gateway)
184}
185
186pub fn resolver_is_valid(item: &[u8]) -> bool {
192 match item.len() {
193 4 => {
194 let mut address = [0u8; 4];
195 address.copy_from_slice(item);
196 ipv4_is_usable_host(&address)
197 }
198 16 => {
199 let mut address = [0u8; 16];
200 address.copy_from_slice(item);
201 ipv6_is_usable_host(&address) || ipv6_is_link_local(&address)
202 }
203 _ => false,
204 }
205}
206
207#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209pub struct V4Config {
210 pub method: Method,
211 pub address: [u8; 4],
213 pub prefix: u8,
215 pub gateway: [u8; 4],
218}
219
220impl Default for V4Config {
221 fn default() -> Self {
222 Self {
223 method: Method::Auto,
224 address: [0; 4],
225 prefix: 0,
226 gateway: [0; 4],
227 }
228 }
229}
230
231impl V4Config {
232 pub fn validate(&self) -> Result<(), IpError> {
234 if !matches!(self.method, Method::Static) {
235 return Ok(());
236 }
237 if self.prefix > 32 {
238 return Err(IpError::NotWellFormed);
239 }
240 if !ipv4_is_usable_host(&self.address) {
241 return Err(IpError::NotWellFormed);
242 }
243 if !ipv4_gateway_is_valid(&self.gateway) {
244 return Err(IpError::NotWellFormed);
245 }
246 Ok(())
247 }
248
249 pub fn encode(&self, out: &mut [u8]) -> Result<usize, IpError> {
250 let len = if matches!(self.method, Method::Static) {
251 V4_CONFIG_STATIC_LEN
252 } else {
253 1
254 };
255 if out.len() < len {
256 return Err(IpError::BufferTooSmall);
257 }
258 out[0] = self.method.code();
259 if len > 1 {
260 out[1..5].copy_from_slice(&self.address);
261 out[5] = self.prefix;
262 out[6..10].copy_from_slice(&self.gateway);
263 }
264 Ok(len)
265 }
266
267 pub fn decode(bytes: &[u8]) -> Result<Self, IpError> {
268 let method = Method::from_code(*bytes.first().ok_or(IpError::Truncated)?)
269 .ok_or(IpError::Malformed)?;
270 let mut config = Self {
271 method,
272 ..Self::default()
273 };
274 if matches!(method, Method::Static) {
275 if bytes.len() < V4_CONFIG_STATIC_LEN {
276 return Err(IpError::Truncated);
277 }
278 config.address.copy_from_slice(&bytes[1..5]);
279 config.prefix = bytes[5];
280 config.gateway.copy_from_slice(&bytes[6..10]);
281 }
282 Ok(config)
283 }
284}
285
286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
288pub struct V6Config {
289 pub method: Method,
290 pub address: [u8; 16],
291 pub prefix: u8,
293 pub gateway: [u8; 16],
295}
296
297impl Default for V6Config {
298 fn default() -> Self {
299 Self {
300 method: Method::Auto,
301 address: [0; 16],
302 prefix: 0,
303 gateway: [0; 16],
304 }
305 }
306}
307
308impl V6Config {
309 pub fn validate(&self) -> Result<(), IpError> {
316 if !matches!(self.method, Method::Static) {
317 return Ok(());
318 }
319 if self.prefix > 128 {
320 return Err(IpError::NotWellFormed);
321 }
322 if !ipv6_is_usable_host(&self.address) {
323 return Err(IpError::NotWellFormed);
324 }
325 if !ipv6_gateway_is_valid(&self.gateway) {
326 return Err(IpError::NotWellFormed);
327 }
328 Ok(())
329 }
330
331 pub fn encode(&self, out: &mut [u8]) -> Result<usize, IpError> {
332 let len = if matches!(self.method, Method::Static) {
333 V6_CONFIG_STATIC_LEN
334 } else {
335 1
336 };
337 if out.len() < len {
338 return Err(IpError::BufferTooSmall);
339 }
340 out[0] = self.method.code();
341 if len > 1 {
342 out[1..17].copy_from_slice(&self.address);
343 out[17] = self.prefix;
344 out[18..34].copy_from_slice(&self.gateway);
345 }
346 Ok(len)
347 }
348
349 pub fn decode(bytes: &[u8]) -> Result<Self, IpError> {
350 let method = Method::from_code(*bytes.first().ok_or(IpError::Truncated)?)
351 .ok_or(IpError::Malformed)?;
352 let mut config = Self {
353 method,
354 ..Self::default()
355 };
356 if matches!(method, Method::Static) {
357 if bytes.len() < V6_CONFIG_STATIC_LEN {
358 return Err(IpError::Truncated);
359 }
360 config.address.copy_from_slice(&bytes[1..17]);
361 config.prefix = bytes[17];
362 config.gateway.copy_from_slice(&bytes[18..34]);
363 }
364 Ok(config)
365 }
366}
367
368#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
370pub struct V4Address {
371 pub address: [u8; 4],
372 pub prefix: u8,
373 pub gateway: [u8; 4],
375}
376
377impl V4Address {
378 pub fn has_gateway(&self) -> bool {
380 self.gateway != [0; 4]
381 }
382
383 pub fn encode(&self, out: &mut [u8]) -> Result<usize, IpError> {
384 if out.len() < V4_ADDRESS_LEN {
385 return Err(IpError::BufferTooSmall);
386 }
387 out[0..4].copy_from_slice(&self.address);
388 out[4] = self.prefix;
389 out[5..9].copy_from_slice(&self.gateway);
390 Ok(V4_ADDRESS_LEN)
391 }
392
393 pub fn decode(bytes: &[u8]) -> Result<Self, IpError> {
394 if bytes.len() < V4_ADDRESS_LEN {
395 return Err(IpError::Truncated);
396 }
397 let mut value = Self::default();
398 value.address.copy_from_slice(&bytes[0..4]);
399 value.prefix = bytes[4];
400 value.gateway.copy_from_slice(&bytes[5..9]);
401 Ok(value)
402 }
403}
404
405#[derive(Clone, Copy, Debug, PartialEq, Eq)]
407pub enum V6ItemKind {
408 Address = 0,
410 Router = 1,
412}
413
414impl V6ItemKind {
415 pub const fn code(self) -> u8 {
416 self as u8
417 }
418
419 pub const fn from_code(code: u8) -> Option<Self> {
420 match code {
421 0 => Some(Self::Address),
422 1 => Some(Self::Router),
423 _ => None,
424 }
425 }
426}
427
428#[derive(Clone, Copy, Debug, PartialEq, Eq)]
430pub enum V6Item {
431 Address {
435 address: [u8; 16],
436 prefix: u8,
440 },
441 Router { address: [u8; 16] },
443}
444
445impl V6Item {
446 pub const fn kind(&self) -> V6ItemKind {
447 match self {
448 Self::Address { .. } => V6ItemKind::Address,
449 Self::Router { .. } => V6ItemKind::Router,
450 }
451 }
452
453 pub fn encode(&self, out: &mut [u8]) -> Result<usize, IpError> {
454 let len = match self {
455 Self::Address { .. } => V6_ADDRESS_ITEM_LEN,
456 Self::Router { .. } => V6_ROUTER_ITEM_LEN,
457 };
458 if out.len() < len {
459 return Err(IpError::BufferTooSmall);
460 }
461 out[0] = self.kind().code();
462 match self {
463 Self::Address { address, prefix } => {
464 out[1..17].copy_from_slice(address);
465 out[17] = *prefix;
466 }
467 Self::Router { address } => out[1..17].copy_from_slice(address),
468 }
469 Ok(len)
470 }
471
472 pub fn decode(bytes: &[u8]) -> Result<Self, IpError> {
473 let kind = V6ItemKind::from_code(*bytes.first().ok_or(IpError::Truncated)?)
474 .ok_or(IpError::Malformed)?;
475 let mut address = [0u8; 16];
476 match kind {
477 V6ItemKind::Address => {
478 if bytes.len() < V6_ADDRESS_ITEM_LEN {
479 return Err(IpError::Truncated);
480 }
481 address.copy_from_slice(&bytes[1..17]);
482 Ok(Self::Address {
483 address,
484 prefix: bytes[17],
485 })
486 }
487 V6ItemKind::Router => {
488 if bytes.len() < V6_ROUTER_ITEM_LEN {
489 return Err(IpError::Truncated);
490 }
491 address.copy_from_slice(&bytes[1..17]);
492 Ok(Self::Router { address })
493 }
494 }
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 const GLOBAL_V6: [u8; 16] = [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
503 const LINK_LOCAL_V6: [u8; 16] = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
504
505 #[test]
506 fn family_state_codes_round_trip_strictly() {
507 for code in 0..=4u8 {
508 let state = FamilyState::from_code(code).expect("allocated");
509 assert_eq!(state.code(), code);
510 }
511 assert_eq!(FamilyState::from_code(5), None);
512 assert!(FamilyState::Ready.is_ready());
513 assert!(!FamilyState::Waiting.is_ready());
514 assert!(!FamilyState::Conflict.is_ready());
515 }
516
517 #[test]
518 fn method_codes_round_trip_strictly() {
519 for code in 0..=2u8 {
520 assert_eq!(Method::from_code(code).expect("allocated").code(), code);
521 }
522 assert_eq!(Method::from_code(3), None);
523 assert_eq!(Method::default(), Method::Auto);
526 }
527
528 #[test]
529 fn a_link_local_v4_address_is_not_usable() {
530 assert!(!ipv4_is_usable_host(&[169, 254, 3, 4]));
533 assert!(ipv4_is_usable_host(&[169, 253, 3, 4]));
534 assert!(ipv4_is_usable_host(&[192, 168, 4, 1]));
535 assert!(ipv4_is_usable_host(&[8, 8, 8, 8]));
536 assert!(!ipv4_is_usable_host(&[0, 0, 0, 0]));
537 assert!(!ipv4_is_usable_host(&[127, 0, 0, 1]));
538 assert!(!ipv4_is_usable_host(&[224, 0, 0, 1]));
539 assert!(!ipv4_is_usable_host(&[239, 255, 255, 250]));
540 assert!(!ipv4_is_usable_host(&[255, 255, 255, 255]));
541 }
542
543 #[test]
544 fn a_link_local_v6_address_is_not_usable() {
545 assert!(ipv6_is_usable_host(&GLOBAL_V6));
546 assert!(!ipv6_is_usable_host(&LINK_LOCAL_V6));
547 assert!(ipv6_is_link_local(&LINK_LOCAL_V6));
548 assert!(!ipv6_is_link_local(&GLOBAL_V6));
549 assert!(!ipv6_is_usable_host(&[0; 16]));
550 let mut loopback = [0u8; 16];
551 loopback[15] = 1;
552 assert!(!ipv6_is_usable_host(&loopback));
553 let mut multicast = [0u8; 16];
554 multicast[0] = 0xFF;
555 assert!(!ipv6_is_usable_host(&multicast));
556 let mut high = LINK_LOCAL_V6;
558 high[1] = 0xBF;
559 assert!(ipv6_is_link_local(&high));
560 }
561
562 #[test]
563 fn only_the_v6_gateway_may_be_link_local() {
564 assert!(ipv6_gateway_is_valid(&LINK_LOCAL_V6));
567 assert!(ipv6_gateway_is_valid(&GLOBAL_V6));
568 assert!(ipv6_gateway_is_valid(&[0; 16]));
569 assert!(!ipv4_gateway_is_valid(&[169, 254, 0, 1]));
570 assert!(ipv4_gateway_is_valid(&[0, 0, 0, 0]));
571 assert!(ipv4_gateway_is_valid(&[192, 168, 4, 254]));
572 }
573
574 #[test]
575 fn a_resolver_is_four_or_sixteen_octets() {
576 assert!(resolver_is_valid(&[8, 8, 8, 8]));
577 assert!(resolver_is_valid(&GLOBAL_V6));
578 assert!(resolver_is_valid(&LINK_LOCAL_V6));
580 assert!(!resolver_is_valid(&[169, 254, 0, 1]));
581 assert!(!resolver_is_valid(&[0, 0, 0, 0]));
582 assert!(!resolver_is_valid(&[127, 0, 0, 1]));
583 assert!(!resolver_is_valid(&[]));
584 assert!(!resolver_is_valid(&[1, 2, 3]));
585 assert!(!resolver_is_valid(&[0; 5]));
586 }
587
588 #[test]
589 fn an_automatic_config_is_one_octet() {
590 let config = V4Config::default();
591 let mut buf = [0u8; V4_CONFIG_STATIC_LEN];
592 assert_eq!(config.encode(&mut buf), Ok(1));
593 assert_eq!(buf[0], Method::Auto.code());
594 assert_eq!(V4Config::decode(&buf[..1]), Ok(config));
595
596 let v6 = V6Config::default();
597 let mut buf = [0u8; V6_CONFIG_STATIC_LEN];
598 assert_eq!(v6.encode(&mut buf), Ok(1));
599 assert_eq!(V6Config::decode(&buf[..1]), Ok(v6));
600 }
601
602 #[test]
603 fn a_static_v4_config_round_trips() {
604 let config = V4Config {
605 method: Method::Static,
606 address: [192, 168, 1, 40],
607 prefix: 24,
608 gateway: [192, 168, 1, 1],
609 };
610 config.validate().expect("well formed");
611 let mut buf = [0u8; V4_CONFIG_STATIC_LEN];
612 assert_eq!(config.encode(&mut buf), Ok(V4_CONFIG_STATIC_LEN));
613 assert_eq!(V4Config::decode(&buf), Ok(config));
614 }
615
616 #[test]
617 fn a_static_v6_config_round_trips() {
618 let config = V6Config {
619 method: Method::Static,
620 address: GLOBAL_V6,
621 prefix: 64,
622 gateway: LINK_LOCAL_V6,
623 };
624 config.validate().expect("well formed");
625 let mut buf = [0u8; V6_CONFIG_STATIC_LEN];
626 assert_eq!(config.encode(&mut buf), Ok(V6_CONFIG_STATIC_LEN));
627 assert_eq!(V6Config::decode(&buf), Ok(config));
628 }
629
630 #[test]
631 fn a_static_config_is_validated_against_the_readiness_boundary() {
632 let base = V4Config {
633 method: Method::Static,
634 address: [192, 168, 1, 40],
635 prefix: 24,
636 gateway: [0; 4],
637 };
638 assert!(base.validate().is_ok());
639 assert!(
642 V4Config {
643 address: [169, 254, 1, 1],
644 ..base
645 }
646 .validate()
647 .is_err()
648 );
649 assert!(V4Config { prefix: 33, ..base }.validate().is_err());
650 assert!(
651 V4Config {
652 gateway: [224, 0, 0, 1],
653 ..base
654 }
655 .validate()
656 .is_err()
657 );
658 assert!(
660 V4Config {
661 method: Method::Auto,
662 address: [0; 4],
663 prefix: 99,
664 gateway: [0; 4],
665 }
666 .validate()
667 .is_ok()
668 );
669 assert!(
670 V6Config {
671 method: Method::Static,
672 address: GLOBAL_V6,
673 prefix: 129,
674 gateway: [0; 16],
675 }
676 .validate()
677 .is_err()
678 );
679 assert!(
680 V6Config {
681 method: Method::Static,
682 address: LINK_LOCAL_V6,
683 prefix: 64,
684 gateway: [0; 16],
685 }
686 .validate()
687 .is_err()
688 );
689 }
690
691 #[test]
692 fn a_v4_address_round_trips_with_and_without_a_gateway() {
693 let held = V4Address {
694 address: [10, 0, 0, 5],
695 prefix: 8,
696 gateway: [10, 0, 0, 1],
697 };
698 let mut buf = [0u8; V4_ADDRESS_LEN];
699 assert_eq!(held.encode(&mut buf), Ok(V4_ADDRESS_LEN));
700 assert_eq!(V4Address::decode(&buf), Ok(held));
701 assert!(held.has_gateway());
702
703 let isolated = V4Address {
706 gateway: [0; 4],
707 ..held
708 };
709 assert_eq!(isolated.encode(&mut buf), Ok(V4_ADDRESS_LEN));
710 assert_eq!(V4Address::decode(&buf), Ok(isolated));
711 assert!(!isolated.has_gateway());
712 }
713
714 #[test]
715 fn the_two_v6_item_kinds_round_trip() {
716 let address = V6Item::Address {
717 address: GLOBAL_V6,
718 prefix: 64,
719 };
720 let mut buf = [0u8; V6_ADDRESS_ITEM_LEN];
721 assert_eq!(address.encode(&mut buf), Ok(V6_ADDRESS_ITEM_LEN));
722 assert_eq!(V6Item::decode(&buf), Ok(address));
723 assert_eq!(address.kind(), V6ItemKind::Address);
724
725 let router = V6Item::Router {
726 address: LINK_LOCAL_V6,
727 };
728 assert_eq!(router.encode(&mut buf), Ok(V6_ROUTER_ITEM_LEN));
729 assert_eq!(V6Item::decode(&buf[..V6_ROUTER_ITEM_LEN]), Ok(router));
730 assert_eq!(router.kind(), V6ItemKind::Router);
731
732 let dhcpv6 = V6Item::Address {
734 address: GLOBAL_V6,
735 prefix: 128,
736 };
737 assert_eq!(dhcpv6.encode(&mut buf), Ok(V6_ADDRESS_ITEM_LEN));
738 assert_eq!(V6Item::decode(&buf), Ok(dhcpv6));
739 }
740
741 #[test]
742 fn unknown_codes_and_short_values_are_told_apart() {
743 assert_eq!(V4Config::decode(&[]), Err(IpError::Truncated));
744 assert_eq!(V4Config::decode(&[7]), Err(IpError::Malformed));
745 assert_eq!(
746 V4Config::decode(&[Method::Static.code(), 1, 2]),
747 Err(IpError::Truncated)
748 );
749 assert_eq!(V6Item::decode(&[]), Err(IpError::Truncated));
750 assert_eq!(V6Item::decode(&[9]), Err(IpError::Malformed));
751 assert_eq!(V6Item::decode(&[0, 1, 2]), Err(IpError::Truncated));
752 assert_eq!(V4Address::decode(&[0; 8]), Err(IpError::Truncated));
753 }
754
755 #[test]
756 fn encoders_report_a_buffer_too_small_rather_than_truncating() {
757 let mut small = [0u8; 4];
758 assert_eq!(
759 V4Config {
760 method: Method::Static,
761 ..V4Config::default()
762 }
763 .encode(&mut small),
764 Err(IpError::BufferTooSmall)
765 );
766 assert_eq!(
767 V4Address::default().encode(&mut small),
768 Err(IpError::BufferTooSmall)
769 );
770 assert_eq!(
771 V6Item::Router { address: [0; 16] }.encode(&mut small),
772 Err(IpError::BufferTooSmall)
773 );
774 }
775}