use crate::ast::*;
use crate::builders::WireContext;
use crate::error::CompilerError;
use crate::packet::internet_checksum;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::net::IpAddr;
pub fn build_ndp_payload(
ctx: &WireContext,
spec: &NdpSpec,
payload: &[u8],
) -> Result<(Vec<u8>, IpAddr, u8), CompilerError> {
let (src_v6, dst_v6) = match (ctx.src_ip, ctx.dst_ip) {
(IpAddr::V6(s), IpAddr::V6(d)) => (s, d),
_ => {
return Err(CompilerError::BackendError(
"NDP is strictly an IPv6 protocol".to_string(),
));
}
};
let target_v6 = match spec.target {
Some(IpAddr::V6(t)) => t,
_ => dst_v6,
};
let ndp_type = spec.ndp_type.unwrap_or(NdpType::NeighborSolicit);
let mut ndp_body = Vec::with_capacity(32 + payload.len());
let mut actual_dst_ip = IpAddr::V6(dst_v6);
match ndp_type {
NdpType::NeighborSolicit => {
ndp_body.push(135); ndp_body.push(0); ndp_body.extend_from_slice(&[0, 0]); ndp_body.extend_from_slice(&[0u8; 4]); ndp_body.extend_from_slice(&target_v6.octets());
let slla = spec.slla.unwrap_or(ctx.src_mac);
ndp_body.push(1);
ndp_body.push(1);
ndp_body.extend_from_slice(&slla);
let target_octets = target_v6.octets();
actual_dst_ip = IpAddr::V6(core::net::Ipv6Addr::new(
0xff02, 0, 0, 0, 0, 1,
0xff00 | (target_octets[13] as u16),
((target_octets[14] as u16) << 8) | (target_octets[15] as u16),
));
}
NdpType::NeighborAdvert => {
ndp_body.push(136); ndp_body.push(0); ndp_body.extend_from_slice(&[0, 0]); ndp_body.extend_from_slice(&[0x60, 0, 0, 0]); ndp_body.extend_from_slice(&target_v6.octets());
let tlla = spec.tlla.unwrap_or(ctx.src_mac);
ndp_body.push(2);
ndp_body.push(1);
ndp_body.extend_from_slice(&tlla);
}
NdpType::RouterSolicit => {
ndp_body.push(133); ndp_body.push(0);
ndp_body.extend_from_slice(&[0, 0]);
ndp_body.extend_from_slice(&[0u8; 4]);
let slla = spec.slla.unwrap_or(ctx.src_mac);
ndp_body.push(1);
ndp_body.push(1);
ndp_body.extend_from_slice(&slla);
actual_dst_ip = IpAddr::V6(core::net::Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 2));
}
NdpType::RouterAdvert => {
ndp_body.push(134); ndp_body.push(0);
ndp_body.extend_from_slice(&[0, 0]);
ndp_body.push(64); ndp_body.push(0); ndp_body.extend_from_slice(&spec.router_lifetime.unwrap_or(1800).to_be_bytes());
ndp_body.extend_from_slice(&0_u32.to_be_bytes()); ndp_body.extend_from_slice(&0_u32.to_be_bytes()); }
NdpType::Redirect => {
ndp_body.push(137);
ndp_body.push(0);
ndp_body.extend_from_slice(&[0, 0]);
ndp_body.extend_from_slice(&[0u8; 4]);
ndp_body.extend_from_slice(&target_v6.octets());
ndp_body.extend_from_slice(&dst_v6.octets());
}
}
ndp_body.extend_from_slice(payload);
let pseudo = {
let mut p = Vec::with_capacity(40);
p.extend_from_slice(&src_v6.octets());
if let IpAddr::V6(ad) = actual_dst_ip {
p.extend_from_slice(&ad.octets());
}
p.extend_from_slice(&(ndp_body.len() as u32).to_be_bytes());
p.extend_from_slice(&[0, 0, 0, 58]);
p
};
let cksum = internet_checksum(&[&pseudo, &ndp_body]);
ndp_body[2..4].copy_from_slice(&cksum.to_be_bytes());
Ok((ndp_body, actual_dst_ip, 58))
}