Initial support for VLANs

This commit is contained in:
Aode (Lion) 2021-10-17 12:54:47 -05:00
parent 850a45a727
commit ccf6b29423
5 changed files with 292 additions and 259 deletions

View file

@ -3,6 +3,9 @@ external = "eth[0-9]+"
internal = [ internal = [
"enp1s0" "enp1s0"
] ]
vlan = [
"vlan[0-9]+"
]
tunnel = [ tunnel = [
"wg[0-9]+" "wg[0-9]+"
] ]

View file

@ -25,43 +25,7 @@ impl fmt::Display for Proto {
} }
} }
pub(crate) async fn input_accept( pub(crate) async fn input(
proto: Proto,
external_interface: &str,
external_ip: Ipv4Addr,
external_port: u16,
external_mask: u8,
) -> Result<(), anyhow::Error> {
input(
proto,
external_interface,
external_ip,
external_port,
external_mask,
move |cmd| cmd.arg("-I"),
)
.await
}
pub(crate) async fn delete_input_accept(
proto: Proto,
external_interface: &str,
external_ip: Ipv4Addr,
external_port: u16,
external_mask: u8,
) -> Result<(), anyhow::Error> {
input(
proto,
external_interface,
external_ip,
external_port,
external_mask,
move |cmd| cmd.arg("-D"),
)
.await
}
async fn input(
proto: Proto, proto: Proto,
external_interface: &str, external_interface: &str,
external_ip: Ipv4Addr, external_ip: Ipv4Addr,
@ -93,39 +57,36 @@ async fn input(
.await .await
} }
pub(crate) async fn forward_accept( pub(crate) async fn outbound_forward_established(
external_interface: &str, external_interface: &str,
internal_interface: &str, internal_interface: &str,
proto: Proto, proto: Proto,
internal_port: u16, internal_port: u16,
func: impl Fn(&mut Command) -> &mut Command,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
forward( iptables_filter(move |cmd| {
external_interface, func(cmd).args(&[
internal_interface, "FORWARD",
proto, "-i",
internal_port, internal_interface,
move |cmd| cmd.arg("-I"), "-o",
) external_interface,
"-p",
proto.as_iptables_str(),
"--sport",
&internal_port.to_string(),
"-m",
"conntrack",
"--ctstate",
"ESTABLISHED,RELATED",
"-j",
"ACCEPT",
])
})
.await .await
} }
pub(crate) async fn delete_forward_accept( pub(crate) async fn forward(
external_interface: &str,
internal_interface: &str,
proto: Proto,
internal_port: u16,
) -> Result<(), anyhow::Error> {
forward(
external_interface,
internal_interface,
proto,
internal_port,
move |cmd| cmd.arg("-D"),
)
.await
}
async fn forward(
external_interface: &str, external_interface: &str,
internal_interface: &str, internal_interface: &str,
proto: Proto, proto: Proto,
@ -154,47 +115,7 @@ async fn forward(
.await .await
} }
pub(crate) async fn forward_postrouting( pub(crate) async fn forward_postrouting_snat(
proto: Proto,
internal_ip: Ipv4Addr,
internal_mask: u8,
internal_port: u16,
external_ip: Ipv4Addr,
destination_ip: Ipv4Addr,
) -> Result<(), anyhow::Error> {
forward_postrouting_snat(
proto,
internal_ip,
internal_mask,
internal_port,
external_ip,
destination_ip,
|cmd| cmd.arg("-I"),
)
.await
}
pub(crate) async fn delete_forward_postrouting(
proto: Proto,
internal_ip: Ipv4Addr,
internal_mask: u8,
internal_port: u16,
external_ip: Ipv4Addr,
destination_ip: Ipv4Addr,
) -> Result<(), anyhow::Error> {
forward_postrouting_snat(
proto,
internal_ip,
internal_mask,
internal_port,
external_ip,
destination_ip,
|cmd| cmd.arg("-D"),
)
.await
}
async fn forward_postrouting_snat(
proto: Proto, proto: Proto,
internal_ip: Ipv4Addr, internal_ip: Ipv4Addr,
internal_mask: u8, internal_mask: u8,
@ -229,47 +150,7 @@ async fn forward_postrouting_snat(
.await .await
} }
pub(crate) async fn forward_prerouting( pub(crate) async fn forward_prerouting_dnat(
proto: Proto,
external_ip: Ipv4Addr,
external_mask: u8,
external_port: u16,
destination_ip: Ipv4Addr,
destination_port: u16,
) -> Result<(), anyhow::Error> {
forward_prerouting_dnat(
proto,
external_ip,
external_mask,
external_port,
destination_ip,
destination_port,
|cmd| cmd.arg("-I"),
)
.await
}
pub(crate) async fn delete_forward_prerouting(
proto: Proto,
external_ip: Ipv4Addr,
external_mask: u8,
external_port: u16,
destination_ip: Ipv4Addr,
destination_port: u16,
) -> Result<(), anyhow::Error> {
forward_prerouting_dnat(
proto,
external_ip,
external_mask,
external_port,
destination_ip,
destination_port,
|cmd| cmd.arg("-D"),
)
.await
}
async fn forward_prerouting_dnat(
proto: Proto, proto: Proto,
external_ip: Ipv4Addr, external_ip: Ipv4Addr,
external_mask: u8, external_mask: u8,

View file

@ -78,69 +78,103 @@ pub(crate) async fn delete(db: &Db, rule_id: &str) -> Result<Rule, anyhow::Error
Ok(rule) Ok(rule)
} }
pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> { async fn set_rule(
interfaces: &Interfaces,
rule: Rule,
func: impl Fn(&mut async_process::Command) -> &mut async_process::Command + Copy,
) -> anyhow::Result<()> {
match rule.kind { match rule.kind {
RuleKind::Accept => { RuleKind::Accept => {
iptables::delete_input_accept( iptables::input(
rule.proto, rule.proto,
&interfaces.external.interface, &interfaces.external.interface,
interfaces.external.ip, interfaces.external.ip,
rule.port, rule.port,
interfaces.external.mask, interfaces.external.mask,
func,
) )
.await?; .await?;
} }
RuleKind::Forward { dest_ip, dest_port } => { RuleKind::Forward { dest_ip, dest_port } => {
for info in &interfaces.internal { let internal_iface = interfaces
iptables::delete_forward_accept( .internal
&interfaces.external.interface, .iter()
&info.interface, .chain(&interfaces.tunnel)
rule.proto, .chain(&interfaces.vlan)
dest_port, .find(|info| mask_matches(info.ip, info.mask, dest_ip));
)
.await?; let internal_iface = if let Some(internal_iface) = internal_iface {
} internal_iface
iptables::delete_forward_prerouting( } else {
return Ok(());
};
iptables::forward(
&interfaces.external.interface,
&internal_iface.interface,
rule.proto,
dest_port,
func,
)
.await?;
iptables::forward_prerouting_dnat(
rule.proto, rule.proto,
interfaces.external.ip, interfaces.external.ip,
interfaces.external.mask, interfaces.external.mask,
rule.port, rule.port,
dest_ip, dest_ip,
dest_port, dest_port,
func,
)
.await?;
iptables::forward_postrouting_snat(
rule.proto,
internal_iface.ip,
internal_iface.mask,
dest_port,
interfaces.external.ip,
dest_ip,
func,
) )
.await?; .await?;
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = interfaces
.nats
.iter()
.any(|nat_iface| *nat_iface == iface.interface);
for iface in interfaces
.internal
.iter()
.chain(&interfaces.tunnel)
.chain(&interfaces.vlan)
.filter(|iface| *iface != internal_iface)
{
let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| { let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| {
*nat_iface != iface.interface *nat_iface == iface.interface
&& interfaces || *nat_iface != iface.interface
.internal && interfaces
.iter() .internal
.chain(interfaces.tunnel.iter()) .iter()
.any(|other_iface| { .chain(&interfaces.tunnel)
*nat_iface == other_iface.interface .chain(&interfaces.vlan)
&& other_iface.ip == iface.ip .any(|other_iface| {
&& other_iface.mask == iface.mask *nat_iface == other_iface.interface
}) && other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
})
}); });
if is_nat_iface { if !has_nat_subnet {
iptables::delete_forward_prerouting( iptables::forward(
rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port, &iface.interface,
&internal_iface.interface,
rule.proto,
dest_port,
func,
) )
.await?; .await?;
} else if !has_nat_subnet { iptables::outbound_forward_established(
iptables::delete_forward_postrouting( &iface.interface,
&internal_iface.interface,
rule.proto, rule.proto,
iface.ip,
iface.mask,
dest_port, dest_port,
interfaces.external.ip, func,
dest_ip,
) )
.await?; .await?;
} }
@ -151,6 +185,14 @@ pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), any
Ok(()) Ok(())
} }
pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> {
set_rule(interfaces, rule, |cmd| cmd.arg("-I")).await
}
pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> {
set_rule(interfaces, rule, |cmd| cmd.arg("-D")).await
}
pub(crate) async fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> { pub(crate) async fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> {
let tree = rules_tree(db); let tree = rules_tree(db);
@ -164,77 +206,29 @@ pub(crate) async fn save(db: &Db, rule: &Rule) -> Result<(), anyhow::Error> {
Ok(()) Ok(())
} }
pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), anyhow::Error> { // 255 - 2^n where n is the index
match rule.kind { const MASK_BITS: [u8; 7] = [254, 252, 248, 240, 224, 192, 128];
RuleKind::Accept => {
iptables::input_accept(
rule.proto,
&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,
dest_port,
)
.await?;
}
iptables::forward_prerouting(
rule.proto,
interfaces.external.ip,
interfaces.external.mask,
rule.port,
dest_ip,
dest_port,
)
.await?;
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = interfaces
.nats
.iter()
.any(|nat_iface| *nat_iface == iface.interface);
let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| { fn mask_matches(mask_ip: Ipv4Addr, netmask: u8, rule_ip: Ipv4Addr) -> bool {
*nat_iface != iface.interface let mut count: u8 = 8;
&& interfaces let mut matches = true;
.internal
.iter()
.chain(interfaces.tunnel.iter())
.any(|other_iface| {
*nat_iface == other_iface.interface
&& other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
})
});
if is_nat_iface { for (mask_byte, rule_byte) in mask_ip.octets().iter().zip(&rule_ip.octets()) {
iptables::forward_prerouting( if count <= netmask {
rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port, matches = matches && mask_byte == rule_byte
) } else {
.await?; let remaining = count.saturating_sub(netmask);
} else if !has_nat_subnet { if remaining < 8 {
iptables::forward_postrouting( let mask = MASK_BITS[remaining.saturating_sub(1) as usize];
rule.proto,
iface.ip, matches = matches && (mask_byte & mask) == (rule_byte & mask);
iface.mask,
dest_port,
interfaces.external.ip,
dest_ip,
)
.await?;
}
} }
} }
count += 8;
} }
Ok(()) matches
} }
fn rule_id(id: u64) -> String { fn rule_id(id: u64) -> String {
@ -243,11 +237,58 @@ fn rule_id(id: u64) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::mask_matches;
use crate::{ use crate::{
iptables::Proto, iptables::Proto,
rules::{Rule, RuleKind}, rules::{Rule, RuleKind},
}; };
#[test]
fn ips_match_mask() {
let tests = [
("192.168.6.0", 24, "192.168.6.1"),
("192.168.6.0", 24, "192.168.6.254"),
("192.168.6.0", 25, "192.168.6.1"),
("192.168.6.0", 26, "192.168.6.1"),
("192.168.6.0", 27, "192.168.6.1"),
("192.168.6.0", 31, "192.168.6.1"),
("192.168.6.0", 32, "192.168.6.0"),
("192.168.0.0", 16, "192.168.255.0"),
("192.168.0.0", 16, "192.168.0.255"),
("192.168.0.0", 16, "192.168.1.0"),
("192.168.0.0", 16, "192.168.0.1"),
("192.168.0.0", 16, "192.168.128.0"),
];
for (mask_ip, mask, rule_ip) in tests {
let matches = mask_matches(mask_ip.parse().unwrap(), mask, rule_ip.parse().unwrap());
assert!(matches);
}
}
#[test]
fn ips_dont_match_mask() {
let tests = [
("192.168.6.0", 24, "192.168.5.0"),
("192.168.6.0", 25, "192.168.6.128"),
("192.168.6.0", 31, "192.168.6.2"),
("192.169.0.0", 16, "192.168.0.1"),
("192.1.0.0", 16, "192.0.0.0"),
("192.0.0.0", 16, "192.1.0.0"),
("192.255.0.0", 16, "192.0.0.0"),
("192.0.0.0", 16, "192.255.0.0"),
("192.168.0.0", 17, "192.168.128.0"),
];
for (mask_ip, mask, rule_ip) in tests {
println!("comparing: {}/{} to {}", mask_ip, mask, rule_ip);
let matches = mask_matches(mask_ip.parse().unwrap(), mask, rule_ip.parse().unwrap());
assert!(!matches);
}
}
#[test] #[test]
fn can_serialize() { fn can_serialize() {
let rule = Rule { let rule = Rule {

View file

@ -20,6 +20,7 @@ struct InterfaceConfig {
external: String, external: String,
internal: Vec<String>, internal: Vec<String>,
tunnel: Vec<String>, tunnel: Vec<String>,
vlan: Vec<String>,
} }
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
@ -39,11 +40,13 @@ struct ServerConfig {
pub struct Interfaces { pub struct Interfaces {
pub(crate) external: InterfaceInfo, pub(crate) external: InterfaceInfo,
pub(crate) internal: Vec<InterfaceInfo>, pub(crate) internal: Vec<InterfaceInfo>,
pub(crate) vlan: Vec<InterfaceInfo>,
pub(crate) tunnel: Vec<InterfaceInfo>, pub(crate) tunnel: Vec<InterfaceInfo>,
pub(crate) shared_internal: bool, pub(crate) shared_internal: bool,
pub(crate) nats: Vec<String>, pub(crate) nats: Vec<String>,
} }
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct InterfaceInfo { pub(crate) struct InterfaceInfo {
pub(crate) interface: String, pub(crate) interface: String,
pub(crate) ip: Ipv4Addr, pub(crate) ip: Ipv4Addr,
@ -90,6 +93,11 @@ impl Interfaces {
ip: "192.168.6.1".parse()?, ip: "192.168.6.1".parse()?,
mask: 24, mask: 24,
}], }],
vlan: vec![InterfaceInfo {
interface: String::from("vlan20@enp1s0"),
ip: "192.168.6.20".parse()?,
mask: 24,
}],
tunnel: vec![ tunnel: vec![
InterfaceInfo { InterfaceInfo {
interface: String::from("wg0"), interface: String::from("wg0"),
@ -135,6 +143,12 @@ impl Interfaces {
)); ));
} }
let mut vlan = Vec::new();
for iface in &config.interface.vlan {
vlan.extend(parse_interface_info(&output, &iface)?);
}
let mut tunnel = Vec::new(); let mut tunnel = Vec::new();
for iface in &config.interface.tunnel { for iface in &config.interface.tunnel {
@ -144,6 +158,7 @@ impl Interfaces {
Ok(Interfaces { Ok(Interfaces {
external, external,
internal, internal,
vlan,
tunnel, tunnel,
shared_internal: config.network.shared_internal, shared_internal: config.network.shared_internal,
nats: config.network.nats.clone(), nats: config.network.nats.clone(),
@ -218,13 +233,45 @@ mod tests {
valid_lft forever preferred_lft forever valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether ca:b8:a7:51:07:3d brd ff:ff:ff:ff:ff:ff link/ether ca:b8:a7:51:07:3d brd ff:ff:ff:ff:ff:ff
inet 136.49.5.58/20 brd 136.49.15.255 scope global dynamic eth1 inet 70.112.254.176/19 brd 70.112.255.255 scope global dynamic eth1
valid_lft 85973sec preferred_lft 85973sec valid_lft 45780sec preferred_lft 45780sec
inet6 2605:6000:ffc0:77:f985:3af7:8be2:ba44/128 scope global dynamic noprefixroute
valid_lft 584496sec preferred_lft 584496sec
inet6 fe80::c8b8:a7ff:fe51:73d/64 scope link inet6 fe80::c8b8:a7ff:fe51:73d/64 scope link
valid_lft forever preferred_lft forever valid_lft forever preferred_lft forever
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 4: vlan40@enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 24:4b:fe:37:ad:b1 brd ff:ff:ff:ff:ff:ff
inet 192.168.40.1/24 brd 192.168.40.255 scope global vlan40
valid_lft forever preferred_lft forever
inet6 fe80::264b:feff:fe37:adb1/64 scope link
valid_lft forever preferred_lft forever
5: vlan30@enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 24:4b:fe:37:ad:b1 brd ff:ff:ff:ff:ff:ff
inet 192.168.30.1/24 brd 192.168.30.255 scope global vlan30
valid_lft forever preferred_lft forever
inet6 fe80::264b:feff:fe37:adb1/64 scope link
valid_lft forever preferred_lft forever
6: vlan20@enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 24:4b:fe:37:ad:b1 brd ff:ff:ff:ff:ff:ff
inet 192.168.20.1/24 brd 192.168.20.255 scope global vlan20
valid_lft forever preferred_lft forever
inet6 fe80::264b:feff:fe37:adb1/64 scope link
valid_lft forever preferred_lft forever
7: wg1: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none link/none
inet 192.168.5.0/24 scope global wg0 inet 10.42.6.1/24 scope global wg1
valid_lft forever preferred_lft forever
8: wg2: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.42.6.1/24 scope global wg2
valid_lft forever preferred_lft forever
9: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 192.168.5.1/24 scope global wg0
valid_lft forever preferred_lft forever
10: wg3: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 192.168.4.1/24 scope global wg3
valid_lft forever preferred_lft forever valid_lft forever preferred_lft forever
"#; "#;
@ -236,8 +283,8 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(info.interface, "eth1"); assert_eq!(info.interface, "eth1");
assert_eq!(info.ip.to_string(), "136.49.5.58"); assert_eq!(info.ip.to_string(), "70.112.254.176");
assert_eq!(info.mask, 20); assert_eq!(info.mask, 19);
} }
#[test] #[test]
@ -251,4 +298,28 @@ mod tests {
assert_eq!(info.ip.to_string(), "192.168.6.1"); assert_eq!(info.ip.to_string(), "192.168.6.1");
assert_eq!(info.mask, 24); assert_eq!(info.mask, 24);
} }
#[test]
fn parses_tunnel() {
let info = parse_interface_info(OUTPUT, "wg[0-9]+")
.unwrap()
.next()
.unwrap();
assert_eq!(info.interface, "wg1");
assert_eq!(info.ip.to_string(), "10.42.6.1");
assert_eq!(info.mask, 24);
}
#[test]
fn parses_vlan() {
let info = parse_interface_info(OUTPUT, "vlan[0-9]+")
.unwrap()
.next()
.unwrap();
assert_eq!(info.interface, "vlan40");
assert_eq!(info.ip.to_string(), "192.168.40.1");
assert_eq!(info.mask, 24);
}
} }

View file

@ -26,7 +26,7 @@ fn filter(interfaces: &Interfaces) -> String {
); );
// Allow internal machines to connect to anything // Allow internal machines to connect to anything
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
filter += &format!( filter += &format!(
"-A INPUT -i {intif} -s {intip}/{intmask} -d {universe} -j ACCEPT\n", "-A INPUT -i {intif} -s {intip}/{intmask} -d {universe} -j ACCEPT\n",
intif = iface.interface, intif = iface.interface,
@ -38,7 +38,7 @@ fn filter(interfaces: &Interfaces) -> String {
// Disallow IP spoofing, internal IPs should only come from the internal network // Disallow IP spoofing, internal IPs should only come from the internal network
// If an internal IP is seen by the external interface, it's BAD!!!! // If an internal IP is seen by the external interface, it's BAD!!!!
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
filter += &format!( filter += &format!(
"-A INPUT -i {extif} -s {intip}/{intmask} -d {universe} -j REJECT\n", "-A INPUT -i {extif} -s {intip}/{intmask} -d {universe} -j REJECT\n",
extif = interfaces.external.interface, extif = interfaces.external.interface,
@ -67,7 +67,7 @@ fn filter(interfaces: &Interfaces) -> String {
); );
// Allow DHCP traffic // Allow DHCP traffic
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
filter += &format!( filter += &format!(
"-A INPUT -i {intif} -p tcp --sport 68 --dport 67 -j ACCEPT\n", "-A INPUT -i {intif} -p tcp --sport 68 --dport 67 -j ACCEPT\n",
intif = iface.interface, intif = iface.interface,
@ -102,6 +102,27 @@ fn filter(interfaces: &Interfaces) -> String {
universe = UNIVERSE, universe = UNIVERSE,
); );
// Allow VLAN traffic only on VLAN
for iface in &interfaces.vlan {
// Allow internal traffic only on network associated with interface
filter += &format!(
"-A OUTPUT -o {intif} -s {extip}/{extmask} -d {intip}/{intmask} -j ACCEPT\n",
intif = iface.interface,
extip = interfaces.external.ip,
extmask = interfaces.external.mask,
intip = iface.ip,
intmask = iface.mask,
);
// Allow traffic from self to networks associated with interface
filter += &format!(
"-A OUTPUT -o {intif} -s {intip}/32 -d {intip}/{intmask} -j ACCEPT\n",
intif = iface.interface,
intip = iface.ip,
intmask = iface.mask,
);
}
if interfaces.shared_internal { if interfaces.shared_internal {
for iface in &interfaces.internal { for iface in &interfaces.internal {
// jface (jeans iface) // jface (jeans iface)
@ -148,7 +169,7 @@ fn filter(interfaces: &Interfaces) -> String {
} }
} }
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
// Deny traffic to internal networks on external interface // Deny traffic to internal networks on external interface
filter += &format!( filter += &format!(
"-A OUTPUT -o {extif} -s {universe} -d {intip}/{intmask} -j REJECT\n", "-A OUTPUT -o {extif} -s {universe} -d {intip}/{intmask} -j REJECT\n",
@ -175,7 +196,7 @@ fn filter(interfaces: &Interfaces) -> String {
universe = UNIVERSE, universe = UNIVERSE,
); );
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
// Allow DHCP traffic out from internal interfaces // Allow DHCP traffic out from internal interfaces
filter += &format!( filter += &format!(
"-A OUTPUT -o {intif} -p tcp -s {intip} --sport 67 -d 255.255.255.255 --dport 68 -j ACCEPT\n", "-A OUTPUT -o {intif} -p tcp -s {intip} --sport 67 -d 255.255.255.255 --dport 68 -j ACCEPT\n",
@ -203,6 +224,7 @@ fn filter(interfaces: &Interfaces) -> String {
.internal .internal
.iter() .iter()
.chain(interfaces.tunnel.iter()) .chain(interfaces.tunnel.iter())
.chain(interfaces.vlan.iter())
.find(|iface| iface.interface == *nat_iface) .find(|iface| iface.interface == *nat_iface)
{ {
filter += &format!( filter += &format!(
@ -213,13 +235,19 @@ fn filter(interfaces: &Interfaces) -> String {
); );
} }
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) { for iface in interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.chain(interfaces.vlan.iter())
{
let is_nat_iface = *nat_iface == iface.interface; let is_nat_iface = *nat_iface == iface.interface;
let has_nat_subnet = interfaces let has_nat_subnet = interfaces
.internal .internal
.iter() .iter()
.chain(interfaces.tunnel.iter()) .chain(interfaces.tunnel.iter())
.chain(interfaces.vlan.iter())
.any(|other_iface| { .any(|other_iface| {
*nat_iface == other_iface.interface *nat_iface == other_iface.interface
&& other_iface.ip == iface.ip && other_iface.ip == iface.ip
@ -257,7 +285,7 @@ fn filter(interfaces: &Interfaces) -> String {
} }
// Accept TCP packets // Accept TCP packets
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
filter += &format!( filter += &format!(
"-A FORWARD -i {extif} -o {intif} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n", "-A FORWARD -i {extif} -o {intif} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n",
extif = interfaces.external.interface, extif = interfaces.external.interface,
@ -265,6 +293,14 @@ fn filter(interfaces: &Interfaces) -> String {
); );
} }
for iface in &interfaces.vlan {
// Allow packets across vlan interface
filter += &format!(
"-A FORWARD -i {intif} -o {intif} -j ACCEPT\n",
intif = iface.interface,
);
}
if interfaces.shared_internal { if interfaces.shared_internal {
for iface in &interfaces.internal { for iface in &interfaces.internal {
// jface (jeans interface) // jface (jeans interface)
@ -288,7 +324,7 @@ fn filter(interfaces: &Interfaces) -> String {
} }
// Forward packets to the internet // Forward packets to the internet
for iface in &interfaces.internal { for iface in interfaces.internal.iter().chain(&interfaces.vlan) {
filter += &format!( filter += &format!(
"-A FORWARD -i {intif} -o {extif} -j ACCEPT\n", "-A FORWARD -i {intif} -o {extif} -j ACCEPT\n",
intif = iface.interface, intif = iface.interface,
@ -326,6 +362,7 @@ fn nat(interfaces: &Interfaces) -> String {
.internal .internal
.iter() .iter()
.chain(interfaces.tunnel.iter()) .chain(interfaces.tunnel.iter())
.chain(interfaces.vlan.iter())
.any(|other_iface| { .any(|other_iface| {
*nat_iface == other_iface.interface *nat_iface == other_iface.interface
&& other_iface.ip == iface.ip && other_iface.ip == iface.ip