1#![allow(dead_code)] use alloc::{collections::BTreeMap, format, string::String, vec::Vec};
10use core::sync::atomic::{AtomicU64, Ordering};
11
12use spin::RwLock;
13
14use super::{Ipv6Address, MacAddress};
15use crate::{error::KernelError, sync::once_lock::GlobalState};
16
17pub const IPV6_HEADER_SIZE: usize = 40;
23
24pub const IPV6_VERSION: u8 = 6;
26
27pub const DEFAULT_HOP_LIMIT: u8 = 64;
29
30pub const IPV6_MIN_MTU: usize = 1280;
32
33pub const NEXT_HEADER_HOP_BY_HOP: u8 = 0;
35pub const NEXT_HEADER_TCP: u8 = 6;
36pub const NEXT_HEADER_UDP: u8 = 17;
37pub const NEXT_HEADER_ICMPV6: u8 = 58;
38pub const NEXT_HEADER_NO_NEXT: u8 = 59;
39pub const NEXT_HEADER_FRAGMENT: u8 = 44;
40
41pub const ICMPV6_ROUTER_SOLICIT: u8 = 133;
43pub const ICMPV6_ROUTER_ADVERT: u8 = 134;
44pub const ICMPV6_NEIGHBOR_SOLICIT: u8 = 135;
45pub const ICMPV6_NEIGHBOR_ADVERT: u8 = 136;
46pub const ICMPV6_REDIRECT: u8 = 137;
47
48pub const NDP_OPT_SOURCE_LINK_ADDR: u8 = 1;
50pub const NDP_OPT_TARGET_LINK_ADDR: u8 = 2;
51pub const NDP_OPT_PREFIX_INFO: u8 = 3;
52pub const NDP_OPT_MTU: u8 = 5;
53
54const NDP_CACHE_MAX: usize = 128;
56
57const NDP_REACHABLE_TIME: u64 = 30;
59
60const NDP_STALE_TIMEOUT: u64 = 600;
62
63pub const ETHERTYPE_IPV6: u16 = 0x86DD;
65
66#[derive(Debug, Clone, Copy)]
83#[repr(C)]
84pub struct Ipv6Header {
85 pub version_tc_flow: u32,
88 pub payload_length: u16,
90 pub next_header: u8,
92 pub hop_limit: u8,
94 pub source: [u8; 16],
96 pub destination: [u8; 16],
98}
99
100impl Ipv6Header {
101 pub fn new(src: &Ipv6Address, dst: &Ipv6Address, next_header: u8) -> Self {
103 let version_tc_flow: u32 = (IPV6_VERSION as u32) << 28;
105
106 Self {
107 version_tc_flow,
108 payload_length: 0,
109 next_header,
110 hop_limit: DEFAULT_HOP_LIMIT,
111 source: src.0,
112 destination: dst.0,
113 }
114 }
115
116 pub fn version(&self) -> u8 {
118 ((self.version_tc_flow >> 28) & 0x0F) as u8
119 }
120
121 pub fn traffic_class(&self) -> u8 {
123 ((self.version_tc_flow >> 20) & 0xFF) as u8
124 }
125
126 pub fn flow_label(&self) -> u32 {
128 self.version_tc_flow & 0x000F_FFFF
129 }
130
131 pub fn to_bytes(&self) -> [u8; IPV6_HEADER_SIZE] {
133 let mut bytes = [0u8; IPV6_HEADER_SIZE];
134
135 let vtf_be = self.version_tc_flow.to_be_bytes();
137 bytes[0..4].copy_from_slice(&vtf_be);
138
139 bytes[4..6].copy_from_slice(&self.payload_length.to_be_bytes());
141
142 bytes[6] = self.next_header;
144
145 bytes[7] = self.hop_limit;
147
148 bytes[8..24].copy_from_slice(&self.source);
150
151 bytes[24..40].copy_from_slice(&self.destination);
153
154 bytes
155 }
156}
157
158pub fn parse_ipv6(data: &[u8]) -> Result<(Ipv6Header, &[u8]), KernelError> {
166 if data.len() < IPV6_HEADER_SIZE {
167 return Err(KernelError::InvalidArgument {
168 name: "ipv6_packet",
169 value: "too_short",
170 });
171 }
172
173 let version_tc_flow = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
175
176 let version = (version_tc_flow >> 28) as u8;
177 if version != IPV6_VERSION {
178 return Err(KernelError::InvalidArgument {
179 name: "ipv6_version",
180 value: "not_ipv6",
181 });
182 }
183
184 let payload_length = u16::from_be_bytes([data[4], data[5]]);
185 let next_header = data[6];
186 let hop_limit = data[7];
187
188 let mut source = [0u8; 16];
189 let mut destination = [0u8; 16];
190 source.copy_from_slice(&data[8..24]);
191 destination.copy_from_slice(&data[24..40]);
192
193 let header = Ipv6Header {
194 version_tc_flow,
195 payload_length,
196 next_header,
197 hop_limit,
198 source,
199 destination,
200 };
201
202 let payload_end = IPV6_HEADER_SIZE + (payload_length as usize);
204 let actual_end = payload_end.min(data.len());
205 let payload = &data[IPV6_HEADER_SIZE..actual_end];
206
207 Ok((header, payload))
208}
209
210pub fn build_ipv6(
214 src: &Ipv6Address,
215 dst: &Ipv6Address,
216 next_header: u8,
217 payload: &[u8],
218) -> Vec<u8> {
219 let mut header = Ipv6Header::new(src, dst, next_header);
220 header.payload_length = payload.len() as u16;
221
222 let header_bytes = header.to_bytes();
223 let mut packet = Vec::with_capacity(IPV6_HEADER_SIZE + payload.len());
224 packet.extend_from_slice(&header_bytes);
225 packet.extend_from_slice(payload);
226
227 packet
228}
229
230pub fn is_link_local(addr: &Ipv6Address) -> bool {
236 addr.0[0] == 0xfe && (addr.0[1] & 0xc0) == 0x80
237}
238
239pub fn is_multicast(addr: &Ipv6Address) -> bool {
241 addr.0[0] == 0xff
242}
243
244pub fn is_loopback(addr: &Ipv6Address) -> bool {
246 *addr == Ipv6Address::LOCALHOST
247}
248
249pub fn is_unspecified(addr: &Ipv6Address) -> bool {
251 *addr == Ipv6Address::UNSPECIFIED
252}
253
254pub fn is_global_unicast(addr: &Ipv6Address) -> bool {
256 (addr.0[0] & 0xe0) == 0x20
257}
258
259pub fn is_unique_local(addr: &Ipv6Address) -> bool {
261 (addr.0[0] & 0xfe) == 0xfc
262}
263
264pub fn is_ipv4_mapped(addr: &Ipv6Address) -> bool {
266 addr.0[0..10] == [0; 10] && addr.0[10] == 0xff && addr.0[11] == 0xff
267}
268
269pub fn solicited_node_multicast(addr: &Ipv6Address) -> Ipv6Address {
274 Ipv6Address([
275 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, addr.0[13],
276 addr.0[14], addr.0[15],
277 ])
278}
279
280pub fn link_local_from_mac(mac: &MacAddress) -> Ipv6Address {
285 let mut addr = [0u8; 16];
286
287 addr[0] = 0xfe;
289 addr[1] = 0x80;
290 addr[8] = mac.0[0] ^ 0x02; addr[9] = mac.0[1];
295 addr[10] = mac.0[2];
296 addr[11] = 0xff;
297 addr[12] = 0xfe;
298 addr[13] = mac.0[3];
299 addr[14] = mac.0[4];
300 addr[15] = mac.0[5];
301
302 Ipv6Address(addr)
303}
304
305pub fn format_ipv6(addr: &Ipv6Address) -> String {
310 let b = &addr.0;
311 format!(
312 "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
313 u16::from_be_bytes([b[0], b[1]]),
314 u16::from_be_bytes([b[2], b[3]]),
315 u16::from_be_bytes([b[4], b[5]]),
316 u16::from_be_bytes([b[6], b[7]]),
317 u16::from_be_bytes([b[8], b[9]]),
318 u16::from_be_bytes([b[10], b[11]]),
319 u16::from_be_bytes([b[12], b[13]]),
320 u16::from_be_bytes([b[14], b[15]]),
321 )
322}
323
324pub fn format_ipv6_compressed(addr: &Ipv6Address) -> String {
328 let b = &addr.0;
329 let groups: [u16; 8] = [
330 u16::from_be_bytes([b[0], b[1]]),
331 u16::from_be_bytes([b[2], b[3]]),
332 u16::from_be_bytes([b[4], b[5]]),
333 u16::from_be_bytes([b[6], b[7]]),
334 u16::from_be_bytes([b[8], b[9]]),
335 u16::from_be_bytes([b[10], b[11]]),
336 u16::from_be_bytes([b[12], b[13]]),
337 u16::from_be_bytes([b[14], b[15]]),
338 ];
339
340 let mut best_start = 0usize;
342 let mut best_len = 0usize;
343 let mut cur_start = 0usize;
344 let mut cur_len = 0usize;
345
346 for (i, &group) in groups.iter().enumerate() {
347 if group == 0 {
348 if cur_len == 0 {
349 cur_start = i;
350 }
351 cur_len += 1;
352 if cur_len > best_len {
353 best_start = cur_start;
354 best_len = cur_len;
355 }
356 } else {
357 cur_len = 0;
358 }
359 }
360
361 if best_len < 2 {
363 return format_ipv6(addr);
364 }
365
366 let mut parts: Vec<String> = Vec::new();
367 let mut i = 0usize;
368 let mut compressed = false;
369
370 while i < 8 {
371 if i == best_start && !compressed {
372 if i == 0 {
374 parts.push(String::new()); }
376 parts.push(String::new()); if best_start + best_len == 8 {
378 parts.push(String::new()); }
380 i += best_len;
381 compressed = true;
382 } else {
383 parts.push(format!("{:x}", groups[i]));
384 i += 1;
385 }
386 }
387
388 parts.join(":")
389}
390
391pub fn multicast_mac(addr: &Ipv6Address) -> MacAddress {
396 MacAddress([0x33, 0x33, addr.0[12], addr.0[13], addr.0[14], addr.0[15]])
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq)]
405pub enum NdpState {
406 Incomplete,
408 Reachable,
410 Stale,
412 Delay,
414 Probe,
416}
417
418#[derive(Debug, Clone)]
420pub struct NdpEntry {
421 pub mac: MacAddress,
423 pub state: NdpState,
425 pub timestamp: u64,
427 pub probe_count: u8,
429}
430
431pub struct NdpCache {
433 entries: BTreeMap<Ipv6Address, NdpEntry>,
434}
435
436impl Default for NdpCache {
437 fn default() -> Self {
438 Self::new()
439 }
440}
441
442impl NdpCache {
443 pub const fn new() -> Self {
445 Self {
446 entries: BTreeMap::new(),
447 }
448 }
449
450 pub fn lookup(&self, addr: &Ipv6Address) -> Option<MacAddress> {
454 if let Some(entry) = self.entries.get(addr) {
455 match entry.state {
456 NdpState::Reachable | NdpState::Stale | NdpState::Delay | NdpState::Probe => {
457 Some(entry.mac)
458 }
459 NdpState::Incomplete => None,
460 }
461 } else {
462 None
463 }
464 }
465
466 pub fn update(&mut self, addr: Ipv6Address, mac: MacAddress, state: NdpState) {
468 let now = current_tick();
469
470 if self.entries.len() >= NDP_CACHE_MAX && !self.entries.contains_key(&addr) {
472 let oldest_key = self
473 .entries
474 .iter()
475 .min_by_key(|(_, e)| e.timestamp)
476 .map(|(k, _)| *k);
477 if let Some(key) = oldest_key {
478 self.entries.remove(&key);
479 }
480 }
481
482 self.entries.insert(
483 addr,
484 NdpEntry {
485 mac,
486 state,
487 timestamp: now,
488 probe_count: 0,
489 },
490 );
491 }
492
493 pub fn mark_incomplete(&mut self, addr: Ipv6Address) {
495 let now = current_tick();
496 self.entries.insert(
497 addr,
498 NdpEntry {
499 mac: MacAddress::ZERO,
500 state: NdpState::Incomplete,
501 timestamp: now,
502 probe_count: 1,
503 },
504 );
505 }
506
507 pub fn age_entries(&mut self) {
509 let now = current_tick();
510 for entry in self.entries.values_mut() {
511 match entry.state {
512 NdpState::Reachable => {
513 if now.wrapping_sub(entry.timestamp) > NDP_REACHABLE_TIME {
514 entry.state = NdpState::Stale;
515 }
516 }
517 NdpState::Stale => {
518 if now.wrapping_sub(entry.timestamp) > NDP_STALE_TIMEOUT {
519 entry.state = NdpState::Incomplete;
520 }
521 }
522 _ => {}
523 }
524 }
525 }
526
527 pub fn remove(&mut self, addr: &Ipv6Address) {
529 self.entries.remove(addr);
530 }
531
532 pub fn get_entries(&self) -> Vec<(Ipv6Address, MacAddress, NdpState)> {
534 self.entries
535 .iter()
536 .map(|(addr, entry)| (*addr, entry.mac, entry.state))
537 .collect()
538 }
539
540 pub fn flush(&mut self) {
542 self.entries.clear();
543 }
544
545 pub fn len(&self) -> usize {
547 self.entries.len()
548 }
549
550 pub fn is_empty(&self) -> bool {
552 self.entries.is_empty()
553 }
554}
555
556pub fn ndp_solicit(src: &Ipv6Address, target: &Ipv6Address, src_mac: &MacAddress) -> Vec<u8> {
566 let mut msg = Vec::with_capacity(32);
570
571 msg.push(ICMPV6_NEIGHBOR_SOLICIT); msg.push(0); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&[0u8; 4]); msg.extend_from_slice(&target.0);
579
580 msg.push(NDP_OPT_SOURCE_LINK_ADDR);
582 msg.push(1); msg.extend_from_slice(&src_mac.0);
584
585 let dst = solicited_node_multicast(target);
587 let checksum = compute_icmpv6_checksum(&src.0, &dst.0, &msg);
588 msg[2] = (checksum >> 8) as u8;
589 msg[3] = (checksum & 0xff) as u8;
590
591 msg
592}
593
594pub fn ndp_advertise(
599 src: &Ipv6Address,
600 dst: &Ipv6Address,
601 target: &Ipv6Address,
602 mac: &MacAddress,
603 solicited: bool,
604 override_flag: bool,
605) -> Vec<u8> {
606 let mut msg = Vec::with_capacity(32);
611
612 msg.push(ICMPV6_NEIGHBOR_ADVERT); msg.push(0); msg.extend_from_slice(&[0u8; 2]); let mut flags: u8 = 0;
619 if solicited {
620 flags |= 0x40; }
622 if override_flag {
623 flags |= 0x20; }
625 msg.push(flags);
626 msg.extend_from_slice(&[0u8; 3]); msg.extend_from_slice(&target.0);
630
631 msg.push(NDP_OPT_TARGET_LINK_ADDR);
633 msg.push(1); msg.extend_from_slice(&mac.0);
635
636 let checksum = compute_icmpv6_checksum(&src.0, &dst.0, &msg);
638 msg[2] = (checksum >> 8) as u8;
639 msg[3] = (checksum & 0xff) as u8;
640
641 msg
642}
643
644pub fn ndp_router_solicit(src: &Ipv6Address, src_mac: &MacAddress) -> Vec<u8> {
648 let mut msg = Vec::with_capacity(16);
652
653 msg.push(ICMPV6_ROUTER_SOLICIT); msg.push(0); msg.extend_from_slice(&[0u8; 2]); msg.extend_from_slice(&[0u8; 4]); if !is_unspecified(src) {
660 msg.push(NDP_OPT_SOURCE_LINK_ADDR);
661 msg.push(1); msg.extend_from_slice(&src_mac.0);
663 }
664
665 let dst = Ipv6Address::ALL_ROUTERS_LINK_LOCAL;
667 let checksum = compute_icmpv6_checksum(&src.0, &dst.0, &msg);
668 msg[2] = (checksum >> 8) as u8;
669 msg[3] = (checksum & 0xff) as u8;
670
671 msg
672}
673
674pub fn handle_ndp(
681 src_addr: &Ipv6Address,
682 dst_addr: &Ipv6Address,
683 data: &[u8],
684) -> Result<Option<Vec<u8>>, KernelError> {
685 if data.len() < 4 {
686 return Err(KernelError::InvalidArgument {
687 name: "ndp_message",
688 value: "too_short",
689 });
690 }
691
692 let icmp_type = data[0];
693
694 match icmp_type {
695 ICMPV6_NEIGHBOR_SOLICIT => handle_neighbor_solicitation(src_addr, dst_addr, data),
696 ICMPV6_NEIGHBOR_ADVERT => handle_neighbor_advertisement(src_addr, data),
697 ICMPV6_ROUTER_SOLICIT => {
698 Ok(None)
700 }
701 ICMPV6_ROUTER_ADVERT => handle_router_advertisement(src_addr, data),
702 _ => {
703 Ok(None)
705 }
706 }
707}
708
709fn handle_neighbor_solicitation(
711 src_addr: &Ipv6Address,
712 _dst_addr: &Ipv6Address,
713 data: &[u8],
714) -> Result<Option<Vec<u8>>, KernelError> {
715 if data.len() < 24 {
718 return Err(KernelError::InvalidArgument {
719 name: "ndp_ns",
720 value: "too_short",
721 });
722 }
723
724 let mut target = [0u8; 16];
725 target.copy_from_slice(&data[8..24]);
726 let target_addr = Ipv6Address(target);
727
728 let source_mac = parse_link_layer_option(&data[24..], NDP_OPT_SOURCE_LINK_ADDR);
730
731 if let Some(mac) = source_mac {
733 if !is_unspecified(src_addr) {
734 IPV6_STATE.with_mut(|state| {
735 let mut s = state.write();
736 s.ndp_cache.update(*src_addr, mac, NdpState::Stale);
737 });
738 }
739 }
740
741 let is_our_addr = IPV6_STATE
743 .with(|state| {
744 let s = state.read();
745 s.config
746 .ipv6_addresses
747 .iter()
748 .any(|a| a.address == target_addr)
749 })
750 .unwrap_or(false);
751
752 if !is_our_addr {
753 return Ok(None);
754 }
755
756 let our_mac = get_interface_mac();
758 let reply_dst = if is_unspecified(src_addr) {
759 Ipv6Address::ALL_NODES_LINK_LOCAL
760 } else {
761 *src_addr
762 };
763
764 let na = ndp_advertise(
765 &target_addr,
766 &reply_dst,
767 &target_addr,
768 &our_mac,
769 !is_unspecified(src_addr), true, );
772
773 Ok(Some(na))
774}
775
776fn handle_neighbor_advertisement(
778 _src_addr: &Ipv6Address,
779 data: &[u8],
780) -> Result<Option<Vec<u8>>, KernelError> {
781 if data.len() < 24 {
784 return Err(KernelError::InvalidArgument {
785 name: "ndp_na",
786 value: "too_short",
787 });
788 }
789
790 let _flags = data[4];
791
792 let mut target = [0u8; 16];
793 target.copy_from_slice(&data[8..24]);
794 let target_addr = Ipv6Address(target);
795
796 let target_mac = parse_link_layer_option(&data[24..], NDP_OPT_TARGET_LINK_ADDR);
798
799 if let Some(mac) = target_mac {
801 IPV6_STATE.with_mut(|state| {
802 let mut s = state.write();
803 s.ndp_cache.update(target_addr, mac, NdpState::Reachable);
804 });
805 }
806
807 Ok(None)
808}
809
810fn handle_router_advertisement(
812 src_addr: &Ipv6Address,
813 data: &[u8],
814) -> Result<Option<Vec<u8>>, KernelError> {
815 if data.len() < 16 {
819 return Err(KernelError::InvalidArgument {
820 name: "ndp_ra",
821 value: "too_short",
822 });
823 }
824
825 let cur_hop_limit = data[4];
826 let _flags = data[5];
827 let router_lifetime = u16::from_be_bytes([data[6], data[7]]);
828
829 if cur_hop_limit != 0 {
831 IPV6_STATE.with_mut(|state| {
832 let mut s = state.write();
833 s.hop_limit = cur_hop_limit;
834 });
835 }
836
837 let source_mac = parse_link_layer_option(&data[16..], NDP_OPT_SOURCE_LINK_ADDR);
839 if let Some(mac) = source_mac {
840 IPV6_STATE.with_mut(|state| {
841 let mut s = state.write();
842 s.ndp_cache.update(*src_addr, mac, NdpState::Reachable);
843 });
844 }
845
846 parse_prefix_options(&data[16..], router_lifetime);
848
849 Ok(None)
850}
851
852fn parse_link_layer_option(options: &[u8], opt_type: u8) -> Option<MacAddress> {
854 let mut offset = 0;
855 while offset + 2 <= options.len() {
856 let otype = options[offset];
857 let olen = options[offset + 1] as usize;
858 if olen == 0 {
859 break; }
861 let opt_bytes = olen * 8;
862 if offset + opt_bytes > options.len() {
863 break;
864 }
865 if otype == opt_type && opt_bytes >= 8 {
866 let mut mac_bytes = [0u8; 6];
867 mac_bytes.copy_from_slice(&options[offset + 2..offset + 8]);
868 return Some(MacAddress(mac_bytes));
869 }
870 offset += opt_bytes;
871 }
872 None
873}
874
875fn parse_prefix_options(options: &[u8], _router_lifetime: u16) {
877 let mut offset = 0;
878 while offset + 2 <= options.len() {
879 let otype = options[offset];
880 let olen = options[offset + 1] as usize;
881 if olen == 0 {
882 break;
883 }
884 let opt_bytes = olen * 8;
885 if offset + opt_bytes > options.len() {
886 break;
887 }
888
889 if otype == NDP_OPT_PREFIX_INFO && opt_bytes >= 32 {
890 let prefix_len = options[offset + 2];
891 let flags = options[offset + 3];
892 let autonomous = (flags & 0x40) != 0;
893
894 if autonomous && prefix_len == 64 {
895 let mut prefix = [0u8; 16];
896 prefix.copy_from_slice(&options[offset + 16..offset + 32]);
897
898 let our_mac = get_interface_mac();
900 let ll = link_local_from_mac(&our_mac);
901 let mut addr_bytes = prefix;
903 addr_bytes[8..16].copy_from_slice(&ll.0[8..16]);
904 let new_addr = Ipv6Address(addr_bytes);
905
906 IPV6_STATE.with_mut(|state| {
907 let mut s = state.write();
908 let exists = s
909 .config
910 .ipv6_addresses
911 .iter()
912 .any(|a| a.address == new_addr);
913 if !exists {
914 s.config.ipv6_addresses.push(Ipv6InterfaceAddr {
915 address: new_addr,
916 prefix_len,
917 scope: Ipv6Scope::Global,
918 });
919 println!(
920 "[IPv6] SLAAC: configured global address {}",
921 format_ipv6_compressed(&new_addr)
922 );
923 }
924 });
925 }
926 }
927
928 offset += opt_bytes;
929 }
930}
931
932pub fn compute_icmpv6_checksum(src: &[u8; 16], dst: &[u8; 16], data: &[u8]) -> u16 {
941 let mut sum: u32 = 0;
942
943 for chunk in src.chunks(2) {
945 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
946 }
947
948 for chunk in dst.chunks(2) {
950 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
951 }
952
953 let length = data.len() as u32;
955 sum += length >> 16;
956 sum += length & 0xFFFF;
957
958 sum += NEXT_HEADER_ICMPV6 as u32;
960
961 for chunk in data.chunks(2) {
964 if chunk.len() == 2 {
965 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
966 } else {
967 sum += (chunk[0] as u32) << 8;
968 }
969 }
970
971 while sum >> 16 != 0 {
973 sum = (sum & 0xFFFF) + (sum >> 16);
974 }
975
976 !(sum as u16)
977}
978
979#[derive(Debug, Clone, Copy, PartialEq, Eq)]
985pub enum Ipv6Scope {
986 LinkLocal,
988 Global,
990 SiteLocal,
992}
993
994#[derive(Debug, Clone)]
996pub struct Ipv6InterfaceAddr {
997 pub address: Ipv6Address,
999 pub prefix_len: u8,
1001 pub scope: Ipv6Scope,
1003}
1004
1005#[derive(Debug, Clone)]
1007pub struct DualStackConfig {
1008 pub ipv4_enabled: bool,
1010 pub ipv6_enabled: bool,
1012 pub prefer_ipv6: bool,
1014 pub ipv6_addresses: Vec<Ipv6InterfaceAddr>,
1016}
1017
1018impl DualStackConfig {
1019 pub fn new() -> Self {
1021 Self {
1022 ipv4_enabled: true,
1023 ipv6_enabled: true,
1024 prefer_ipv6: true,
1025 ipv6_addresses: Vec::new(),
1026 }
1027 }
1028
1029 pub fn link_local_addr(&self) -> Option<&Ipv6InterfaceAddr> {
1031 self.ipv6_addresses
1032 .iter()
1033 .find(|a| a.scope == Ipv6Scope::LinkLocal)
1034 }
1035
1036 pub fn global_addr(&self) -> Option<&Ipv6InterfaceAddr> {
1038 self.ipv6_addresses
1039 .iter()
1040 .find(|a| a.scope == Ipv6Scope::Global)
1041 }
1042
1043 pub fn addresses_by_scope(&self, scope: Ipv6Scope) -> Vec<&Ipv6InterfaceAddr> {
1045 self.ipv6_addresses
1046 .iter()
1047 .filter(|a| a.scope == scope)
1048 .collect()
1049 }
1050}
1051
1052impl Default for DualStackConfig {
1053 fn default() -> Self {
1054 Self::new()
1055 }
1056}
1057
1058pub struct Ipv6State {
1064 pub config: DualStackConfig,
1066 pub ndp_cache: NdpCache,
1068 pub hop_limit: u8,
1070}
1071
1072static IPV6_STATE: GlobalState<RwLock<Ipv6State>> = GlobalState::new();
1074
1075static NDP_TICK: AtomicU64 = AtomicU64::new(0);
1077
1078fn current_tick() -> u64 {
1080 NDP_TICK.load(Ordering::Relaxed)
1081}
1082
1083pub fn tick() {
1085 NDP_TICK.fetch_add(1, Ordering::Relaxed);
1086}
1087
1088pub fn init() -> Result<(), KernelError> {
1097 println!("[IPv6] Initializing IPv6 subsystem...");
1098
1099 let our_mac = get_interface_mac();
1100 let link_local = link_local_from_mac(&our_mac);
1101
1102 let mut config = DualStackConfig::new();
1103 config.ipv6_addresses.push(Ipv6InterfaceAddr {
1104 address: link_local,
1105 prefix_len: 10,
1106 scope: Ipv6Scope::LinkLocal,
1107 });
1108
1109 let state = Ipv6State {
1110 config,
1111 ndp_cache: NdpCache::new(),
1112 hop_limit: DEFAULT_HOP_LIMIT,
1113 };
1114
1115 IPV6_STATE
1116 .init(RwLock::new(state))
1117 .map_err(|_| KernelError::AlreadyExists {
1118 resource: "ipv6_state",
1119 id: 0,
1120 })?;
1121
1122 println!(
1123 "[IPv6] Link-local address: {}",
1124 format_ipv6_compressed(&link_local)
1125 );
1126 println!("[IPv6] IPv6 subsystem initialized");
1127
1128 Ok(())
1129}
1130
1131pub fn ndp_lookup(addr: &Ipv6Address) -> Option<MacAddress> {
1133 IPV6_STATE
1134 .with(|state| {
1135 let s = state.read();
1136 s.ndp_cache.lookup(addr)
1137 })
1138 .flatten()
1139}
1140
1141pub fn get_config() -> Option<DualStackConfig> {
1143 IPV6_STATE.with(|state| {
1144 let s = state.read();
1145 s.config.clone()
1146 })
1147}
1148
1149pub fn get_ndp_entries() -> Vec<(Ipv6Address, MacAddress, NdpState)> {
1151 IPV6_STATE
1152 .with(|state| {
1153 let s = state.read();
1154 s.ndp_cache.get_entries()
1155 })
1156 .unwrap_or_default()
1157}
1158
1159pub fn flush_ndp_cache() {
1161 IPV6_STATE.with_mut(|state| {
1162 let mut s = state.write();
1163 s.ndp_cache.flush();
1164 });
1165}
1166
1167pub fn get_hop_limit() -> u8 {
1169 IPV6_STATE
1170 .with(|state| {
1171 let s = state.read();
1172 s.hop_limit
1173 })
1174 .unwrap_or(DEFAULT_HOP_LIMIT)
1175}
1176
1177pub fn send(
1182 src: &Ipv6Address,
1183 dst: &Ipv6Address,
1184 next_header: u8,
1185 payload: &[u8],
1186) -> Result<(), KernelError> {
1187 let packet = build_ipv6(src, dst, next_header, payload);
1188
1189 let dst_mac = if is_multicast(dst) {
1191 multicast_mac(dst)
1192 } else {
1193 ndp_lookup(dst).unwrap_or_else(|| {
1194 let src_mac = get_interface_mac();
1196 let ns = ndp_solicit(src, dst, &src_mac);
1197 let sol_dst = solicited_node_multicast(dst);
1198 let ns_packet = build_ipv6(src, &sol_dst, NEXT_HEADER_ICMPV6, &ns);
1199 let sol_mac = multicast_mac(&sol_dst);
1200
1201 let frame =
1203 super::ethernet::construct_frame(sol_mac, src_mac, ETHERTYPE_IPV6, &ns_packet);
1204 let pkt = super::Packet::from_bytes(&frame);
1205 super::device::with_device_mut("eth0", |dev| {
1206 let _ = dev.transmit(&pkt);
1207 });
1208
1209 IPV6_STATE.with_mut(|state| {
1211 let mut s = state.write();
1212 s.ndp_cache.mark_incomplete(*dst);
1213 });
1214
1215 MacAddress::BROADCAST
1217 })
1218 };
1219
1220 let src_mac = get_interface_mac();
1221 let frame = super::ethernet::construct_frame(dst_mac, src_mac, ETHERTYPE_IPV6, &packet);
1222 let pkt = super::Packet::from_bytes(&frame);
1223 super::device::with_device_mut("eth0", |dev| {
1224 let _ = dev.transmit(&pkt);
1225 });
1226
1227 super::update_stats_tx(IPV6_HEADER_SIZE + payload.len());
1228
1229 Ok(())
1230}
1231
1232pub fn select_source_address(dst: &Ipv6Address) -> Option<Ipv6Address> {
1237 IPV6_STATE
1238 .with(|state| {
1239 let s = state.read();
1240 if is_link_local(dst) || is_multicast(dst) {
1241 s.config.link_local_addr().map(|a| a.address)
1242 } else {
1243 s.config
1244 .global_addr()
1245 .or_else(|| s.config.link_local_addr())
1246 .map(|a| a.address)
1247 }
1248 })
1249 .flatten()
1250}
1251
1252pub fn process_packet(data: &[u8]) -> Result<(), KernelError> {
1254 let (header, payload) = parse_ipv6(data)?;
1255
1256 let src = Ipv6Address(header.source);
1257 let dst = Ipv6Address(header.destination);
1258
1259 match header.next_header {
1260 NEXT_HEADER_ICMPV6 => {
1261 super::icmpv6::handle_icmpv6(&src, &dst, payload)?;
1262 }
1263 NEXT_HEADER_TCP => {
1264 let src_ip = super::IpAddress::V6(src);
1265 let dst_ip = super::IpAddress::V6(dst);
1266 let _ = super::tcp::process_packet(src_ip, dst_ip, payload);
1267 }
1268 NEXT_HEADER_UDP => {
1269 let src_ip = super::IpAddress::V6(src);
1270 let dst_ip = super::IpAddress::V6(dst);
1271 let _ = super::udp::process_packet(src_ip, dst_ip, payload);
1272 }
1273 _ => {
1274 }
1276 }
1277
1278 Ok(())
1279}
1280
1281fn get_interface_mac() -> MacAddress {
1287 super::device::with_device("eth0", |dev| dev.mac_address()).unwrap_or(MacAddress::ZERO)
1288}
1289
1290#[derive(Debug, Clone, Copy, Default)]
1296pub struct Ipv6Stats {
1297 pub addresses_configured: usize,
1299 pub ndp_cache_entries: usize,
1301 pub hop_limit: u8,
1303 pub dual_stack_active: bool,
1305}
1306
1307pub fn get_stats() -> Ipv6Stats {
1309 IPV6_STATE
1310 .with(|state| {
1311 let s = state.read();
1312 Ipv6Stats {
1313 addresses_configured: s.config.ipv6_addresses.len(),
1314 ndp_cache_entries: s.ndp_cache.len(),
1315 hop_limit: s.hop_limit,
1316 dual_stack_active: s.config.ipv4_enabled && s.config.ipv6_enabled,
1317 }
1318 })
1319 .unwrap_or_default()
1320}
1321
1322#[cfg(test)]
1327mod tests {
1328 use super::*;
1329
1330 #[test]
1331 fn test_ipv6_header_roundtrip() {
1332 let src = Ipv6Address([
1333 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x01,
1334 ]);
1335 let dst = Ipv6Address([
1336 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x02,
1337 ]);
1338 let payload = b"Hello IPv6!";
1339 let packet = build_ipv6(&src, &dst, NEXT_HEADER_TCP, payload);
1340
1341 let (header, parsed_payload) = parse_ipv6(&packet).unwrap();
1342 assert_eq!(header.version(), IPV6_VERSION);
1343 assert_eq!(header.next_header, NEXT_HEADER_TCP);
1344 assert_eq!(header.hop_limit, DEFAULT_HOP_LIMIT);
1345 assert_eq!(header.source, src.0);
1346 assert_eq!(header.destination, dst.0);
1347 assert_eq!(parsed_payload, payload);
1348 }
1349
1350 #[test]
1351 fn test_ipv6_parse_too_short() {
1352 let short = [0u8; 10];
1353 assert!(parse_ipv6(&short).is_err());
1354 }
1355
1356 #[test]
1357 fn test_ipv6_parse_wrong_version() {
1358 let mut packet = [0u8; IPV6_HEADER_SIZE];
1359 packet[0] = 0x40;
1361 assert!(parse_ipv6(&packet).is_err());
1362 }
1363
1364 #[test]
1365 fn test_is_link_local() {
1366 let ll = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
1367 assert!(is_link_local(&ll));
1368
1369 let global = Ipv6Address([0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1370 assert!(!is_link_local(&global));
1371 }
1372
1373 #[test]
1374 fn test_is_multicast() {
1375 let mc = Ipv6Address([0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1376 assert!(is_multicast(&mc));
1377 assert!(!is_multicast(&Ipv6Address::LOCALHOST));
1378 }
1379
1380 #[test]
1381 fn test_is_loopback() {
1382 assert!(is_loopback(&Ipv6Address::LOCALHOST));
1383 assert!(!is_loopback(&Ipv6Address::UNSPECIFIED));
1384 }
1385
1386 #[test]
1387 fn test_is_unspecified() {
1388 assert!(is_unspecified(&Ipv6Address::UNSPECIFIED));
1389 assert!(!is_unspecified(&Ipv6Address::LOCALHOST));
1390 }
1391
1392 #[test]
1393 fn test_is_global_unicast() {
1394 let global = Ipv6Address([0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1395 assert!(is_global_unicast(&global));
1396 assert!(!is_global_unicast(&Ipv6Address::LOCALHOST));
1397 }
1398
1399 #[test]
1400 fn test_solicited_node_multicast() {
1401 let addr = Ipv6Address([
1402 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x00, 0x00, 0xff, 0xfe, 0xab, 0xcd, 0xef,
1403 ]);
1404 let sol = solicited_node_multicast(&addr);
1405 assert_eq!(sol.0[0], 0xff);
1406 assert_eq!(sol.0[1], 0x02);
1407 assert_eq!(sol.0[11], 0x01);
1408 assert_eq!(sol.0[12], 0xff);
1409 assert_eq!(sol.0[13], 0xab);
1410 assert_eq!(sol.0[14], 0xcd);
1411 assert_eq!(sol.0[15], 0xef);
1412 }
1413
1414 #[test]
1415 fn test_link_local_from_mac() {
1416 let mac = MacAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]);
1417 let ll = link_local_from_mac(&mac);
1418 assert_eq!(ll.0[0], 0xfe);
1419 assert_eq!(ll.0[1], 0x80);
1420 assert_eq!(ll.0[8], 0x52 ^ 0x02);
1421 assert_eq!(ll.0[9], 0x54);
1422 assert_eq!(ll.0[10], 0x00);
1423 assert_eq!(ll.0[11], 0xff);
1424 assert_eq!(ll.0[12], 0xfe);
1425 assert_eq!(ll.0[13], 0x12);
1426 assert_eq!(ll.0[14], 0x34);
1427 assert_eq!(ll.0[15], 0x56);
1428 assert!(is_link_local(&ll));
1429 }
1430
1431 #[test]
1432 fn test_multicast_mac() {
1433 let mc = Ipv6Address([0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1434 let mac = multicast_mac(&mc);
1435 assert_eq!(mac.0[0], 0x33);
1436 assert_eq!(mac.0[1], 0x33);
1437 assert_eq!(mac.0[2], 0x00);
1438 assert_eq!(mac.0[3], 0x00);
1439 assert_eq!(mac.0[4], 0x00);
1440 assert_eq!(mac.0[5], 0x01);
1441 }
1442
1443 #[test]
1444 fn test_format_ipv6() {
1445 let addr = Ipv6Address::LOCALHOST;
1446 let formatted = format_ipv6(&addr);
1447 assert_eq!(formatted, "0:0:0:0:0:0:0:1");
1448 }
1449
1450 #[test]
1451 fn test_format_ipv6_compressed_loopback() {
1452 let addr = Ipv6Address::LOCALHOST;
1453 let formatted = format_ipv6_compressed(&addr);
1454 assert_eq!(formatted, "::1");
1455 }
1456
1457 #[test]
1458 fn test_format_ipv6_compressed_unspecified() {
1459 let addr = Ipv6Address::UNSPECIFIED;
1460 let formatted = format_ipv6_compressed(&addr);
1461 assert_eq!(formatted, "::");
1462 }
1463
1464 #[test]
1465 fn test_icmpv6_checksum() {
1466 let src = [0u8; 16];
1467 let dst = [0u8; 16];
1468 let data = [0u8; 4];
1469 let cksum = compute_icmpv6_checksum(&src, &dst, &data);
1470 assert_ne!(cksum, 0);
1471 }
1472
1473 #[test]
1474 fn test_ndp_cache_basic() {
1475 let mut cache = NdpCache::new();
1476 let addr = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
1477 let mac = MacAddress([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1478
1479 assert!(cache.lookup(&addr).is_none());
1480
1481 cache.update(addr, mac, NdpState::Reachable);
1482 assert_eq!(cache.lookup(&addr), Some(mac));
1483 assert_eq!(cache.len(), 1);
1484
1485 cache.flush();
1486 assert!(cache.is_empty());
1487 }
1488
1489 #[test]
1490 fn test_ndp_cache_incomplete() {
1491 let mut cache = NdpCache::new();
1492 let addr = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
1493
1494 cache.mark_incomplete(addr);
1495 assert!(cache.lookup(&addr).is_none());
1496 assert_eq!(cache.len(), 1);
1497 }
1498
1499 #[test]
1500 fn test_dual_stack_config() {
1501 let mut config = DualStackConfig::new();
1502 assert!(config.ipv4_enabled);
1503 assert!(config.ipv6_enabled);
1504 assert!(config.prefer_ipv6);
1505 assert!(config.link_local_addr().is_none());
1506
1507 config.ipv6_addresses.push(Ipv6InterfaceAddr {
1508 address: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]),
1509 prefix_len: 10,
1510 scope: Ipv6Scope::LinkLocal,
1511 });
1512
1513 assert!(config.link_local_addr().is_some());
1514 assert!(config.global_addr().is_none());
1515 }
1516
1517 #[test]
1518 fn test_is_ipv4_mapped() {
1519 let mapped = Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]);
1520 assert!(is_ipv4_mapped(&mapped));
1521 assert!(!is_ipv4_mapped(&Ipv6Address::LOCALHOST));
1522 }
1523}