[pve-devel] [PATCH conntrack-tool v2 1/5] initial commit

Mira Limbeck m.limbeck at proxmox.com
Wed Feb 3 15:25:31 CET 2021


Dumping conntrack information and importing conntrack information works
for IPv4 and IPv6. No filtering is supported for now. pve-conntrack-tool
will always return both IPv4 and IPv6 conntracks together.

Conntracks are serialized as JSON and printed on STDOUT line by line
with one line containing one conntrack. When inserting data is read
from STDIN line by line and expected to be one JSON object per line
representing the conntrack.

Currently some conntrack attributes are not supported. These are
HELPER_INFO, CONNLABELS and CONNLABELS_MASK. The reason for this is that
handling of variable length attributes does not seem to be correctly
implemented in libnetfilter_conntrack. To fix this we would probably have
to use libmnl directly.

Conntracks containing protonum 2 (IGMP) are ignored in the dump as
they can't be inserted using libnetfilter_conntrack (conntrack-tools'
conntrack also exhibits the same behavior).

Expectation support, which is necessary for FTP and other protocols, is
not yet implemented.

Signed-off-by: Mira Limbeck <m.limbeck at proxmox.com>
---
v2:
 - changed Conntracks to Socket
 - reworked a lot of the code for less code duplication
 - reduced usage of 'unsafe'
 - added/changed things based on @Wobu's suggestions (off-list)

 Cargo.toml                 |  14 ++
 src/main.rs                | 488 +++++++++++++++++++++++++++++++++++++
 src/mnl.rs                 | 132 ++++++++++
 src/netfilter_conntrack.rs | 168 +++++++++++++
 4 files changed, 802 insertions(+)
 create mode 100644 Cargo.toml
 create mode 100644 src/main.rs
 create mode 100644 src/mnl.rs
 create mode 100644 src/netfilter_conntrack.rs

diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3e3851a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "pve-conntrack-tool"
+version = "1.0.0"
+authors = ["Mira Limbeck <m.limbeck at proxmox.com>"]
+edition = "2018"
+license = "AGPL-3"
+
+exclude = [ "build", "debian" ]
+
+[dependencies]
+anyhow = "1.0.26"
+libc = "0.2.69"
+serde = { version = "1.0.106", features = ["derive"] }
+serde_json = "1.0.41"
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..2137556
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,488 @@
+mod mnl;
+mod netfilter_conntrack;
+
+use std::convert::TryInto;
+use std::ffi::CString;
+use std::io::{stdin, BufRead, BufReader};
+use std::os::unix::ffi::OsStringExt;
+use std::ptr::NonNull;
+
+use anyhow::{bail, format_err, Result};
+use mnl::*;
+use netfilter_conntrack::*;
+use serde::{Deserialize, Serialize};
+
+fn main() -> Result<()> {
+    let args = std::env::args_os()
+        .map(|os| String::from_utf8(os.into_vec()))
+        .try_fold(Vec::new(), |mut args, s| match s {
+            Ok(s) => {
+                args.push(s);
+                Ok(args)
+            }
+            Err(err) => bail!("Invalid UTF8 argument: {}", err),
+        })?;
+    if args.len() != 2 {
+        bail!("Either 'dump' or 'insert' command required.");
+    }
+
+    let mut socket = Socket::open()?;
+
+    if args[1] == "dump" {
+        let mut cts = Vec::new();
+        socket
+            .query_conntracks(&mut cts)
+            .map_err(|err| format_err!("Error querying conntracks: {}", err))?;
+
+        for ct in cts.iter() {
+            match serde_json::to_string(ct) {
+                Ok(s) => println!("{}", s),
+                Err(err) => {
+                    eprintln!("Failed to serialize conntrack: {}", err);
+                    break;
+                }
+            }
+        }
+    } else if args[1] == "insert" {
+        for line in BufReader::new(stdin())
+            .lines()
+            .map(|line| line.unwrap_or_else(|_| "".to_string()))
+        {
+            let ct: Conntrack = match serde_json::from_str(&line) {
+                Ok(ct) => ct,
+                Err(err) => {
+                    eprintln!("Failed to deserialize conntrack: {}", err);
+                    break;
+                }
+            };
+            if let Err(err) = socket.insert_conntrack(ct) {
+                eprintln!("Error inserting conntrack: {}", err);
+            }
+        }
+    } else {
+        bail!("Unknown command: {}", args[1]);
+    }
+
+    Ok(())
+}
+
+extern "C" fn query_cts_cb(nlh: *const libc::nlmsghdr, data_ptr: *mut libc::c_void) -> libc::c_int {
+    let ct = unsafe { nfct_new() };
+    unsafe {
+        nfct_nlmsg_parse(nlh, ct);
+    }
+
+    if let Some(conntrack) = parse_conntrack(ct) {
+        let cts: &mut Vec<Conntrack> = unsafe { &mut *(data_ptr as *mut Vec<Conntrack>) };
+        cts.push(conntrack);
+    }
+
+    unsafe {
+        nfct_destroy(ct);
+    }
+
+    MNL_CB_OK
+}
+
+const CONNTRACK_QUERY_MSG_TYPE: u16 =
+    ((libc::NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET) as u16;
+const CONNTRACK_QUERY_FLAGS: u16 =
+    (libc::NLM_F_ACK | libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16;
+const CONNTRACK_INSERT_MSG_TYPE: u16 =
+    ((libc::NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW) as u16;
+const CONNTRACK_INSERT_FLAGS: u16 =
+    (libc::NLM_F_ACK | libc::NLM_F_REQUEST | libc::NLM_F_CREATE) as u16;
+
+pub struct Socket {
+    socket: NonNull<mnl_socket>,
+    seq: u32,
+}
+
+impl Socket {
+    fn open() -> Result<Self> {
+        let socket = unsafe { mnl_socket_open(libc::NETLINK_NETFILTER) };
+        let socket = match NonNull::new(socket) {
+            Some(s) => s,
+            None => {
+                let err = std::io::Error::last_os_error();
+                bail!("Failed to open MNL socket: {}", err)
+            }
+        };
+
+        let res = unsafe { mnl_socket_bind(socket.as_ptr(), 0, MNL_SOCKET_AUTOPID) };
+        if res < 0 {
+            let err = std::io::Error::last_os_error();
+            bail!("Failed to bind MNL socket: {}", err);
+        }
+
+        Ok(Self { socket, seq: 0 })
+    }
+
+    fn seq(&mut self) -> u32 {
+        self.seq += 1;
+        self.seq
+    }
+
+    fn query_conntracks(&mut self, cts: &mut Vec<Conntrack>) -> Result<()> {
+        let seq = self.seq();
+        self.query_conntracks_impl(cts, seq, libc::AF_INET as _)?;
+        let seq = self.seq();
+        self.query_conntracks_impl(cts, seq, libc::AF_INET6 as _)?;
+        Ok(())
+    }
+
+    fn query_conntracks_impl(
+        &mut self,
+        cts: &mut Vec<Conntrack>,
+        seq: u32,
+        proto: u8,
+    ) -> Result<()> {
+        let mut buf = [0u8; MNL_SOCKET_DUMP_SIZE as _];
+        let hdr = build_msg_header(
+            buf.as_mut_ptr() as _,
+            CONNTRACK_QUERY_MSG_TYPE,
+            CONNTRACK_QUERY_FLAGS,
+            seq,
+            proto,
+        );
+        self.send_and_receive(hdr, 0, Some(query_cts_cb), cts as *mut Vec<Conntrack> as _)
+    }
+
+    fn insert_conntrack(&mut self, ct: Conntrack) -> Result<()> {
+        let proto = if ct.is_ipv6() {
+            libc::AF_INET6 as u8
+        } else {
+            libc::AF_INET as u8
+        };
+
+        let mut buf = [0u8; MNL_SOCKET_BUFFER_SIZE as _];
+        let hdr = build_msg_header(
+            buf.as_mut_ptr() as _,
+            CONNTRACK_INSERT_MSG_TYPE,
+            CONNTRACK_INSERT_FLAGS,
+            self.seq(),
+            proto,
+        );
+
+        let (cth, _strings) = build_conntrack(ct)?;
+
+        unsafe {
+            nfct_nlmsg_build(hdr, cth);
+            nfct_destroy(cth);
+        }
+
+        self.send_and_receive(hdr, 0, None, std::ptr::null_mut())?;
+
+        Ok(())
+    }
+
+    fn send_and_receive(
+        &mut self,
+        msg: *const libc::nlmsghdr,
+        seq: u32,
+        cb: Option<mnl_cb_t>,
+        data: *mut libc::c_void,
+    ) -> Result<()> {
+        let res =
+            unsafe { mnl_socket_sendto(self.socket.as_ptr(), msg as _, (*msg).nlmsg_len as _) };
+        if res == -1 {
+            let err = std::io::Error::last_os_error();
+            bail!("Failed to send message: {}", err);
+        }
+
+        let portid = unsafe { mnl_socket_get_portid(self.socket.as_ptr()) };
+        let mut buffer = [0u8; MNL_SOCKET_DUMP_SIZE as _];
+
+        loop {
+            let res = unsafe {
+                mnl_socket_recvfrom(
+                    self.socket.as_ptr(),
+                    buffer.as_mut_ptr() as _,
+                    MNL_SOCKET_DUMP_SIZE as _,
+                )
+            };
+            if res == -1 {
+                let err = std::io::Error::last_os_error();
+                bail!("Failed to read message: {}", err);
+            }
+
+            let res = unsafe { mnl_cb_run(buffer.as_ptr() as _, res as _, seq, portid, cb, data) };
+            if res == -1 {
+                let err = std::io::Error::last_os_error();
+                bail!("Failed to run callback: {}", err);
+            } else if res <= MNL_CB_STOP {
+                break;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl Drop for Socket {
+    fn drop(&mut self) {
+        let res = unsafe { mnl_socket_close(self.socket.as_ptr()) };
+        if res < 0 {
+            eprintln!("Error closing socket");
+        }
+    }
+}
+
+fn build_msg_header(
+    buf: *mut libc::c_void,
+    ty: u16,
+    flags: u16,
+    seq: u32,
+    proto: u8,
+) -> *mut libc::nlmsghdr {
+    let nlh = unsafe { mnl_nlmsg_put_header(buf) };
+    unsafe {
+        (*nlh).nlmsg_type = ty;
+        (*nlh).nlmsg_flags = flags;
+        (*nlh).nlmsg_seq = seq;
+    }
+
+    let nfh = unsafe {
+        mnl_nlmsg_put_extra_header(nlh, std::mem::size_of::<nfgenmsg>()) as *mut nfgenmsg
+    };
+    unsafe {
+        (*nfh).nfgen_family = proto;
+        (*nfh).version = libc::NFNETLINK_V0 as _;
+        (*nfh).res_id = 0;
+    }
+    nlh
+}
+
+enum AttrType {
+    U8,
+    U16,
+    U32,
+    U64,
+    U128,
+    String(Option<u32>),
+    VarLen,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+enum AttrValue {
+    U8(u8),
+    U16(u16),
+    U32(u32),
+    U64(u64),
+    U128([u32; 4]),
+    String(CString),
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Attr {
+    #[serde(rename = "type")]
+    key: CTAttr,
+    value: AttrValue,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Conntrack {
+    attributes: Vec<Attr>,
+}
+
+impl Conntrack {
+    pub fn is_ipv6(&self) -> bool {
+        for attr in self.attributes.iter() {
+            // only IPV6 attributes have U128 values
+            if let AttrValue::U128(_) = attr.value {
+                return true;
+            }
+        }
+        false
+    }
+}
+
+fn parse_conntrack(ct: *const nf_conntrack) -> Option<Conntrack> {
+    let mut attributes = Vec::new();
+    for (attr, ty) in ALL_ATTRIBUTES {
+        if *attr == CTAttr::ID {
+            continue;
+        }
+        if unsafe { nfct_attr_is_set(ct, *attr) } == 0 {
+            continue;
+        }
+        // check for IGMP and skip it as we can't insert it again
+        if unsafe { nfct_get_attr_u8(ct, CTAttr::ORIG_L4PROTO) } == 2 {
+            return None;
+        }
+        match ty {
+            AttrType::U8 => {
+                let val = unsafe { nfct_get_attr_u8(ct, *attr) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::U8(val),
+                });
+            }
+            AttrType::U16 => {
+                let val = unsafe { nfct_get_attr_u16(ct, *attr) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::U16(val),
+                });
+            }
+            AttrType::U32 => {
+                let val = unsafe { nfct_get_attr_u32(ct, *attr) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::U32(val),
+                });
+            }
+            AttrType::U64 => {
+                let val = unsafe { nfct_get_attr_u64(ct, *attr) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::U64(val),
+                });
+            }
+            AttrType::U128 => {
+                let val = unsafe { nfct_get_attr(ct, *attr) } as *const u32;
+                let val = unsafe { std::slice::from_raw_parts(val, 4) }
+                    .try_into()
+                    .unwrap();
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::U128(val),
+                });
+            }
+            AttrType::String(Some(len)) => {
+                let ptr = unsafe { nfct_get_attr(ct, *attr) };
+                let cstr = unsafe { std::ffi::CStr::from_ptr(ptr as _) };
+                let s = cstr.to_bytes();
+                let s =
+                    unsafe { CString::from_vec_unchecked(s[0..s.len().min(*len as _)].to_vec()) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::String(s),
+                });
+            }
+            AttrType::String(None) => {
+                let ptr = unsafe { nfct_get_attr(ct, *attr) };
+                let cstr = unsafe { std::ffi::CStr::from_ptr(ptr as _) };
+                let s = cstr.to_bytes();
+                let s = unsafe { CString::from_vec_unchecked(s.to_vec()) };
+                attributes.push(Attr {
+                    key: *attr,
+                    value: AttrValue::String(s),
+                });
+            }
+            // ignore VarLen case for now
+            AttrType::VarLen => {}
+        }
+    }
+
+    Some(Conntrack { attributes })
+}
+
+fn build_conntrack(ct: Conntrack) -> Result<(*mut nf_conntrack, Vec<CString>)> {
+    let cth = unsafe { nfct_new() };
+    if cth.is_null() {
+        bail!("Failed to create new conntrack object");
+    }
+
+    let mut strings = Vec::new();
+    for attr in ct.attributes {
+        match attr.value {
+            AttrValue::U8(v) => unsafe {
+                nfct_set_attr_u8(cth, attr.key, v);
+            },
+            AttrValue::U16(v) => unsafe {
+                nfct_set_attr_u16(cth, attr.key, v);
+            },
+            AttrValue::U32(v) => unsafe {
+                nfct_set_attr_u32(cth, attr.key, v);
+            },
+            AttrValue::U64(v) => unsafe {
+                nfct_set_attr_u64(cth, attr.key, v);
+            },
+            AttrValue::U128(v) => unsafe {
+                nfct_set_attr_l(cth, attr.key, v.as_ptr() as _, std::mem::size_of_val(&v));
+            },
+            AttrValue::String(v) => unsafe {
+                nfct_set_attr(cth, attr.key, v.as_ptr() as _);
+                strings.push(v);
+            },
+        }
+    }
+    Ok((cth, strings))
+}
+const ALL_ATTRIBUTES: &[(CTAttr, AttrType)] = &[
+    (CTAttr::ORIG_IPV4_SRC, AttrType::U32),        /* u32 bits */
+    (CTAttr::ORIG_IPV4_DST, AttrType::U32),        /* u32 bits */
+    (CTAttr::REPL_IPV4_SRC, AttrType::U32),        /* u32 bits */
+    (CTAttr::REPL_IPV4_DST, AttrType::U32),        /* u32 bits */
+    (CTAttr::ORIG_IPV6_SRC, AttrType::U128),       /* u128 bits */
+    (CTAttr::ORIG_IPV6_DST, AttrType::U128),       /* u128 bits */
+    (CTAttr::REPL_IPV6_SRC, AttrType::U128),       /* u128 bits */
+    (CTAttr::REPL_IPV6_DST, AttrType::U128),       /* u128 bits */
+    (CTAttr::ORIG_PORT_SRC, AttrType::U16),        /* u16 bits */
+    (CTAttr::ORIG_PORT_DST, AttrType::U16),        /* u16 bits */
+    (CTAttr::REPL_PORT_SRC, AttrType::U16),        /* u16 bits */
+    (CTAttr::REPL_PORT_DST, AttrType::U16),        /* u16 bits */
+    (CTAttr::ICMP_TYPE, AttrType::U8),             /* u8 bits */
+    (CTAttr::ICMP_CODE, AttrType::U8),             /* u8 bits */
+    (CTAttr::ICMP_ID, AttrType::U16),              /* u16 bits */
+    (CTAttr::ORIG_L3PROTO, AttrType::U8),          /* u8 bits */
+    (CTAttr::REPL_L3PROTO, AttrType::U8),          /* u8 bits */
+    (CTAttr::ORIG_L4PROTO, AttrType::U8),          /* u8 bits */
+    (CTAttr::REPL_L4PROTO, AttrType::U8),          /* u8 bits */
+    (CTAttr::TCP_STATE, AttrType::U8),             /* u8 bits */
+    (CTAttr::SNAT_IPV4, AttrType::U32),            /* u32 bits */
+    (CTAttr::DNAT_IPV4, AttrType::U32),            /* u32 bits */
+    (CTAttr::SNAT_PORT, AttrType::U16),            /* u16 bits */
+    (CTAttr::DNAT_PORT, AttrType::U16),            /* u16 bits */
+    (CTAttr::TIMEOUT, AttrType::U32),              /* u32 bits */
+    (CTAttr::MARK, AttrType::U32),                 /* u32 bits */
+    (CTAttr::ORIG_COUNTER_PACKETS, AttrType::U64), /* u64 bits */
+    (CTAttr::REPL_COUNTER_PACKETS, AttrType::U64), /* u64 bits */
+    (CTAttr::ORIG_COUNTER_BYTES, AttrType::U64),   /* u64 bits */
+    (CTAttr::REPL_COUNTER_BYTES, AttrType::U64),   /* u64 bits */
+    (CTAttr::USE, AttrType::U32),                  /* u32 bits */
+    (CTAttr::ID, AttrType::U32),                   /* u32 bits */
+    (CTAttr::STATUS, AttrType::U32),               /* u32 bits  */
+    (CTAttr::TCP_FLAGS_ORIG, AttrType::U8),        /* u8 bits */
+    (CTAttr::TCP_FLAGS_REPL, AttrType::U8),        /* u8 bits */
+    (CTAttr::TCP_MASK_ORIG, AttrType::U8),         /* u8 bits */
+    (CTAttr::TCP_MASK_REPL, AttrType::U8),         /* u8 bits */
+    (CTAttr::MASTER_IPV4_SRC, AttrType::U32),      /* u32 bits */
+    (CTAttr::MASTER_IPV4_DST, AttrType::U32),      /* u32 bits */
+    (CTAttr::MASTER_IPV6_SRC, AttrType::U128),     /* u128 bits */
+    (CTAttr::MASTER_IPV6_DST, AttrType::U128),     /* u128 bits */
+    (CTAttr::MASTER_PORT_SRC, AttrType::U16),      /* u16 bits */
+    (CTAttr::MASTER_PORT_DST, AttrType::U16),      /* u16 bits */
+    (CTAttr::MASTER_L3PROTO, AttrType::U8),        /* u8 bits */
+    (CTAttr::MASTER_L4PROTO, AttrType::U8),        /* u8 bits */
+    (CTAttr::SECMARK, AttrType::U32),              /* u32 bits */
+    (CTAttr::ORIG_NAT_SEQ_CORRECTION_POS, AttrType::U32), /* u32 bits */
+    (CTAttr::ORIG_NAT_SEQ_OFFSET_BEFORE, AttrType::U32), /* u32 bits */
+    (CTAttr::ORIG_NAT_SEQ_OFFSET_AFTER, AttrType::U32), /* u32 bits */
+    (CTAttr::REPL_NAT_SEQ_CORRECTION_POS, AttrType::U32), /* u32 bits */
+    (CTAttr::REPL_NAT_SEQ_OFFSET_BEFORE, AttrType::U32), /* u32 bits */
+    (CTAttr::REPL_NAT_SEQ_OFFSET_AFTER, AttrType::U32), /* u32 bits */
+    (CTAttr::SCTP_STATE, AttrType::U8),            /* u8 bits */
+    (CTAttr::SCTP_VTAG_ORIG, AttrType::U32),       /* u32 bits */
+    (CTAttr::SCTP_VTAG_REPL, AttrType::U32),       /* u32 bits */
+    (CTAttr::HELPER_NAME, AttrType::String(Some(30))), /* string (30 bytes max) */
+    (CTAttr::DCCP_STATE, AttrType::U8),            /* u8 bits */
+    (CTAttr::DCCP_ROLE, AttrType::U8),             /* u8 bits */
+    (CTAttr::DCCP_HANDSHAKE_SEQ, AttrType::U64),   /* u64 bits */
+    (CTAttr::TCP_WSCALE_ORIG, AttrType::U8),       /* u8 bits */
+    (CTAttr::TCP_WSCALE_REPL, AttrType::U8),       /* u8 bits */
+    (CTAttr::ZONE, AttrType::U16),                 /* u16 bits */
+    (CTAttr::SECCTX, AttrType::String(None)),      /* string */
+    (CTAttr::TIMESTAMP_START, AttrType::U64),      /* u64 bits; linux >= 2.6.38 */
+    (CTAttr::TIMESTAMP_STOP, AttrType::U64),       /* u64 bits; linux >= 2.6.38 */
+    (CTAttr::HELPER_INFO, AttrType::VarLen),       /* variable length */
+    (CTAttr::CONNLABELS, AttrType::VarLen),        /* variable length */
+    (CTAttr::CONNLABELS_MASK, AttrType::VarLen),   /* variable length */
+    (CTAttr::ORIG_ZONE, AttrType::U16),            /* u16 bits */
+    (CTAttr::REPL_ZONE, AttrType::U16),            /* u16 bits */
+    (CTAttr::SNAT_IPV6, AttrType::U128),           /* u128 bits */
+    (CTAttr::DNAT_IPV6, AttrType::U128),           /* u128 bits */
+    (CTAttr::SYNPROXY_ISN, AttrType::U32),         /* u32 bits */
+    (CTAttr::SYNPROXY_ITS, AttrType::U32),         /* u32 bits */
+    (CTAttr::SYNPROXY_TSOFF, AttrType::U32),       /* u32 bits */
+];
diff --git a/src/mnl.rs b/src/mnl.rs
new file mode 100644
index 0000000..a23313e
--- /dev/null
+++ b/src/mnl.rs
@@ -0,0 +1,132 @@
+#![allow(dead_code)]
+
+pub const MNL_SOCKET_AUTOPID: libc::c_int = 0;
+pub const MNL_SOCKET_DUMP_SIZE: libc::c_int = 32768;
+pub const MNL_SOCKET_BUFFER_SIZE: libc::c_int = 8192;
+
+pub const MNL_CB_ERROR: libc::c_int = -1;
+pub const MNL_CB_STOP: libc::c_int = 0;
+pub const MNL_CB_OK: libc::c_int = 1;
+
+#[repr(C)]
+pub struct mnl_socket {
+    _private: [u8; 0],
+}
+
+pub const IPCTNL_MSG_CT_NEW: libc::c_int = 0;
+pub const IPCTNL_MSG_CT_GET: libc::c_int = 1;
+pub const IPCTNL_MSG_CT_DELETE: libc::c_int = 2;
+pub const IPCTNL_MSG_CT_GET_CTRZERO: libc::c_int = 3;
+pub const IPCTNL_MSG_CT_GET_STATS_CPU: libc::c_int = 4;
+pub const IPCTNL_MSG_CT_GET_STATS: libc::c_int = 5;
+pub const IPCTNL_MSG_CT_GET_DYING: libc::c_int = 6;
+pub const IPCTNL_MSG_CT_GET_UNCONFIRMED: libc::c_int = 7;
+pub const IPCTNL_MSG_MAX: libc::c_int = 8;
+
+pub const IPCTNL_MSG_EXP_NEW: libc::c_int = 0;
+pub const IPCTNL_MSG_EXP_GET: libc::c_int = 1;
+pub const IPCTNL_MSG_EXP_DELETE: libc::c_int = 2;
+pub const IPCTNL_MSG_EXP_GET_STATS_CPU: libc::c_int = 3;
+pub const IPCTNL_MSG_EXP_MAX: libc::c_int = 4;
+
+#[link(name = "mnl")]
+extern "C" {
+    pub fn mnl_socket_open(bus: libc::c_int) -> *mut mnl_socket;
+    pub fn mnl_socket_bind(
+        nl: *const mnl_socket,
+        groups: libc::c_uint,
+        pid: libc::pid_t,
+    ) -> libc::c_int;
+    pub fn mnl_socket_close(nl: *mut mnl_socket) -> libc::c_int;
+    pub fn mnl_socket_get_portid(nl: *mut mnl_socket) -> libc::c_uint;
+    pub fn mnl_socket_sendto(
+        nl: *mut mnl_socket,
+        buf: *const libc::c_void,
+        len: libc::size_t,
+    ) -> libc::ssize_t;
+    pub fn mnl_socket_recvfrom(
+        nl: *mut mnl_socket,
+        buf: *mut libc::c_void,
+        bufsiz: libc::size_t,
+    ) -> libc::ssize_t;
+
+    pub fn mnl_nlmsg_put_header(buf: *mut libc::c_void) -> *mut libc::nlmsghdr;
+    pub fn mnl_nlmsg_put_extra_header(
+        nlh: *mut libc::nlmsghdr,
+        size: libc::size_t,
+    ) -> *mut libc::c_void;
+    pub fn mnl_nlmsg_get_payload(nlh: *const libc::nlmsghdr) -> *mut libc::c_void;
+
+    pub fn mnl_cb_run(
+        buf: *const libc::c_void,
+        numbytes: libc::size_t,
+        seq: libc::c_uint,
+        portid: libc::c_uint,
+        cb_data: Option<mnl_cb_t>,
+        data: *mut libc::c_void,
+    ) -> libc::c_int;
+
+    pub fn mnl_attr_parse(
+        nlh: *const libc::nlmsghdr,
+        offset: libc::c_uint,
+        cb: mnl_attr_cb_t,
+        data: *mut libc::c_void,
+    ) -> libc::c_int;
+    pub fn mnl_attr_get_type(attr: *const libc::nlattr) -> u16;
+    pub fn mnl_attr_type_valid(attr: *const libc::nlattr, maxtype: u16) -> libc::c_int;
+
+    pub fn mnl_nlmsg_batch_start(buf: *mut libc::c_void, limit: libc::size_t) -> *mut mnl_nlmsg_batch;
+    pub fn mnl_nlmsg_batch_stop(b: *mut mnl_nlmsg_batch);
+    pub fn mnl_nlmsg_batch_next(b: *mut mnl_nlmsg_batch) -> bool;
+    pub fn mnl_nlmsg_batch_current(b: *mut mnl_nlmsg_batch) -> *mut libc::c_void;
+    pub fn mnl_nlmsg_batch_head(b: *mut mnl_nlmsg_batch) -> *mut libc::c_void;
+    pub fn mnl_nlmsg_batch_size(b: *mut mnl_nlmsg_batch) -> libc::size_t;
+}
+
+#[allow(non_camel_case_types)]
+pub type mnl_cb_t = extern "C" fn(nlh: *const libc::nlmsghdr, data: *mut libc::c_void) -> libc::c_int;
+#[allow(non_camel_case_types)]
+pub type mnl_attr_cb_t =
+    extern "C" fn(attr: *const libc::nlattr, data: *mut libc::c_void) -> libc::c_int;
+
+#[repr(C)]
+pub struct nfgenmsg {
+    pub nfgen_family: u8,
+    pub version: u8,
+    pub res_id: u16, // TODO any better solution for __be16?
+}
+
+pub const CTA_UNSPEC: u16 = 0;
+pub const CTA_TUPLE_ORIG: u16 = 1;
+pub const CTA_TUPLE_REPLY: u16 = 2;
+pub const CTA_STATUS: u16 = 3;
+pub const CTA_PROTOINFO: u16 = 4;
+pub const CTA_HELP: u16 = 5;
+pub const CTA_NAT_SRC: u16 = 6;
+pub const CTA_NAT: u16 = CTA_NAT_SRC; // backwards compatibility
+pub const CTA_TIMEOUT: u16 = 7;
+pub const CTA_MARK: u16 = 8;
+pub const CTA_COUNTERS_ORIG: u16 = 9;
+pub const CTA_COUNTERS_REPLY: u16 = 10;
+pub const CTA_USE: u16 = 11;
+pub const CTA_ID: u16 = 12;
+pub const CTA_NAT_DST: u16 = 13;
+pub const CTA_TUPLE_MASTER: u16 = 14;
+pub const CTA_SEQ_ADJ_ORIG: u16 = 15;
+pub const CTA_NAT_SEQ_ADJ_ORIG: u16 = CTA_SEQ_ADJ_ORIG;
+pub const CTA_SEQ_ADJ_REPLY: u16 = 16;
+pub const CTA_NAT_SEQ_ADJ_REPLY: u16 = CTA_SEQ_ADJ_REPLY;
+pub const CTA_SECMARK: u16 = 17; // obsolete
+pub const CTA_ZONE: u16 = 18;
+pub const CTA_SECCTX: u16 = 19;
+pub const CTA_TIMESTAMP: u16 = 20;
+pub const CTA_MARK_MASK: u16 = 21;
+pub const CTA_LABELS: u16 = 22;
+pub const CTA_LABELS_MASK: u16 = 23;
+pub const CTA_SYNPROXY: u16 = 24;
+pub const CTA_MAX: u16 = CTA_SYNPROXY;
+
+#[repr(C)]
+pub struct mnl_nlmsg_batch {
+    _private: [u8; 0],
+}
diff --git a/src/netfilter_conntrack.rs b/src/netfilter_conntrack.rs
new file mode 100644
index 0000000..a9e67e4
--- /dev/null
+++ b/src/netfilter_conntrack.rs
@@ -0,0 +1,168 @@
+#![allow(dead_code)]
+
+#[repr(C)]
+pub struct nf_conntrack {
+    _private: [u8; 0],
+}
+
+#[link(name = "netfilter_conntrack")]
+extern "C" {
+    pub fn nfct_new() -> *mut nf_conntrack;
+    pub fn nfct_destroy(ct: *mut nf_conntrack);
+
+    pub fn nfct_nlmsg_build(nlh: *mut libc::nlmsghdr, ct: *const nf_conntrack) -> libc::c_int;
+    pub fn nfct_nlmsg_parse(nlh: *const libc::nlmsghdr, ct: *mut nf_conntrack) -> libc::c_int;
+
+    pub fn nfct_snprintf(
+        buf: *mut libc::c_char,
+        size: libc::c_uint,
+        ct: *const nf_conntrack,
+        msg_type: libc::c_uint,
+        out_type: libc::c_uint,
+        out_flags: libc::c_uint,
+    ) -> libc::c_int;
+
+    pub fn nfct_setobjopt(ct: *mut nf_conntrack, option: libc::c_uint) -> libc::c_int;
+    pub fn nfct_getobjopt(ct: *const nf_conntrack, option: libc::c_uint) -> libc::c_int;
+
+    pub fn nfct_set_attr(ct: *mut nf_conntrack, type_: CTAttr, value: *const libc::c_void);
+    pub fn nfct_set_attr_u8(ct: *mut nf_conntrack, type_: CTAttr, value: u8);
+    pub fn nfct_set_attr_u16(ct: *mut nf_conntrack, type_: CTAttr, value: u16);
+    pub fn nfct_set_attr_u32(ct: *mut nf_conntrack, type_: CTAttr, value: u32);
+    pub fn nfct_set_attr_u64(ct: *mut nf_conntrack, type_: CTAttr, value: u64);
+    pub fn nfct_set_attr_l(ct: *mut nf_conntrack, type_: CTAttr, value: *const libc::c_void, len: libc::size_t);
+
+    pub fn nfct_get_attr(ct: *const nf_conntrack, type_: CTAttr) -> *const libc::c_void;
+    pub fn nfct_get_attr_u8(ct: *const nf_conntrack, type_: CTAttr) -> u8;
+    pub fn nfct_get_attr_u16(ct: *const nf_conntrack, type_: CTAttr) -> u16;
+    pub fn nfct_get_attr_u32(ct: *const nf_conntrack, type_: CTAttr) -> u32;
+    pub fn nfct_get_attr_u64(ct: *const nf_conntrack, type_: CTAttr) -> u64;
+
+    pub fn nfct_attr_is_set(ct: *const nf_conntrack, type_: CTAttr) -> libc::c_int;
+}
+
+// set option
+pub const NFCT_SOPT_UNDO_SNAT: u32 = 0;
+pub const NFCT_SOPT_UNDO_DNAT: u32 = 1;
+pub const NFCT_SOPT_UNDO_SPAT: u32 = 2;
+pub const NFCT_SOPT_UNDO_DPAT: u32 = 3;
+pub const NFCT_SOPT_SETUP_ORIGINAL: u32 = 4;
+pub const NFCT_SOPT_SETUP_REPLY: u32 = 5;
+
+// get option
+pub const NFCT_GOPT_IS_SNAT: u32 = 0;
+pub const NFCT_GOPT_IS_DNAT: u32 = 1;
+pub const NFCT_GOPT_IS_SPAT: u32 = 2;
+pub const NFCT_GOPT_IS_DPAT: u32 = 3;
+
+// output type
+pub const NFCT_O_PLAIN: u32 = 0;
+pub const NFCT_O_DEFAULT: u32 = NFCT_O_PLAIN;
+pub const NFCT_O_XML: u32 = 1;
+pub const NFCT_O_MAX: u32 = 2;
+
+// output flags
+pub const NFCT_OF_SHOW_LAYER3_BIT: u32 = 0;
+pub const NFCT_OF_SHOW_LAYER3: u32 = 1 << NFCT_OF_SHOW_LAYER3_BIT;
+pub const NFCT_OF_TIME_BIT: u32 = 1;
+pub const NFCT_OF_TIME: u32 = 1 << NFCT_OF_TIME_BIT;
+pub const NFCT_OF_ID_BIT: u32 = 2;
+pub const NFCT_OF_ID: u32 = 1 << NFCT_OF_ID_BIT;
+pub const NFCT_OF_TIMESTAMP_BIT: u32 = 3;
+pub const NFCT_OF_TIMESTAMP: u32 = 1 << NFCT_OF_TIMESTAMP_BIT;
+
+// message type
+pub const NFCT_T_UNKNOWN: u32 = 0;
+pub const NFCT_T_NEW_BIT: u32 = 0;
+pub const NFCT_T_NEW: u32 = 1 << NFCT_T_NEW_BIT;
+pub const NFCT_T_UPDATE_BIT: u32 = 1;
+pub const NFCT_T_UPDATE: u32 = 1 << NFCT_T_UPDATE_BIT;
+pub const NFCT_T_DESTROY_BIT: u32 = 2;
+pub const NFCT_T_DESTROY: u32 = 1 << NFCT_T_DESTROY_BIT;
+pub const NFCT_T_ALL: u32 = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY;
+pub const NFCT_T_ERROR_BIT: u32 = 31;
+pub const NFCT_T_ERROR: u32 = 1 << NFCT_T_ERROR_BIT;
+
+#[repr(u32)]
+#[non_exhaustive]
+#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(serde::Deserialize, serde::Serialize)]
+#[allow(non_camel_case_types)]
+pub enum CTAttr {
+    ORIG_IPV4_SRC = 0,			/* u32 bits */
+    ORIG_IPV4_DST = 1,			/* u32 bits */
+    REPL_IPV4_SRC = 2,		        /* u32 bits */
+    REPL_IPV4_DST = 3,			/* u32 bits */
+    ORIG_IPV6_SRC = 4,			/* u128 bits */
+    ORIG_IPV6_DST = 5,			/* u128 bits */
+    REPL_IPV6_SRC = 6,			/* u128 bits */
+    REPL_IPV6_DST = 7,			/* u128 bits */
+    ORIG_PORT_SRC = 8,			/* u16 bits */
+    ORIG_PORT_DST = 9,			/* u16 bits */
+    REPL_PORT_SRC = 10,			/* u16 bits */
+    REPL_PORT_DST = 11,			/* u16 bits */
+    ICMP_TYPE = 12,			/* u8 bits */
+    ICMP_CODE = 13,			/* u8 bits */
+    ICMP_ID = 14,			/* u16 bits */
+    ORIG_L3PROTO = 15,			/* u8 bits */
+    REPL_L3PROTO = 16,			/* u8 bits */
+    ORIG_L4PROTO = 17,			/* u8 bits */
+    REPL_L4PROTO = 18,			/* u8 bits */
+    TCP_STATE = 19,			/* u8 bits */
+    SNAT_IPV4 = 20,			/* u32 bits */
+    DNAT_IPV4 = 21,			/* u32 bits */
+    SNAT_PORT = 22,			/* u16 bits */
+    DNAT_PORT = 23,			/* u16 bits */
+    TIMEOUT = 24,			/* u32 bits */
+    MARK = 25,				/* u32 bits */
+    ORIG_COUNTER_PACKETS = 26,		/* u64 bits */
+    REPL_COUNTER_PACKETS = 27,		/* u64 bits */
+    ORIG_COUNTER_BYTES = 28,		/* u64 bits */
+    REPL_COUNTER_BYTES = 29,		/* u64 bits */
+    USE = 30,				/* u32 bits */
+    ID = 31,				/* u32 bits */
+    STATUS = 32,			/* u32 bits  */
+    TCP_FLAGS_ORIG = 33,		/* u8 bits */
+    TCP_FLAGS_REPL = 34,		/* u8 bits */
+    TCP_MASK_ORIG = 35,			/* u8 bits */
+    TCP_MASK_REPL = 36,		        /* u8 bits */
+    MASTER_IPV4_SRC = 37,		/* u32 bits */
+    MASTER_IPV4_DST = 38,		/* u32 bits */
+    MASTER_IPV6_SRC = 39,		/* u128 bits */
+    MASTER_IPV6_DST = 40,		/* u128 bits */
+    MASTER_PORT_SRC = 41,		/* u16 bits */
+    MASTER_PORT_DST = 42,		/* u16 bits */
+    MASTER_L3PROTO = 43,		/* u8 bits */
+    MASTER_L4PROTO = 44,		/* u8 bits */
+    SECMARK = 45,			/* u32 bits */
+    ORIG_NAT_SEQ_CORRECTION_POS = 46,	/* u32 bits */
+    ORIG_NAT_SEQ_OFFSET_BEFORE = 47,	/* u32 bits */
+    ORIG_NAT_SEQ_OFFSET_AFTER = 48,	/* u32 bits */
+    REPL_NAT_SEQ_CORRECTION_POS = 49,	/* u32 bits */
+    REPL_NAT_SEQ_OFFSET_BEFORE = 50,	/* u32 bits */
+    REPL_NAT_SEQ_OFFSET_AFTER = 51,	/* u32 bits */
+    SCTP_STATE = 52,			/* u8 bits */
+    SCTP_VTAG_ORIG = 53,		/* u32 bits */
+    SCTP_VTAG_REPL = 54,		/* u32 bits */
+    HELPER_NAME = 55,			/* string (30 bytes max) */
+    DCCP_STATE = 56,			/* u8 bits */
+    DCCP_ROLE = 57,			/* u8 bits */
+    DCCP_HANDSHAKE_SEQ = 58,		/* u64 bits */
+    TCP_WSCALE_ORIG = 59,		/* u8 bits */
+    TCP_WSCALE_REPL = 60,		/* u8 bits */
+    ZONE = 61,				/* u16 bits */
+    SECCTX = 62,			/* string */
+    TIMESTAMP_START = 63,		/* u64 bits, linux >= 2.6.38 */
+    TIMESTAMP_STOP = 64,		/* u64 bits, linux >= 2.6.38 */
+    HELPER_INFO = 65,			/* variable length */
+    CONNLABELS = 66,			/* variable length */
+    CONNLABELS_MASK = 67,		/* variable length */
+    ORIG_ZONE = 68,			/* u16 bits */
+    REPL_ZONE = 69,			/* u16 bits */
+    SNAT_IPV6 = 70,			/* u128 bits */
+    DNAT_IPV6 = 71,			/* u128 bits */
+    SYNPROXY_ISN = 72,			/* u32 bits */
+    SYNPROXY_ITS = 73,			/* u32 bits */
+    SYNPROXY_TSOFF = 74,		/* u32 bits */
+    MAX = 75,
+}
-- 
2.20.1






More information about the pve-devel mailing list