restore: networking stack files from reflog (Phases 1-6)

Recovered from reflog commits 1c80937e and d0ecc067 after force-push data loss.
Includes: filter/, icmp_error.rs, slaac.rs, bond.rs, bridge.rs, gre.rs, ipip.rs,
qdisc.rs, tun.rs, vlan.rs, vxlan.rs, netfilter.rs, tun.rs, conntrack.rs, nat.rs,
rule.rs, table.rs, redbear-ufw/, dhcpv6d/, netdiag/ — 39 files total.
This commit is contained in:
Red Bear OS
2026-07-08 13:27:49 +03:00
parent 4506bfe02a
commit bb3e36e4e0
40 changed files with 6042 additions and 467 deletions
+349 -23
View File
@@ -4,9 +4,11 @@ use std::rc::Rc;
use smoltcp::phy::{Device, DeviceCapabilities, Medium};
use smoltcp::storage::PacketMetadata;
use smoltcp::time::Instant;
use smoltcp::wire::IpAddress;
use smoltcp::wire::{IpAddress, Ipv4Address, Ipv4Packet, Ipv6Packet};
use self::route_table::RouteTable;
use crate::filter::{FilterTable, Hook, PacketContext, Verdict};
use crate::icmp_error;
use crate::link::DeviceList;
use crate::scheme::Smolnetd;
@@ -49,6 +51,170 @@ impl Router {
can_recv
}
pub fn filter_input(&mut self, filter_table: &Rc<RefCell<FilterTable>>, now: Instant) {
let mut filtered: Vec<Vec<u8>> = Vec::new();
while let Ok(((), packet)) = self.rx_buffer.dequeue() {
if packet.is_empty() {
continue;
}
let mut table = filter_table.borrow_mut();
let context = infer_context(Hook::InputLocal, None, packet);
match table.evaluate(&context, now) {
Verdict::Accept => {
drop(table);
filtered.push(packet.to_vec());
}
Verdict::Reject => {
drop(table);
let err_fn = if packet[0] >> 4 == 6 {
icmp_error::build_icmpv6_port_unreachable
} else {
icmp_error::build_icmpv4_port_unreachable
};
if let Some(err_pkt) = err_fn(packet) {
if let Ok(buf) = self.tx_buffer.enqueue(err_pkt.len(), ()) {
buf.copy_from_slice(&err_pkt);
}
}
debug!("filter: rejected INPUT packet {} → {}", context.src_addr, context.dst_addr);
}
Verdict::Drop | Verdict::Log => {
drop(table);
debug!("filter: dropped INPUT packet {} → {}", context.src_addr, context.dst_addr);
}
}
}
for packet in filtered {
let Ok(buf) = self.rx_buffer.enqueue(packet.len(), ()) else {
break;
};
buf.copy_from_slice(&packet);
}
}
pub fn forward_packets(&mut self, filter_table: &Rc<RefCell<FilterTable>>, now: Instant) {
let mut forwarded: Vec<Vec<u8>> = Vec::new();
let mut local: Vec<Vec<u8>> = Vec::new();
while let Ok(((), packet)) = self.rx_buffer.dequeue() {
let packet_data: &[u8] = packet;
if packet_data.is_empty() || packet_data[0] >> 4 != 4 {
local.push(packet_data.to_vec());
continue;
}
let Ok(ipv4) = Ipv4Packet::new_checked(packet_data) else {
local.push(packet_data.to_vec());
continue;
};
let dst = IpAddress::Ipv4(ipv4.dst_addr());
let is_broadcast = ipv4.dst_addr().is_broadcast() || dst.is_multicast();
if is_broadcast {
local.push(packet.to_vec());
continue;
}
let route_info: Option<(Rc<str>, Option<IpAddress>)> = self.route_table.borrow()
.lookup_rule(&dst)
.map(|r| (r.dev.clone(), r.via));
let Some((dev_name, _via)) = route_info else {
local.push(packet.to_vec());
continue;
};
let mut buf = packet.to_vec();
let context = infer_context(Hook::Forward, Some(dev_name.clone()), &buf);
{
let mut table = filter_table.borrow_mut();
if table.evaluate(&context, now) == Verdict::Drop {
debug!("filter: dropped FORWARD packet");
continue;
}
if let Some((trans_addr, _)) = table.nat_table.lookup_snat(
Hook::Forward,
IpAddress::Ipv4(ipv4.src_addr()),
dst,
) {
let IpAddress::Ipv4(new_src) = trans_addr else { continue; };
let _ = crate::filter::rewrite_src_ipv4(&mut buf, new_src);
}
}
let ttl = ipv4.hop_limit();
if ttl <= 1 {
debug!("forward: TTL expired");
if let Some(error_pkt) = icmp_error::build_icmpv4_time_exceeded(&buf) {
if let Ok(buf) = self.tx_buffer.enqueue(error_pkt.len(), ()) {
buf.copy_from_slice(&error_pkt);
}
}
continue;
}
buf[8] = ttl - 1;
if let Ok(mut pkt) = Ipv4Packet::new_checked(&mut buf) {
pkt.fill_checksum();
}
forwarded.push(buf);
}
for packet in local {
let Ok(buf) = self.rx_buffer.enqueue(packet.len(), ()) else {
break;
};
buf.copy_from_slice(&packet);
}
for packet in forwarded {
let Ok(buf) = self.tx_buffer.enqueue(packet.len(), ()) else {
break;
};
buf.copy_from_slice(&packet);
}
}
pub fn filter_output(&self, packet: &[u8], filter_table: &Rc<RefCell<FilterTable>>, now: Instant) -> Verdict {
if packet.is_empty() {
return Verdict::Accept;
}
let context = infer_context(Hook::OutputLocal, None, packet);
filter_table.borrow_mut().evaluate(&context, now)
}
fn apply_snat(
&self,
packet: &mut [u8],
filter_table: &Rc<RefCell<FilterTable>>,
) {
if packet.is_empty() {
return;
}
let version = packet[0] >> 4;
if version != 4 {
return;
}
let Ok(ipv4) = Ipv4Packet::new_checked(&*packet) else {
return;
};
let src = IpAddress::Ipv4(ipv4.src_addr());
let dst = IpAddress::Ipv4(ipv4.dst_addr());
let table = filter_table.borrow();
let snat = table.nat_table.lookup_snat(
crate::filter::Hook::OutputLocal,
src,
dst,
);
drop(table);
if let Some((trans_addr, _trans_port)) = snat {
let IpAddress::Ipv4(new_src) = trans_addr else {
return;
};
crate::filter::rewrite_src_ipv4(packet, new_src);
}
}
pub fn poll(&mut self, now: Instant) {
for dev in self.devices.borrow_mut().iter_mut() {
if self.rx_buffer.is_full() {
@@ -72,41 +238,104 @@ impl Router {
}
}
pub fn dispatch(&mut self, now: Instant) {
pub fn dispatch(&mut self, now: Instant, filter_table: &Rc<RefCell<FilterTable>>) {
let mut packets: Vec<Vec<u8>> = Vec::new();
while let Ok(((), packet)) = self.tx_buffer.dequeue() {
if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) {
let dst_addr = IpAddress::Ipv4(packet.dst_addr());
if packet.dst_addr().is_broadcast() {
let buf = packet.into_inner();
for dev in self.devices.borrow_mut().iter_mut() {
dev.send(dst_addr, buf, now)
}
} else {
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);
if !packet.is_empty() {
packets.push(packet.to_vec());
}
}
for packet in packets {
if packet.is_empty() {
continue;
}
match packet[0] >> 4 {
4 => {
let mut packet_buf = packet;
let Ok(mut ipv4_pkt) = smoltcp::wire::Ipv4Packet::new_checked(&mut packet_buf)
else {
continue;
};
let dst_addr = IpAddress::Ipv4(ipv4_pkt.dst_addr());
let src_addr = ipv4_pkt.src_addr();
let next_hop = match rule.via {
Some(via) => via,
None => dst_addr,
let (next_hop, src_rule, dev_name) = {
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())
};
if ipv4_pkt.dst_addr().is_broadcast() {
let buf = ipv4_pkt.into_inner();
if self.filter_output(buf, filter_table, now) == Verdict::Drop {
debug!("filter: dropped OUTPUT broadcast IPv4 packet");
continue;
}
self.apply_snat(buf, filter_table);
for dev in self.devices.borrow_mut().iter_mut() {
dev.send(dst_addr, buf, now);
}
continue;
}
if let IpAddress::Ipv4(src) = src_rule {
if src != src_addr {
ipv4_pkt.set_src_addr(src);
ipv4_pkt.fill_checksum();
}
}
let buf = ipv4_pkt.into_inner();
let mut devices = self.devices.borrow_mut();
let Some(dev) = devices.get_mut(&rule.dev) else {
warn!("Device {} not found", rule.dev);
let Some(dev) = devices.get_mut(&dev_name) else {
warn!("Device {} not found", dev_name);
// TODO: Remove route if device doesn't exist anymore ?
continue;
};
if self.filter_output(buf, filter_table, now) == Verdict::Drop {
debug!("filter: dropped OUTPUT IPv4 packet");
continue;
}
self.apply_snat(buf, filter_table);
dev.send(next_hop, buf, now);
}
6 => {
let Ok(ipv6_pkt) = smoltcp::wire::Ipv6Packet::new_checked(&packet) else {
continue;
};
let dst_addr = IpAddress::Ipv6(ipv6_pkt.dst_addr());
let IpAddress::Ipv4(src) = rule.src;
if src != packet.src_addr() {
packet.set_src_addr(src);
packet.fill_checksum()
let (next_hop, dev_name) = {
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())
};
let mut devices = self.devices.borrow_mut();
let Some(dev) = devices.get_mut(&dev_name) else {
warn!("Device {} not found", dev_name);
continue;
};
if self.filter_output(&packet, filter_table, now) == Verdict::Drop {
debug!("filter: dropped OUTPUT IPv6 packet");
continue;
}
dev.send(next_hop, packet.into_inner(), now);
dev.send(next_hop, &packet, now);
}
version => {
debug!("Dropped packet with unknown IP version: {}", version);
}
}
}
@@ -188,3 +417,100 @@ impl<'a> smoltcp::phy::RxToken for RxToken<'a> {
f(buf)
}
}
fn infer_context(
hook: Hook,
_dev_name: Option<Rc<str>>,
packet: &[u8],
) -> PacketContext<'_> {
let version = if packet.is_empty() { 0 } else { packet[0] >> 4 };
match version {
4 => {
if let Ok(ipv4) = Ipv4Packet::new_checked(packet) {
let (src_port, dst_port) = parse_ports(&ipv4.next_header(), ipv4.payload());
PacketContext {
hook,
in_dev: None,
out_dev: None,
src_addr: IpAddress::Ipv4(ipv4.src_addr()),
dst_addr: IpAddress::Ipv4(ipv4.dst_addr()),
protocol: ipv4.next_header().into(),
src_port,
dst_port,
packet,
}
} else {
PacketContext {
hook,
in_dev: None,
out_dev: None,
src_addr: IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED),
dst_addr: IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED),
protocol: 0,
src_port: None,
dst_port: None,
packet,
}
}
}
6 => {
if let Ok(ipv6) = Ipv6Packet::new_checked(packet) {
let nh = ipv6.next_header();
let payload_start = 40; // fixed IPv6 header
let payload = if packet.len() > payload_start {
&packet[payload_start..]
} else {
&[]
};
let (src_port, dst_port) = parse_ports(&nh, payload);
PacketContext {
hook,
in_dev: None,
out_dev: None,
src_addr: IpAddress::Ipv6(ipv6.src_addr()),
dst_addr: IpAddress::Ipv6(ipv6.dst_addr()),
protocol: nh.into(),
src_port,
dst_port,
packet,
}
} else {
PacketContext {
hook,
in_dev: None,
out_dev: None,
src_addr: IpAddress::Ipv6(smoltcp::wire::Ipv6Address::UNSPECIFIED),
dst_addr: IpAddress::Ipv6(smoltcp::wire::Ipv6Address::UNSPECIFIED),
protocol: 0,
src_port: None,
dst_port: None,
packet,
}
}
}
_ => PacketContext {
hook,
in_dev: None,
out_dev: None,
src_addr: IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED),
dst_addr: IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED),
protocol: 0,
src_port: None,
dst_port: None,
packet,
},
}
}
fn parse_ports(protocol: &smoltcp::wire::IpProtocol, payload: &[u8]) -> (Option<u16>, Option<u16>) {
let proto_byte: u8 = (*protocol).into();
if proto_byte != 6 && proto_byte != 17 && proto_byte != 58 {
return (None, None);
}
if payload.len() < 4 {
return (None, None);
}
let src = u16::from_be_bytes([payload[0], payload[1]]);
let dst = u16::from_be_bytes([payload[2], payload[3]]);
(Some(src), Some(dst))
}