use ipnetwork::{IpNetwork, Ipv4Network};
use rustables::{
data_type::ip_to_vec,
expr::{
Bitwise, Cmp, CmpOp, Counter, HighLevelPayload, ICMPv6HeaderField, IPv4HeaderField,
IcmpCode, Immediate, Meta, MetaType, NetworkHeaderField, TransportHeaderField, VerdictKind,
},
iface_index, Batch, Chain, ChainPolicy, Hook, HookClass, MsgType, ProtocolFamily, Rule, Table,
};
use std::net::Ipv4Addr;
const TABLE_NAME: &str = "example-table";
const OUT_CHAIN_NAME: &str = "chain-for-outgoing-packets";
const IN_CHAIN_NAME: &str = "chain-for-incoming-packets";
fn main() -> Result<(), Error> {
env_logger::init();
let mut batch = Batch::new();
let table = Table::new(ProtocolFamily::Inet).with_name(TABLE_NAME);
batch.add(&table, MsgType::Add);
let mut out_chain = Chain::new(&table).with_name(OUT_CHAIN_NAME);
let mut in_chain = Chain::new(&table).with_name(IN_CHAIN_NAME);
out_chain.set_hook(Hook::new(HookClass::Out, 0));
in_chain.set_hook(Hook::new(HookClass::In, 0));
out_chain.set_policy(ChainPolicy::Accept);
in_chain.set_policy(ChainPolicy::Accept);
batch.add(&out_chain, MsgType::Add);
batch.add(&in_chain, MsgType::Add);
let lo_iface_index = iface_index("lo")?;
let allow_loopback_in_rule = Rule::new(&in_chain)?
.with_expr(Meta::new(MetaType::Iif))
.with_expr(Cmp::new(CmpOp::Eq, lo_iface_index.to_le_bytes()))
.with_expr(Immediate::new_verdict(VerdictKind::Accept));
batch.add(&allow_loopback_in_rule, rustables::MsgType::Add);
let private_net_ip = Ipv4Addr::new(10, 1, 0, 0);
let private_net_prefix = 24;
let private_net = IpNetwork::V4(Ipv4Network::new(private_net_ip, private_net_prefix)?);
let block_out_to_private_net_rule = Rule::new(&out_chain)?
.with_expr(Meta::new(MetaType::NfProto))
.with_expr(Cmp::new(CmpOp::Eq, [libc::NFPROTO_IPV4 as u8]))
.with_expr(HighLevelPayload::Network(NetworkHeaderField::IPv4(IPv4HeaderField::Daddr)).build())
.with_expr(Bitwise::new(ip_to_vec(private_net.mask()), [0u8; 4])?)
.with_expr(Cmp::new(CmpOp::Eq, ip_to_vec(private_net.ip())))
.with_expr(Counter::default())
.with_expr(Immediate::new_verdict(VerdictKind::Accept));
batch.add(&block_out_to_private_net_rule, rustables::MsgType::Add);
let allow_router_solicitation = Rule::new(&out_chain)?
.with_expr(Meta::new(MetaType::NfProto))
.with_expr(Cmp::new(CmpOp::Eq, [libc::NFPROTO_IPV6 as u8]))
.with_expr(Meta::new(MetaType::L4Proto))
.with_expr(Cmp::new(CmpOp::Eq, [libc::IPPROTO_ICMPV6 as u8]))
.with_expr(
HighLevelPayload::Transport(TransportHeaderField::ICMPv6(ICMPv6HeaderField::Type))
.build(),
)
.with_expr(Cmp::new(CmpOp::Eq, [133u8]))
.with_expr(
HighLevelPayload::Transport(TransportHeaderField::ICMPv6(ICMPv6HeaderField::Code))
.build(),
)
.with_expr(Cmp::new(CmpOp::Eq, [IcmpCode::NoRoute as u8]))
.with_expr(Immediate::new_verdict(VerdictKind::Accept));
batch.add(&allow_router_solicitation, rustables::MsgType::Add);
Ok(batch.send()?)
}
#[allow(dead_code)]
#[derive(Debug)]
struct Error(String);
impl<T: std::error::Error> From<T> for Error {
fn from(error: T) -> Self {
Error(error.to_string())
}
}