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

Mira Limbeck m.limbeck at proxmox.com
Fri Oct 16 15:24:15 CEST 2020


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_NAME, SECCTX, 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 during insert as
they can't be inserted using libnetfilter_conntrack (conntrack-tools'
conntrack also exhibits the same behavior). They are still dumped.

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

Signed-off-by: Mira Limbeck <m.limbeck at proxmox.com>
---
 Cargo.toml                 |  15 ++
 src/main.rs                | 473 +++++++++++++++++++++++++++++++++++++
 src/mnl.rs                 | 131 ++++++++++
 src/netfilter_conntrack.rs | 170 +++++++++++++
 4 files changed, 789 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..d3d1d9e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,15 @@
+[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.68"
+nix = "0.16.1"
+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..e20db53
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,473 @@
+mod mnl;
+mod netfilter_conntrack;
+
+use anyhow::{bail, Result};
+use mnl::*;
+use netfilter_conntrack::*;
+use nix::errno::{errno, Errno};
+use serde::{Deserialize, Serialize};
+use serde_json::*;
+
+fn main() -> Result<()> {
+    let args: Vec<String> = std::env::args().collect();
+    if args.len() != 2 {
+        bail!("Either 'dump' or 'insert' command required.");
+    }
+
+    let mut conntracks = Conntracks::open()?;
+
+    if args[1] == "dump" {
+        let mut cts = Vec::new();
+        if let Err(err) = conntracks.query_all(&mut cts) {
+            bail!("Error querying conntracks: {}", err);
+        }
+
+        for ct in cts.iter() {
+            let ct_str = match to_string(ct) {
+                Ok(s) => s,
+                Err(err) => {
+                    eprintln!("Failed to serialize conntrack: {}", err);
+                    break;
+                }
+            };
+            println!("{}", ct_str);
+        }
+    } else if args[1] == "insert" {
+        let mut buffer = String::new();
+        loop {
+            match std::io::stdin().read_line(&mut buffer) {
+                Ok(0) => break,
+                Ok(size) => {
+                    let input = &buffer[..size];
+                    let ct: Conntrack = match from_str(input) {
+                        Ok(ct) => ct,
+                        Err(err) => {
+                            eprintln!("Failed to deserialize conntrack: {}", err);
+                            break;
+                        }
+                    };
+                    if ct.is_ipv6() {
+                        if let Err(err) = conntracks.insert_ipv6(ct) {
+                            eprintln!("Error inserting IPV6 flow: {}", err);
+                        }
+                    } else if let Err(err) = conntracks.insert_ipv4(ct) {
+                        eprintln!("Error inserting IPV4 flow: {}", err);
+                    }
+                }
+                Err(err) => {
+                    eprintln!("Failed to read line from stdin: {}", err);
+                    break;
+                }
+            }
+            buffer.clear();
+        }
+    } else {
+        bail!("Unknown command: {}", args[1]);
+    }
+
+    Ok(())
+}
+
+fn post_inc(val: &mut u32) -> u32 {
+    let orig = *val;
+    *val += 1;
+    orig
+}
+
+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);
+    }
+
+    use std::convert::TryInto;
+    let mut attributes = Vec::new();
+    for (attr, ty) in ALL_ATTRIBUTES.iter() {
+        if *attr == ATTR_ID {
+            continue;
+        }
+        if unsafe { nfct_attr_is_set(ct, *attr) } == 0 {
+            continue;
+        }
+        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),
+                });
+            }
+            // ignore other cases for now
+            _ => {}
+        }
+    }
+    let mut skip = false;
+    for attr in attributes.iter() {
+        if let Attr {
+            key: ATTR_ORIG_L4PROTO,
+            value: AttrValue::U8(2),
+        } = attr
+        {
+            // ignore conntrack if proto == IGMP, can't be inserted
+            skip = true;
+            break;
+        }
+    }
+    if !skip {
+        let cts: &mut Vec<Conntrack> = unsafe { &mut *(data_ptr as *mut Vec<Conntrack>) };
+        cts.push(Conntrack { attributes });
+    }
+
+    unsafe {
+        nfct_destroy(ct);
+    }
+
+    MNL_CB_OK
+}
+
+struct Conntracks {
+    buffer: [u8; MNL_SOCKET_DUMP_SIZE as _],
+    socket: *mut mnl_socket,
+    seq: u32,
+}
+
+impl Conntracks {
+    pub fn open() -> Result<Self> {
+        let nl = unsafe { mnl_socket_open(libc::NETLINK_NETFILTER) };
+        if nl.is_null() {
+            bail!("Failed to open MNL socket");
+        }
+
+        let res = unsafe { mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) };
+        if res < 0 {
+            bail!("Failed to bind MNL socket");
+        }
+
+        Ok(Self {
+            buffer: [0u8; MNL_SOCKET_DUMP_SIZE as _],
+            socket: nl,
+            seq: 0,
+        })
+    }
+
+    pub fn query_all(&mut self, cts: &mut Vec<Conntrack>) -> Result<()> {
+        self.query_ipv4(cts)?;
+        self.query_ipv6(cts)?;
+        Ok(())
+    }
+
+    pub fn query_ipv4(&mut self, cts: &mut Vec<Conntrack>) -> Result<()> {
+        let (msg, seq) = self.build_query_msg(libc::AF_INET as _);
+        self.send_and_receive(
+            msg,
+            seq,
+            Some(query_cts_cb),
+            cts as *mut Vec<Conntrack> as _,
+        )
+    }
+
+    pub fn query_ipv6(&mut self, cts: &mut Vec<Conntrack>) -> Result<()> {
+        let (msg, seq) = self.build_query_msg(libc::AF_INET6 as _);
+        self.send_and_receive(
+            msg,
+            seq,
+            Some(query_cts_cb),
+            cts as *mut Vec<Conntrack> as _,
+        )
+    }
+
+    pub fn insert_ipv4(&mut self, ct: Conntrack) -> Result<()> {
+        let (msg, seq) = self.build_insert_msg(libc::AF_INET as _, ct)?;
+        self.send_and_receive(msg, seq, None, std::ptr::null_mut())
+    }
+
+    pub fn insert_ipv6(&mut self, ct: Conntrack) -> Result<()> {
+        let (msg, seq) = self.build_insert_msg(libc::AF_INET6 as _, ct)?;
+        self.send_and_receive(msg, seq, None, std::ptr::null_mut())
+    }
+
+    fn build_query_msg(&mut self, proto: u8) -> (*const libc::nlmsghdr, u32) {
+        let seq = post_inc(&mut self.seq);
+        let nlh = unsafe { mnl_nlmsg_put_header(self.buffer.as_mut_ptr() as _) };
+        unsafe {
+            (*nlh).nlmsg_type = ((libc::NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET) as u16;
+            (*nlh).nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16;
+            (*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 as _;
+            (*nfh).version = libc::NFNETLINK_V0 as _;
+            (*nfh).res_id = 0;
+        }
+        (nlh, seq)
+    }
+
+    fn build_insert_msg(
+        &mut self,
+        proto: u8,
+        ct: Conntrack,
+    ) -> Result<(*const libc::nlmsghdr, u32)> {
+        let seq = post_inc(&mut self.seq);
+        let nlh = unsafe { mnl_nlmsg_put_header(self.buffer.as_mut_ptr() as _) };
+        unsafe {
+            (*nlh).nlmsg_type = ((libc::NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW) as u16;
+            (*nlh).nlmsg_flags =
+                (libc::NLM_F_REQUEST | libc::NLM_F_CREATE | libc::NLM_F_ACK) as u16;
+            (*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 as _;
+            (*nfh).version = libc::NFNETLINK_V0 as _;
+            (*nfh).res_id = 0;
+        }
+
+        let cth = unsafe { nfct_new() };
+        if cth.is_null() {
+            bail!("Failed to create new conntrack object");
+        }
+
+        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));
+                },
+                _ => {}
+            }
+        }
+
+        unsafe {
+            nfct_nlmsg_build(nlh, cth);
+        }
+
+        Ok((nlh, seq))
+    }
+
+    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, msg as _, (*msg).nlmsg_len as _) };
+        if res == -1 {
+            bail!("Failed to send message");
+        }
+
+        let portid = unsafe { mnl_socket_get_portid(self.socket) };
+
+        loop {
+            let res = unsafe {
+                mnl_socket_recvfrom(
+                    self.socket,
+                    self.buffer.as_mut_ptr() as _,
+                    MNL_SOCKET_DUMP_SIZE as _,
+                )
+            };
+            if res == -1 {
+                bail!("Failed to read message");
+            }
+
+            let res = unsafe {
+                mnl_cb_run(
+                    self.buffer.as_mut_ptr() as _,
+                    res as _,
+                    seq,
+                    portid,
+                    cb,
+                    data,
+                )
+            };
+            if res == -1 {
+                bail!("Failed to run callback: {}", Errno::from_i32(errno()));
+            } else if res <= MNL_CB_STOP {
+                break;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl Drop for Conntracks {
+    fn drop(&mut self) {
+        if !self.socket.is_null() {
+            let res = unsafe { mnl_socket_close(self.socket) };
+            if res < 0 {
+                eprintln!("Error closing socket");
+            }
+        }
+    }
+}
+
+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]),
+    Bytes(Vec<u8>),
+}
+
+#[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
+    }
+}
+
+const ALL_ATTRIBUTES: [(CTAttr, AttrType); 75] = [
+    (ATTR_ORIG_IPV4_SRC, AttrType::U32),               /* u32 bits */
+    (ATTR_ORIG_IPV4_DST, AttrType::U32),               /* u32 bits */
+    (ATTR_REPL_IPV4_SRC, AttrType::U32),               /* u32 bits */
+    (ATTR_REPL_IPV4_DST, AttrType::U32),               /* u32 bits */
+    (ATTR_ORIG_IPV6_SRC, AttrType::U128),              /* u128 bits */
+    (ATTR_ORIG_IPV6_DST, AttrType::U128),              /* u128 bits */
+    (ATTR_REPL_IPV6_SRC, AttrType::U128),              /* u128 bits */
+    (ATTR_REPL_IPV6_DST, AttrType::U128),              /* u128 bits */
+    (ATTR_ORIG_PORT_SRC, AttrType::U16),               /* u16 bits */
+    (ATTR_ORIG_PORT_DST, AttrType::U16),               /* u16 bits */
+    (ATTR_REPL_PORT_SRC, AttrType::U16),               /* u16 bits */
+    (ATTR_REPL_PORT_DST, AttrType::U16),               /* u16 bits */
+    (ATTR_ICMP_TYPE, AttrType::U8),                    /* u8 bits */
+    (ATTR_ICMP_CODE, AttrType::U8),                    /* u8 bits */
+    (ATTR_ICMP_ID, AttrType::U16),                     /* u16 bits */
+    (ATTR_ORIG_L3PROTO, AttrType::U8),                 /* u8 bits */
+    (ATTR_REPL_L3PROTO, AttrType::U8),                 /* u8 bits */
+    (ATTR_ORIG_L4PROTO, AttrType::U8),                 /* u8 bits */
+    (ATTR_REPL_L4PROTO, AttrType::U8),                 /* u8 bits */
+    (ATTR_TCP_STATE, AttrType::U8),                    /* u8 bits */
+    (ATTR_SNAT_IPV4, AttrType::U32),                   /* u32 bits */
+    (ATTR_DNAT_IPV4, AttrType::U32),                   /* u32 bits */
+    (ATTR_SNAT_PORT, AttrType::U16),                   /* u16 bits */
+    (ATTR_DNAT_PORT, AttrType::U16),                   /* u16 bits */
+    (ATTR_TIMEOUT, AttrType::U32),                     /* u32 bits */
+    (ATTR_MARK, AttrType::U32),                        /* u32 bits */
+    (ATTR_ORIG_COUNTER_PACKETS, AttrType::U64),        /* u64 bits */
+    (ATTR_REPL_COUNTER_PACKETS, AttrType::U64),        /* u64 bits */
+    (ATTR_ORIG_COUNTER_BYTES, AttrType::U64),          /* u64 bits */
+    (ATTR_REPL_COUNTER_BYTES, AttrType::U64),          /* u64 bits */
+    (ATTR_USE, AttrType::U32),                         /* u32 bits */
+    (ATTR_ID, AttrType::U32),                          /* u32 bits */
+    (ATTR_STATUS, AttrType::U32),                      /* u32 bits  */
+    (ATTR_TCP_FLAGS_ORIG, AttrType::U8),               /* u8 bits */
+    (ATTR_TCP_FLAGS_REPL, AttrType::U8),               /* u8 bits */
+    (ATTR_TCP_MASK_ORIG, AttrType::U8),                /* u8 bits */
+    (ATTR_TCP_MASK_REPL, AttrType::U8),                /* u8 bits */
+    (ATTR_MASTER_IPV4_SRC, AttrType::U32),             /* u32 bits */
+    (ATTR_MASTER_IPV4_DST, AttrType::U32),             /* u32 bits */
+    (ATTR_MASTER_IPV6_SRC, AttrType::U128),            /* u128 bits */
+    (ATTR_MASTER_IPV6_DST, AttrType::U128),            /* u128 bits */
+    (ATTR_MASTER_PORT_SRC, AttrType::U16),             /* u16 bits */
+    (ATTR_MASTER_PORT_DST, AttrType::U16),             /* u16 bits */
+    (ATTR_MASTER_L3PROTO, AttrType::U8),               /* u8 bits */
+    (ATTR_MASTER_L4PROTO, AttrType::U8),               /* u8 bits */
+    (ATTR_SECMARK, AttrType::U32),                     /* u32 bits */
+    (ATTR_ORIG_NAT_SEQ_CORRECTION_POS, AttrType::U32), /* u32 bits */
+    (ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, AttrType::U32),  /* u32 bits */
+    (ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, AttrType::U32),   /* u32 bits */
+    (ATTR_REPL_NAT_SEQ_CORRECTION_POS, AttrType::U32), /* u32 bits */
+    (ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, AttrType::U32),  /* u32 bits */
+    (ATTR_REPL_NAT_SEQ_OFFSET_AFTER, AttrType::U32),   /* u32 bits */
+    (ATTR_SCTP_STATE, AttrType::U8),                   /* u8 bits */
+    (ATTR_SCTP_VTAG_ORIG, AttrType::U32),              /* u32 bits */
+    (ATTR_SCTP_VTAG_REPL, AttrType::U32),              /* u32 bits */
+    (ATTR_HELPER_NAME, AttrType::String(Some(30))),    /* string (30 bytes max) */
+    (ATTR_DCCP_STATE, AttrType::U8),                   /* u8 bits */
+    (ATTR_DCCP_ROLE, AttrType::U8),                    /* u8 bits */
+    (ATTR_DCCP_HANDSHAKE_SEQ, AttrType::U64),          /* u64 bits */
+    (ATTR_TCP_WSCALE_ORIG, AttrType::U8),              /* u8 bits */
+    (ATTR_TCP_WSCALE_REPL, AttrType::U8),              /* u8 bits */
+    (ATTR_ZONE, AttrType::U16),                        /* u16 bits */
+    (ATTR_SECCTX, AttrType::String(None)),             /* string */
+    (ATTR_TIMESTAMP_START, AttrType::U64),             /* u64 bits; linux >= 2.6.38 */
+    (ATTR_TIMESTAMP_STOP, AttrType::U64),              /* u64 bits; linux >= 2.6.38 */
+    (ATTR_HELPER_INFO, AttrType::VarLen),              /* variable length */
+    (ATTR_CONNLABELS, AttrType::VarLen),               /* variable length */
+    (ATTR_CONNLABELS_MASK, AttrType::VarLen),          /* variable length */
+    (ATTR_ORIG_ZONE, AttrType::U16),                   /* u16 bits */
+    (ATTR_REPL_ZONE, AttrType::U16),                   /* u16 bits */
+    (ATTR_SNAT_IPV6, AttrType::U128),                  /* u128 bits */
+    (ATTR_DNAT_IPV6, AttrType::U128),                  /* u128 bits */
+    (ATTR_SYNPROXY_ISN, AttrType::U32),                /* u32 bits */
+    (ATTR_SYNPROXY_ITS, AttrType::U32),                /* u32 bits */
+    (ATTR_SYNPROXY_TSOFF, AttrType::U32),              /* u32 bits */
+];
diff --git a/src/mnl.rs b/src/mnl.rs
new file mode 100644
index 0000000..a03e6cc
--- /dev/null
+++ b/src/mnl.rs
@@ -0,0 +1,131 @@
+#![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_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..5897e2e
--- /dev/null
+++ b/src/netfilter_conntrack.rs
@@ -0,0 +1,170 @@
+#![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;
+
+pub type CTAttr = u32;
+pub const ATTR_ORIG_IPV4_SRC: CTAttr = 0;			/* u32 bits */
+pub const ATTR_IPV4_SRC: CTAttr = ATTR_ORIG_IPV4_SRC;   	/* alias */
+pub const ATTR_ORIG_IPV4_DST: CTAttr = 1;			/* u32 bits */
+pub const ATTR_IPV4_DST: CTAttr = ATTR_ORIG_IPV4_DST;	        /* alias */
+pub const ATTR_REPL_IPV4_SRC: CTAttr = 2;		        /* u32 bits */
+pub const ATTR_REPL_IPV4_DST: CTAttr = 3;			/* u32 bits */
+pub const ATTR_ORIG_IPV6_SRC: CTAttr = 4;			/* u128 bits */
+pub const ATTR_IPV6_SRC: CTAttr = ATTR_ORIG_IPV6_SRC;	        /* alias */
+pub const ATTR_ORIG_IPV6_DST: CTAttr = 5;			/* u128 bits */
+pub const ATTR_IPV6_DST: CTAttr = ATTR_ORIG_IPV6_DST;	        /* alias */
+pub const ATTR_REPL_IPV6_SRC: CTAttr = 6;			/* u128 bits */
+pub const ATTR_REPL_IPV6_DST: CTAttr = 7;			/* u128 bits */
+pub const ATTR_ORIG_PORT_SRC: CTAttr = 8;			/* u16 bits */
+pub const ATTR_PORT_SRC: CTAttr = ATTR_ORIG_PORT_SRC;	        /* alias */
+pub const ATTR_ORIG_PORT_DST: CTAttr = 9;			/* u16 bits */
+pub const ATTR_PORT_DST: CTAttr = ATTR_ORIG_PORT_DST;	        /* alias */
+pub const ATTR_REPL_PORT_SRC: CTAttr = 10;			/* u16 bits */
+pub const ATTR_REPL_PORT_DST: CTAttr = 11;			/* u16 bits */
+pub const ATTR_ICMP_TYPE: CTAttr = 12;			        /* u8 bits */
+pub const ATTR_ICMP_CODE: CTAttr = 13;				/* u8 bits */
+pub const ATTR_ICMP_ID: CTAttr = 14;				/* u16 bits */
+pub const ATTR_ORIG_L3PROTO: CTAttr = 15;			/* u8 bits */
+pub const ATTR_L3PROTO: CTAttr = ATTR_ORIG_L3PROTO;	        /* alias */
+pub const ATTR_REPL_L3PROTO: CTAttr = 16;			/* u8 bits */
+pub const ATTR_ORIG_L4PROTO: CTAttr = 17;			/* u8 bits */
+pub const ATTR_L4PROTO: CTAttr = ATTR_ORIG_L4PROTO;	        /* alias */
+pub const ATTR_REPL_L4PROTO: CTAttr = 18;			/* u8 bits */
+pub const ATTR_TCP_STATE: CTAttr = 19;				/* u8 bits */
+pub const ATTR_SNAT_IPV4: CTAttr = 20;			        /* u32 bits */
+pub const ATTR_DNAT_IPV4: CTAttr = 21;				/* u32 bits */
+pub const ATTR_SNAT_PORT: CTAttr = 22;				/* u16 bits */
+pub const ATTR_DNAT_PORT: CTAttr = 23;				/* u16 bits */
+pub const ATTR_TIMEOUT: CTAttr = 24;			        /* u32 bits */
+pub const ATTR_MARK: CTAttr = 25;				/* u32 bits */
+pub const ATTR_ORIG_COUNTER_PACKETS: CTAttr = 26;		/* u64 bits */
+pub const ATTR_REPL_COUNTER_PACKETS: CTAttr = 27;		/* u64 bits */
+pub const ATTR_ORIG_COUNTER_BYTES: CTAttr = 28;		        /* u64 bits */
+pub const ATTR_REPL_COUNTER_BYTES: CTAttr = 29;		        /* u64 bits */
+pub const ATTR_USE: CTAttr = 30;				/* u32 bits */
+pub const ATTR_ID: CTAttr = 31;				        /* u32 bits */
+pub const ATTR_STATUS: CTAttr = 32;			        /* u32 bits  */
+pub const ATTR_TCP_FLAGS_ORIG: CTAttr = 33;			/* u8 bits */
+pub const ATTR_TCP_FLAGS_REPL: CTAttr = 34;			/* u8 bits */
+pub const ATTR_TCP_MASK_ORIG: CTAttr = 35;			/* u8 bits */
+pub const ATTR_TCP_MASK_REPL: CTAttr = 36;		        /* u8 bits */
+pub const ATTR_MASTER_IPV4_SRC: CTAttr = 37;			/* u32 bits */
+pub const ATTR_MASTER_IPV4_DST: CTAttr = 38;			/* u32 bits */
+pub const ATTR_MASTER_IPV6_SRC: CTAttr = 39;			/* u128 bits */
+pub const ATTR_MASTER_IPV6_DST: CTAttr = 40;		        /* u128 bits */
+pub const ATTR_MASTER_PORT_SRC: CTAttr = 41;			/* u16 bits */
+pub const ATTR_MASTER_PORT_DST: CTAttr = 42;			/* u16 bits */
+pub const ATTR_MASTER_L3PROTO: CTAttr = 43;			/* u8 bits */
+pub const ATTR_MASTER_L4PROTO: CTAttr = 44;		        /* u8 bits */
+pub const ATTR_SECMARK: CTAttr = 45;				/* u32 bits */
+pub const ATTR_ORIG_NAT_SEQ_CORRECTION_POS: CTAttr = 46;	/* u32 bits */
+pub const ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE: CTAttr = 47;	        /* u32 bits */
+pub const ATTR_ORIG_NAT_SEQ_OFFSET_AFTER: CTAttr = 48;	        /* u32 bits */
+pub const ATTR_REPL_NAT_SEQ_CORRECTION_POS: CTAttr = 49;	/* u32 bits */
+pub const ATTR_REPL_NAT_SEQ_OFFSET_BEFORE: CTAttr = 50;	        /* u32 bits */
+pub const ATTR_REPL_NAT_SEQ_OFFSET_AFTER: CTAttr = 51;		/* u32 bits */
+pub const ATTR_SCTP_STATE: CTAttr = 52;			        /* u8 bits */
+pub const ATTR_SCTP_VTAG_ORIG: CTAttr = 53;			/* u32 bits */
+pub const ATTR_SCTP_VTAG_REPL: CTAttr = 54;			/* u32 bits */
+pub const ATTR_HELPER_NAME: CTAttr = 55;			/* string (30 bytes max) */
+pub const ATTR_DCCP_STATE: CTAttr = 56;			        /* u8 bits */
+pub const ATTR_DCCP_ROLE: CTAttr = 57;				/* u8 bits */
+pub const ATTR_DCCP_HANDSHAKE_SEQ: CTAttr = 58;		        /* u64 bits */
+pub const ATTR_TCP_WSCALE_ORIG: CTAttr = 59;			/* u8 bits */
+pub const ATTR_TCP_WSCALE_REPL: CTAttr = 60;		        /* u8 bits */
+pub const ATTR_ZONE: CTAttr = 61;				/* u16 bits */
+pub const ATTR_SECCTX: CTAttr = 62;				/* string */
+pub const ATTR_TIMESTAMP_START: CTAttr = 63;			/* u64 bits; linux >= 2.6.38 */
+pub const ATTR_TIMESTAMP_STOP: CTAttr = 64;		        /* u64 bits; linux >= 2.6.38 */
+pub const ATTR_HELPER_INFO: CTAttr = 65;			/* variable length */
+pub const ATTR_CONNLABELS: CTAttr = 66;			        /* variable length */
+pub const ATTR_CONNLABELS_MASK: CTAttr = 67;			/* variable length */
+pub const ATTR_ORIG_ZONE: CTAttr = 68;				/* u16 bits */
+pub const ATTR_REPL_ZONE: CTAttr = 69;				/* u16 bits */
+pub const ATTR_SNAT_IPV6: CTAttr = 70;				/* u128 bits */
+pub const ATTR_DNAT_IPV6: CTAttr = 71;				/* u128 bits */
+pub const ATTR_SYNPROXY_ISN: CTAttr = 72;			/* u32 bits */
+pub const ATTR_SYNPROXY_ITS: CTAttr = 73;			/* u32 bits */
+pub const ATTR_SYNPROXY_TSOFF: CTAttr = 74;			/* u32 bits */
+pub const ATTR_MAX: CTAttr = 75;
-- 
2.20.1






More information about the pve-devel mailing list