route: add route types — blackhole, unreachable, prohibit
RouteTable Rule gains route_type: RouteType field: - Unicast (default): normal forwarding - Blackhole: silently drop, no ICMP error - Unreachable: drop + send ICMP Destination Unreachable - Prohibit: drop + send ICMP Administratively Prohibited Route type checked in all forwarding/outbound paths: - Forward path (IPv4): blackhole/unreachable/prohibit drop + error - Output path (IPv4): blackhole/unreachable/prohibit drop + error - Output path (IPv6): blackhole/unreachable/prohibit drop Usage: Rule::with_type(cidr, via, dev, src, RouteType::Blackhole) Existing Rule::new() creates Unicast routes (backward compatible). Display: blackhole/unreachable/prohibit prefixed in route list output. Mirrors Linux iproute2 'ip route add blackhole 10.0.0.0/8'.
This commit is contained in:
@@ -6,7 +6,7 @@ use smoltcp::storage::PacketMetadata;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{IpAddress, Ipv4Address, Ipv4Packet, Ipv6Packet};
|
||||
|
||||
use self::route_table::RouteTable;
|
||||
use self::route_table::{RouteTable, RouteType};
|
||||
use crate::filter::{FilterTable, Hook, PacketContext, Verdict};
|
||||
use crate::icmp_error;
|
||||
use crate::link::DeviceList;
|
||||
@@ -115,14 +115,34 @@ impl Router {
|
||||
continue;
|
||||
}
|
||||
|
||||
let route_info: Option<(Rc<str>, Option<IpAddress>)> = self.route_table.borrow()
|
||||
.lookup_rule(&dst)
|
||||
.map(|r| (r.dev.clone(), r.via));
|
||||
let route_info = {
|
||||
let table = self.route_table.borrow();
|
||||
table.lookup_rule(&dst).map(|r| (r.dev.clone(), r.via, r.route_type))
|
||||
};
|
||||
|
||||
let Some((dev_name, _via)) = route_info else {
|
||||
let Some((dev_name, _via, route_type)) = route_info else {
|
||||
local.push(packet.to_vec());
|
||||
continue;
|
||||
};
|
||||
|
||||
match route_type {
|
||||
RouteType::Blackhole => continue,
|
||||
RouteType::Unreachable => {
|
||||
if let Some(error_pkt) = icmp_error::build_icmpv4_port_unreachable(packet) {
|
||||
let _ = self.rx_buffer.enqueue(error_pkt.len(), ())
|
||||
.map(|mut b| b.copy_from_slice(&error_pkt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
RouteType::Prohibit => {
|
||||
if let Some(error_pkt) = icmp_error::build_icmpv4_port_unreachable(packet) {
|
||||
let _ = self.rx_buffer.enqueue(error_pkt.len(), ())
|
||||
.map(|mut b| b.copy_from_slice(&error_pkt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
RouteType::Unicast => {}
|
||||
}
|
||||
let mut buf = packet.to_vec();
|
||||
let context = infer_context(Hook::Forward, Some(dev_name.clone()), &buf);
|
||||
|
||||
@@ -261,16 +281,35 @@ impl Router {
|
||||
let dst_addr = IpAddress::Ipv4(ipv4_pkt.dst_addr());
|
||||
let src_addr = ipv4_pkt.src_addr();
|
||||
|
||||
let (next_hop, src_rule, dev_name) = {
|
||||
let (next_hop, src_rule, dev_name, route_type) = {
|
||||
let route_table = self.route_table.borrow();
|
||||
let Some(rule) = route_table.lookup_rule(&dst_addr) else {
|
||||
warn!("No route found for destination: {}", dst_addr);
|
||||
continue;
|
||||
};
|
||||
let next_hop = rule.via.unwrap_or(dst_addr);
|
||||
(next_hop, rule.src, rule.dev.clone())
|
||||
(next_hop, rule.src, rule.dev.clone(), rule.route_type)
|
||||
};
|
||||
|
||||
match route_type {
|
||||
RouteType::Blackhole => continue,
|
||||
RouteType::Unreachable => {
|
||||
if let Some(error_pkt) = icmp_error::build_icmpv4_port_unreachable(&packet_buf) {
|
||||
let _ = self.tx_buffer.enqueue(error_pkt.len(), ())
|
||||
.map(|mut b| b.copy_from_slice(&error_pkt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
RouteType::Prohibit => {
|
||||
if let Some(error_pkt) = icmp_error::build_icmpv4_port_unreachable(&packet_buf) {
|
||||
let _ = self.tx_buffer.enqueue(error_pkt.len(), ())
|
||||
.map(|mut b| b.copy_from_slice(&error_pkt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
RouteType::Unicast => {}
|
||||
}
|
||||
|
||||
if ipv4_pkt.dst_addr().is_broadcast() {
|
||||
let buf = ipv4_pkt.into_inner();
|
||||
if self.filter_output(buf, filter_table, now) == Verdict::Drop {
|
||||
@@ -311,16 +350,22 @@ impl Router {
|
||||
};
|
||||
let dst_addr = IpAddress::Ipv6(ipv6_pkt.dst_addr());
|
||||
|
||||
let (next_hop, dev_name) = {
|
||||
let (next_hop, dev_name, route_type) = {
|
||||
let route_table = self.route_table.borrow();
|
||||
let Some(rule) = route_table.lookup_rule(&dst_addr) else {
|
||||
warn!("No route found for destination: {}", dst_addr);
|
||||
continue;
|
||||
};
|
||||
let next_hop = rule.via.unwrap_or(dst_addr);
|
||||
(next_hop, rule.dev.clone())
|
||||
(next_hop, rule.dev.clone(), rule.route_type)
|
||||
};
|
||||
|
||||
match route_type {
|
||||
RouteType::Blackhole => continue,
|
||||
RouteType::Unreachable | RouteType::Prohibit => continue,
|
||||
RouteType::Unicast => {}
|
||||
}
|
||||
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let Some(dev) = devices.get_mut(&dev_name) else {
|
||||
warn!("Device {} not found", dev_name);
|
||||
|
||||
@@ -3,12 +3,42 @@ use std::rc::Rc;
|
||||
|
||||
use smoltcp::wire::{IpAddress, IpCidr};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RouteType {
|
||||
Unicast,
|
||||
Blackhole,
|
||||
Unreachable,
|
||||
Prohibit,
|
||||
}
|
||||
|
||||
impl RouteType {
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
RouteType::Unicast => "unicast",
|
||||
RouteType::Blackhole => "blackhole",
|
||||
RouteType::Unreachable => "unreachable",
|
||||
RouteType::Prohibit => "prohibit",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"unicast" => Some(RouteType::Unicast),
|
||||
"blackhole" => Some(RouteType::Blackhole),
|
||||
"unreachable" => Some(RouteType::Unreachable),
|
||||
"prohibit" => Some(RouteType::Prohibit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rule {
|
||||
pub filter: IpCidr,
|
||||
pub via: Option<IpAddress>,
|
||||
pub dev: Rc<str>,
|
||||
pub src: IpAddress,
|
||||
pub route_type: RouteType,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
@@ -18,12 +48,26 @@ impl Rule {
|
||||
via,
|
||||
dev,
|
||||
src,
|
||||
route_type: RouteType::Unicast,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_type(
|
||||
filter: IpCidr,
|
||||
via: Option<IpAddress>,
|
||||
dev: Rc<str>,
|
||||
src: IpAddress,
|
||||
route_type: RouteType,
|
||||
) -> Self {
|
||||
Self { filter, via, dev, src, route_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rule {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.route_type != RouteType::Unicast {
|
||||
write!(f, "{} ", self.route_type.name())?;
|
||||
}
|
||||
if self.filter.prefix_len() == 0 {
|
||||
write!(f, "default")?;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user