use crate::ast::*;
use crate::builders::WireContext;
use crate::packet::internet_checksum;
use alloc::vec::Vec;
use core::net::IpAddr;
fn ipv4_octets_or(ip: Option<IpAddr>, fallback: [u8; 4]) -> [u8; 4] {
match ip {
Some(IpAddr::V4(v4)) => v4.octets(),
Some(IpAddr::V6(v6)) => {
let o = v6.octets();
[o[12], o[13], o[14], o[15]]
}
None => fallback,
}
}
pub fn build_pim_payload(ctx: &WireContext, spec: &PimSpec, payload: &[u8]) -> (Vec<u8>, IpAddr, u8) {
let mut pim = Vec::with_capacity(36 + payload.len());
let is_register = spec.is_register || (spec.rp.is_some() && !spec.is_join_prune && !spec.is_hello);
let is_join_prune =
spec.is_join_prune || ((spec.group.is_some() || spec.source.is_some()) && !spec.is_hello && !is_register);
if is_register {
pim.push(0x21); pim.push(0); pim.extend_from_slice(&[0, 0]); pim.extend_from_slice(&0_u32.to_be_bytes());
let src_v4 = ipv4_octets_or(spec.source.or(Some(ctx.src_ip)), [10, 0, 0, 1]);
let grp_v4 = ipv4_octets_or(spec.group.or(spec.rp), [239, 1, 1, 1]);
let mut inner_ip = [
0x45, 0x00, 0x00, 20, 0x00, 0x01, 0x00, 0x00, 64, 17, 0x00, 0x00, src_v4[0],
src_v4[1], src_v4[2], src_v4[3], grp_v4[0], grp_v4[1], grp_v4[2], grp_v4[3],
];
let ip_cksum = internet_checksum(&[&inner_ip]);
inner_ip[10..12].copy_from_slice(&ip_cksum.to_be_bytes());
pim.extend_from_slice(&inner_ip);
} else if is_join_prune {
pim.push(0x23); pim.push(0); pim.extend_from_slice(&[0, 0]);
let upstream = ipv4_octets_or(spec.rp.or(Some(ctx.dst_ip)), [10, 0, 0, 2]);
pim.push(1);
pim.push(0);
pim.extend_from_slice(&upstream);
pim.push(0); pim.push(1); pim.extend_from_slice(&spec.holdtime.unwrap_or(210).to_be_bytes());
let group = ipv4_octets_or(spec.group, [239, 1, 1, 1]);
pim.extend_from_slice(&[1, 0, 0, 32]);
pim.extend_from_slice(&group);
pim.extend_from_slice(&1_u16.to_be_bytes()); pim.extend_from_slice(&0_u16.to_be_bytes());
let source = ipv4_octets_or(spec.source.or(spec.rp).or(Some(ctx.src_ip)), [10, 0, 0, 1]);
pim.extend_from_slice(&[1, 0, 0x04, 32]);
pim.extend_from_slice(&source);
} else {
pim.push(0x20);
pim.push(0); pim.extend_from_slice(&[0, 0]);
pim.extend_from_slice(&1_u16.to_be_bytes());
pim.extend_from_slice(&2_u16.to_be_bytes());
pim.extend_from_slice(&spec.holdtime.unwrap_or(105).to_be_bytes());
pim.extend_from_slice(&19_u16.to_be_bytes());
pim.extend_from_slice(&4_u16.to_be_bytes());
pim.extend_from_slice(&spec.dr_priority.unwrap_or(1).to_be_bytes());
}
pim.extend_from_slice(payload);
let cksum = internet_checksum(&[&pim]);
pim[2..4].copy_from_slice(&cksum.to_be_bytes());
let dst_ip = match ctx.dst_ip {
IpAddr::V6(_) => IpAddr::V6(core::net::Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x0d)),
_ => IpAddr::V4(core::net::Ipv4Addr::new(224, 0, 0, 13)),
};
(pim, dst_ip, 103)
}