aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2023-11-23 19:47:29 +0100
committerHimbeerserverDE <himbeerserverde@gmail.com>2023-11-23 19:47:29 +0100
commitf53df2357f9c6445395b698845be24b93e1f1e89 (patch)
tree25739d7b6fc901e1af3002668d616956f6ed0f12
parent07d4621fc773364821597b5f5bdaf457f8fc3766 (diff)
flush conntrack on wan event
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs65
3 files changed, 69 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ee1b2c8..8a18fe8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -433,7 +433,9 @@ name = "rsdsl_netlinkd"
version = "0.8.3-dev"
dependencies = [
"ipnet",
+ "netlink-packet-core",
"netlink-packet-netfilter",
+ "netlink-packet-utils",
"netlink-sys",
"rsdsl_ip_config",
"rsdsl_netlinklib",
diff --git a/Cargo.toml b/Cargo.toml
index ec358ad..9621f1f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,9 @@ edition = "2021"
[dependencies]
ipnet = "2.8.0"
+netlink-packet-core = "0.7.0"
netlink-packet-netfilter = "0.2.0"
+netlink-packet-utils = "0.5.2"
netlink-sys = { version = "0.8.5", features = ["tokio", "tokio_socket"] }
rsdsl_ip_config = { git = "https://github.com/rsdsl/ip_config.git", version = "0.2.4" }
rsdsl_netlinklib = { git = "https://github.com/rsdsl/netlinklib.git", version = "0.4.5", features = ["blocking"] }
diff --git a/src/main.rs b/src/main.rs
index 8f6a061..556c504 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,20 +3,36 @@ use rsdsl_netlinklib::blocking::Connection;
use std::fs::{self, File};
use std::io;
use std::net::{self, IpAddr, Ipv4Addr, Ipv6Addr};
+use std::process;
+
+use tokio::runtime::Runtime;
use ipnet::Ipv6Net;
+use netlink_packet_core::{NetlinkHeader, NetlinkMessage, NetlinkPayload};
+use netlink_packet_netfilter::{
+ constants::{NLM_F_ACK, NLM_F_REQUEST},
+ NetfilterMessage,
+};
+use netlink_sys::{protocols::NETLINK_NETFILTER, Socket};
use rsdsl_ip_config::DsConfig;
use rsdsl_pd_config::PdConfig;
use signal_hook::{consts::SIGUSR1, iterator::Signals};
use sysinfo::{ProcessExt, Signal, System, SystemExt};
use thiserror::Error;
+const SIZEOFNLMSGHDR: u32 = 0x10;
+const CONNTRACK_TABLE_CONNTRACK: u16 = 1;
+const IPCTNL_MSG_CT_DELETE: u16 = 2;
+
const ADDR_AFTR: Ipv4Addr = Ipv4Addr::new(192, 0, 0, 1);
const ADDR_B4: Ipv4Addr = Ipv4Addr::new(192, 0, 0, 2);
const LINK_LOCAL: Ipv6Addr = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
#[derive(Debug, Error)]
enum Error {
+ #[error("netlink did not return status information")]
+ NoNlStatus,
+
#[error("not enough ipv6 subnets")]
NotEnoughIpv6Subnets,
@@ -27,6 +43,8 @@ enum Error {
#[error("invalid prefix length: {0}")]
PrefixLen(#[from] ipnet::PrefixLenError),
+ #[error("can't decode netlink packet: {0}")]
+ NlDecode(#[from] netlink_packet_utils::DecodeError),
#[error("netlinklib error: {0}")]
Netlinklib(#[from] rsdsl_netlinklib::Error),
}
@@ -138,6 +156,9 @@ fn configure_wan(conn: &Connection) -> Result<()> {
// Deconfigure everything, just to be safe.
conn.address_flush("ppp0".to_string())?;
conn.route_flush("ppp0".to_string())?;
+
+ // Flush the conntrack state to prevent NAT hiccups.
+ flush_conntrack()?;
}
if let Some(v4) = ds_config.v4 {
@@ -219,6 +240,50 @@ fn configure_wan(conn: &Connection) -> Result<()> {
Ok(())
}
+/// Flush the conntrack state to avoid breaking active NAT bindings.
+/// This is required for VoIP to work if it uses a fast registration interval.
+async fn flush_conntrack_async() -> Result<()> {
+ let mut recv_buf = vec![0; 4096];
+
+ let mut socket = Socket::new(NETLINK_NETFILTER)?;
+ socket.bind_auto()?;
+
+ let mut header = NetlinkHeader::default();
+
+ header.length = SIZEOFNLMSGHDR;
+ header.message_type = (CONNTRACK_TABLE_CONNTRACK << 8) | IPCTNL_MSG_CT_DELETE;
+ header.flags = NLM_F_REQUEST | NLM_F_ACK;
+ header.sequence_number = 1;
+ header.port_number = process::id();
+
+ let mut packet = NetlinkMessage::new(header, NetlinkPayload::<NetfilterMessage>::Noop);
+
+ packet.finalize();
+
+ let mut buf = vec![0; packet.header.length as usize];
+ packet.serialize(&mut buf[..]);
+ socket.send(&buf[..], 0)?;
+
+ let n = socket.recv(&mut &mut recv_buf[..], 0)?;
+ let bytes = &recv_buf[..n];
+
+ let rx_packet = <NetlinkMessage<NetfilterMessage>>::deserialize(bytes)?;
+
+ if let NetlinkPayload::Error(e) = rx_packet.payload {
+ if e.code.is_some() {
+ Err(e.to_io().into())
+ } else {
+ Ok(())
+ }
+ } else {
+ Err(Error::NoNlStatus)
+ }
+}
+
+fn flush_conntrack() -> Result<()> {
+ Runtime::new()?.block_on(flush_conntrack_async())
+}
+
fn read_ds_config_optional() -> Option<DsConfig> {
let mut file = File::open(rsdsl_ip_config::LOCATION).ok()?;
serde_json::from_reader(&mut file).ok()