//! The operator's policy script, asked about routes with whatever //! attributes a peer can send. The example script (testdata/policy.rhai) //! touches most of what a script can; whatever the route, a function //! either answers or fails, and never panics. What it changes must still //! be attributes that can be written. #![no_main] use std::sync::{Arc, LazyLock}; use bgp_packet::message::{Message, PathAttribute, UpdateMessage}; use bgp_policy::{Direction, Hook, Peer, Policy, Subject}; use bgp_primitives::ip_prefix::IpPrefix; use bgp_primitives::parser::ParserContext; use bytes::BytesMut; use libfuzzer_sys::fuzz_target; static HOOKS: LazyLock<(Arc, Vec)> = LazyLock::new(|| { let policy = Policy::compile(include_str!("../../testdata/policy.rhai")).unwrap(); let hooks = ["from_peer", "to_peer"] .into_iter() .map(|name| policy.hook(name).unwrap()) .collect(); (policy, hooks) }); fuzz_target!(|data: &[u8]| { let Some((selector, attributes)) = data.split_first() else { return; }; let ctx = ParserContext { four_octet_asn: true, ibgp: false, add_path: Vec::new(), }; // An UPDATE with no routes withdrawn, these attributes, and one route. let mut body = vec![0, 0]; let attributes = &attributes[..attributes.len().min(4000)]; body.extend_from_slice(&(attributes.len() as u16).to_be_bytes()); body.extend_from_slice(attributes); body.extend_from_slice(&[24, 203, 0, 113]); let Ok(decoded) = UpdateMessage::decode(&ctx, &body) else { return; }; let attributes = decoded.update.path_attributes; let prefix = IpPrefix::try_from("203.0.113.0/24").unwrap(); let roles = ["", "customer", "peer", "provider", "route_server"]; let peer = Peer::new( "backup-transit", std::net::Ipv4Addr::new(192, 0, 2, 2).into(), 64602, 65000, roles[usize::from(selector >> 1) % roles.len()], ); let (hook, direction) = if selector & 1 == 0 { (&HOOKS.1[0], Direction::Import) } else { (&HOOKS.1[1], Direction::Export) }; let subject = Subject { prefix: &prefix, attributes: &attributes, next_hop: None, direction, }; let Ok(verdict) = hook.run(&peer, &subject) else { return; }; if let Some(changed) = verdict.attributes { let changed: Vec = changed; let update = UpdateMessage { withdrawn_routes: vec![], path_attributes: changed, announced_routes: vec![], }; let mut wire = BytesMut::new(); // Too long to send is a thing that can be; anything else is not. if let Err(e) = Message::Update(update).to_wire(&ctx, &mut wire) { assert!( matches!(e, bgp_primitives::parser::EncodeError::TooLong { .. }), "{e}" ); } } });