use crate::ast::*;
use crate::builders::WireContext;
use alloc::vec::Vec;
use core::net::IpAddr;
fn encode_bgp_nlri(prefix_str: &str) -> Vec<u8> {
let s = prefix_str.trim();
let (ip_str, len_opt) = match s.split_once('/') {
Some((ip, l)) => (ip.trim(), l.trim().parse::<u8>().ok()),
None => (s, None),
};
if let Ok(ip) = ip_str.parse::<IpAddr>() {
match ip {
IpAddr::V4(v4) => {
let plen = len_opt.unwrap_or(24).min(32);
let byte_len = (plen as usize).div_ceil(8);
let mut nlri = Vec::with_capacity(1 + byte_len);
nlri.push(plen);
nlri.extend_from_slice(&v4.octets()[..byte_len]);
return nlri;
}
IpAddr::V6(v6) => {
let plen = len_opt.unwrap_or(64).min(128);
let byte_len = (plen as usize).div_ceil(8);
let mut nlri = Vec::with_capacity(1 + byte_len);
nlri.push(plen);
nlri.extend_from_slice(&v6.octets()[..byte_len]);
return nlri;
}
}
}
alloc::vec![24, 10, 0, 0]
}
pub fn build_bgp_payload(ctx: &WireContext, spec: &BgpSpec, payload: &[u8]) -> (Vec<u8>, u16) {
let bgp_type = spec.bgp_type.unwrap_or(if spec.prefix.is_some() || spec.nexthop.is_some() {
BgpType::Update
} else {
BgpType::Keepalive
});
let mut bgp = Vec::new();
match bgp_type {
BgpType::Keepalive => {
bgp.reserve(19);
bgp.extend_from_slice(&[0xFF; 16]); bgp.extend_from_slice(&19_u16.to_be_bytes()); bgp.push(4); }
BgpType::Open => {
bgp.reserve(29);
bgp.extend_from_slice(&[0xFF; 16]);
bgp.extend_from_slice(&29_u16.to_be_bytes()); bgp.push(1); bgp.push(4); let asn = (spec.asn.unwrap_or(65001) & 0xFFFF) as u16;
bgp.extend_from_slice(&asn.to_be_bytes());
bgp.extend_from_slice(&spec.holdtime.unwrap_or(180).to_be_bytes());
let bgpid = match spec.bgpid {
Some(IpAddr::V4(b)) => b.octets(),
_ => match ctx.src_ip {
IpAddr::V4(s) => s.octets(),
_ => [192, 0, 2, 1],
},
};
bgp.extend_from_slice(&bgpid);
bgp.push(0); }
BgpType::Update => {
let mut path_attrs = Vec::new();
if spec.nexthop.is_some() || spec.prefix.is_some() {
path_attrs.extend_from_slice(&[0x40, 0x01, 0x01, 0x00]);
if let Some(asn) = spec.asn {
let asn16 = (asn & 0xFFFF) as u16;
path_attrs.extend_from_slice(&[0x40, 0x02, 0x04, 0x02, 0x01]);
path_attrs.extend_from_slice(&asn16.to_be_bytes());
} else {
path_attrs.extend_from_slice(&[0x40, 0x02, 0x00]);
}
let nh_octets = match spec.nexthop.unwrap_or(ctx.src_ip) {
IpAddr::V4(v4) => v4.octets(),
IpAddr::V6(v6) => {
let o = v6.octets();
[o[12], o[13], o[14], o[15]]
}
};
path_attrs.extend_from_slice(&[0x40, 0x03, 0x04]);
path_attrs.extend_from_slice(&nh_octets);
}
let nlri = if let Some(prefix_str) = spec.prefix {
encode_bgp_nlri(prefix_str)
} else {
Vec::new()
};
let total_len = (23 + path_attrs.len() + nlri.len() + payload.len()) as u16;
bgp.reserve(total_len as usize);
bgp.extend_from_slice(&[0xFF; 16]);
bgp.extend_from_slice(&total_len.to_be_bytes());
bgp.push(2); bgp.extend_from_slice(&0_u16.to_be_bytes()); bgp.extend_from_slice(&(path_attrs.len() as u16).to_be_bytes()); bgp.extend_from_slice(&path_attrs);
bgp.extend_from_slice(&nlri);
bgp.extend_from_slice(payload);
}
BgpType::Notification => {
let total_len = (21 + payload.len()) as u16;
bgp.reserve(total_len as usize);
bgp.extend_from_slice(&[0xFF; 16]);
bgp.extend_from_slice(&total_len.to_be_bytes());
bgp.push(3); bgp.push(6); bgp.push(0);
bgp.extend_from_slice(payload);
}
}
(bgp, 179)
}