2020-09-05 03:24:04 +00:00
|
|
|
use crate::{
|
|
|
|
iptables::{self, Proto},
|
|
|
|
startup::Interfaces,
|
|
|
|
};
|
2020-09-09 01:26:40 +00:00
|
|
|
use once_cell::sync::OnceCell;
|
|
|
|
use sled::{Db, Tree};
|
2020-09-05 03:24:04 +00:00
|
|
|
use std::net::Ipv4Addr;
|
|
|
|
|
2020-09-09 01:26:40 +00:00
|
|
|
static RULES_TREE: OnceCell<Tree> = OnceCell::new();
|
|
|
|
|
|
|
|
fn rules_tree(db: &Db) -> &'static Tree {
|
|
|
|
RULES_TREE.get_or_init(|| db.open_tree("rules").unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
2020-09-05 03:24:04 +00:00
|
|
|
pub struct Rule {
|
|
|
|
pub(crate) proto: Proto,
|
|
|
|
pub(crate) port: u16,
|
|
|
|
pub(crate) kind: RuleKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rule {
|
|
|
|
pub(crate) fn as_forward(&self) -> Option<(Ipv4Addr, u16)> {
|
|
|
|
match &self.kind {
|
|
|
|
RuleKind::Forward { dest_ip, dest_port } => Some((*dest_ip, *dest_port)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-09 01:26:40 +00:00
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
2020-09-05 03:24:04 +00:00
|
|
|
#[serde(tag = "type")]
|
|
|
|
pub(crate) enum RuleKind {
|
|
|
|
Accept,
|
2020-09-09 01:26:40 +00:00
|
|
|
Forward {
|
|
|
|
dest_ip: Ipv4Addr,
|
|
|
|
#[serde(with = "serde_with::rust::display_fromstr")]
|
|
|
|
dest_port: u16,
|
|
|
|
},
|
2020-09-05 03:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn apply_all(db: &Db, interfaces: &Interfaces) -> Result<(), anyhow::Error> {
|
|
|
|
for (_, rule) in read(db)? {
|
|
|
|
apply(interfaces, rule).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn read(db: &Db) -> Result<Vec<(String, Rule)>, anyhow::Error> {
|
2020-09-09 01:26:40 +00:00
|
|
|
rules_tree(db)
|
|
|
|
.iter()
|
2020-09-05 03:24:04 +00:00
|
|
|
.map(|res| {
|
|
|
|
let (id, rule) = res?;
|
|
|
|
|
|
|
|
let id = String::from_utf8_lossy(&id).to_string();
|
2020-09-09 01:26:40 +00:00
|
|
|
tide::log::debug!("id: {}", id);
|
|
|
|
tide::log::debug!("rule str: {}", String::from_utf8_lossy(&rule));
|
2020-09-05 03:24:04 +00:00
|
|
|
let rule: Rule = serde_json::from_slice(&rule)?;
|
2020-09-09 01:26:40 +00:00
|
|
|
tide::log::debug!("rule: {:?}", rule);
|
2020-09-05 03:24:04 +00:00
|
|
|
|
|
|
|
Ok((id, rule)) as Result<(String, Rule), anyhow::Error>
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, anyhow::Error>>()
|
|
|
|
}
|
|
|
|
|
2021-02-07 23:10:36 +00:00
|
|
|
pub(crate) async fn delete(db: &Db, rule_id: &str) -> Result<Rule, anyhow::Error> {
|
2020-09-09 01:26:40 +00:00
|
|
|
let tree = rules_tree(db);
|
|
|
|
|
|
|
|
let rule = tree
|
2020-09-05 03:24:04 +00:00
|
|
|
.remove(rule_id.as_bytes())?
|
|
|
|
.ok_or(anyhow::anyhow!("No rule with id {}", rule_id))?;
|
|
|
|
|
2020-09-09 01:26:40 +00:00
|
|
|
tree.flush_async().await?;
|
|
|
|
|
2020-09-05 03:24:04 +00:00
|
|
|
let rule: Rule = serde_json::from_slice(&rule)?;
|
|
|
|
|
|
|
|
Ok(rule)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> {
|
|
|
|
match rule.kind {
|
|
|
|
RuleKind::Accept => {
|
|
|
|
iptables::delete_input_accept(
|
2020-09-15 03:35:56 +00:00
|
|
|
rule.proto,
|
2020-09-05 03:24:04 +00:00
|
|
|
&interfaces.external.interface,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
interfaces.external.mask,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
RuleKind::Forward { dest_ip, dest_port } => {
|
|
|
|
for info in &interfaces.internal {
|
|
|
|
iptables::delete_forward_accept(
|
|
|
|
&interfaces.external.interface,
|
|
|
|
&info.interface,
|
|
|
|
rule.proto,
|
|
|
|
rule.port,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
iptables::delete_forward_prerouting(
|
|
|
|
rule.proto,
|
|
|
|
interfaces.external.ip,
|
|
|
|
interfaces.external.mask,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
dest_port,
|
|
|
|
)
|
|
|
|
.await?;
|
2021-02-07 23:10:36 +00:00
|
|
|
for iface in &interfaces.internal {
|
|
|
|
iptables::delete_forward_postrouting(
|
|
|
|
rule.proto,
|
|
|
|
iface.ip,
|
|
|
|
iface.mask,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
for iface in &interfaces.tunnel {
|
|
|
|
iptables::delete_forward_postrouting(
|
|
|
|
rule.proto,
|
|
|
|
iface.ip,
|
|
|
|
iface.mask,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
2020-09-05 03:24:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-09-09 01:26:40 +00:00
|
|
|
pub(crate) async fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> {
|
|
|
|
let tree = rules_tree(db);
|
|
|
|
|
2020-09-05 03:24:04 +00:00
|
|
|
let s = serde_json::to_string(rule)?;
|
|
|
|
let id = db.generate_id()?;
|
|
|
|
|
2020-09-09 01:26:40 +00:00
|
|
|
tree.insert(rule_id(id).as_bytes(), s.as_bytes())?;
|
|
|
|
|
|
|
|
tree.flush_async().await?;
|
2020-09-05 03:24:04 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> {
|
|
|
|
match rule.kind {
|
|
|
|
RuleKind::Accept => {
|
|
|
|
iptables::input_accept(
|
2020-09-15 03:35:56 +00:00
|
|
|
rule.proto,
|
2020-09-05 03:24:04 +00:00
|
|
|
&interfaces.external.interface,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
interfaces.external.mask,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
RuleKind::Forward { dest_ip, dest_port } => {
|
|
|
|
for info in &interfaces.internal {
|
|
|
|
iptables::forward_accept(
|
|
|
|
&interfaces.external.interface,
|
|
|
|
&info.interface,
|
|
|
|
rule.proto,
|
|
|
|
rule.port,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
iptables::forward_prerouting(
|
|
|
|
rule.proto,
|
|
|
|
interfaces.external.ip,
|
|
|
|
interfaces.external.mask,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
dest_port,
|
|
|
|
)
|
|
|
|
.await?;
|
2021-02-07 23:10:36 +00:00
|
|
|
for iface in &interfaces.internal {
|
|
|
|
iptables::forward_postrouting(
|
|
|
|
rule.proto,
|
|
|
|
iface.ip,
|
|
|
|
iface.mask,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
)
|
2020-09-09 01:26:40 +00:00
|
|
|
.await?;
|
2021-02-07 23:10:36 +00:00
|
|
|
}
|
|
|
|
for iface in &interfaces.tunnel {
|
|
|
|
iptables::forward_postrouting(
|
|
|
|
rule.proto,
|
|
|
|
iface.ip,
|
|
|
|
iface.mask,
|
|
|
|
interfaces.external.ip,
|
|
|
|
rule.port,
|
|
|
|
dest_ip,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
2020-09-05 03:24:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rule_id(id: u64) -> String {
|
|
|
|
format!("rule-{}", id)
|
|
|
|
}
|
2020-09-09 01:26:40 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::{
|
|
|
|
iptables::Proto,
|
|
|
|
rules::{Rule, RuleKind},
|
|
|
|
};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_serialize() {
|
|
|
|
let rule = Rule {
|
|
|
|
proto: Proto::Tcp,
|
|
|
|
port: 53,
|
|
|
|
kind: RuleKind::Forward {
|
|
|
|
dest_ip: "192.168.6.84".parse().unwrap(),
|
|
|
|
dest_port: 53,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let s = serde_qs::to_string(&rule).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
s,
|
|
|
|
"proto=Tcp&port=53&kind[type]=Forward&kind[dest_ip]=192.168.6.84&kind[dest_port]=53"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|