Skip to content

Implement the parsing of conntrack messages over the netfilter protocol #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
authors = ["Loïc Damien <[email protected]>"]
name = "netlink-packet-netfilter"
name = "reyzell-netlink-packet-netfilter"
version = "0.2.0"
edition = "2018"

homepage = "https://github.com/rust-netlink/netlink-packet-netfilter"
homepage = "https://github.com/reyzell/netlink-packet-netfilter"
keywords = ["netlink", "linux", "netfilter"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/rust-netlink/netlink-packet-netfilter"
repository = "https://github.com/reyzell/netlink-packet-netfilter"
description = "netlink packet types for the netfilter subprotocol"

[dependencies]
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
# Rust netlink packet types for the netfilter subprotocol

This is a temporary fork of `netlink-packet-netfilter` [a] until conntrack
support is merged in.

[a] https://github.com/rust-netlink/netlink-packet-netfilter
90 changes: 90 additions & 0 deletions examples/nfconntrack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT

// To run this example:
// 1) build the example: cargo build --example nfconntrack
// 2) run it as root: sudo ../target/debug/examples/nfconntrack
// 3) Perform network activity from your host, which would result in conntrack updates:
// curl http://example.com

use reyzell_netlink_packet_netfilter::{
constants::{NFNLGRP_CONNTRACK_NEW, NFNLGRP_CONNTRACK_DESTROY},
nfconntrack::{
nlas::{ConnectionProperties, ConnectionTuple},
ConnectionNla, NfConntrackMessage,
},
NetfilterMessage, NetfilterMessageInner,
};
use netlink_packet_core::{NetlinkMessage, NetlinkPayload};
use netlink_sys::{constants::NETLINK_NETFILTER, Socket};

use std::convert::TryFrom;

fn print_connection_tuple(tuple: ConnectionTuple) {
match ConnectionProperties::try_from(tuple) {
Ok(cxn) => {
print!("{:?} ", cxn)
}
Err(e) => {
print!("[error] {:?} ", e)
}
}
}

fn print_connection_nla(nlas: Vec<ConnectionNla>) {
for nla in nlas {
match nla {
ConnectionNla::TupleOrig(tuple) => {
print!("[orig] ");
print_connection_tuple(tuple);
}
ConnectionNla::TupleReply(tuple) => {
print!("[reply] ");
print_connection_tuple(tuple);
}
_ => {}
}
}
}

fn main() {
let mut receive_buffer = vec![0; 4096];

let mut socket = Socket::new(NETLINK_NETFILTER).unwrap();
socket.bind_auto().unwrap();
socket.add_membership(NFNLGRP_CONNTRACK_NEW as u32).unwrap();
socket.add_membership(NFNLGRP_CONNTRACK_DESTROY as u32).unwrap();

loop {
match socket.recv(&mut &mut receive_buffer[..], 0) {
Ok(_size) => {
let bytes = &receive_buffer[..];
let msg =
<NetlinkMessage<NetfilterMessage>>::deserialize(bytes)
.unwrap();
if let NetlinkPayload::<NetfilterMessage>::InnerMessage(imsg) =
msg.payload
{
if let NetfilterMessageInner::NfConntrack(ct) = imsg.inner {
match ct {
NfConntrackMessage::ConnectionNew(nlas) => {
print!("[new] ");
print_connection_nla(nlas);
println!();
}
NfConntrackMessage::ConnectionDelete(nlas) => {
print!("[delete] ");
print_connection_nla(nlas);
println!();
}
_ => {}
}
}
}
}
Err(e) => {
println!("error while receiving packets: {:?}", e);
break;
}
}
}
}
2 changes: 1 addition & 1 deletion examples/nflog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{net::Ipv4Addr, time::Duration};

use byteorder::{ByteOrder, NetworkEndian};
use netlink_packet_core::{NetlinkMessage, NetlinkPayload};
use netlink_packet_netfilter::{
use reyzell_netlink_packet_netfilter::{
constants::*,
nflog::{
config_request,
Expand Down
5 changes: 5 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
NetfilterHeader, NetfilterMessage, NetfilterMessageInner,
NETFILTER_HEADER_LEN,
},
nfconntrack::NfConntrackMessage,
nflog::NfLogMessage,
};
use anyhow::Context;
Expand Down Expand Up @@ -56,6 +57,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
let subsys = (message_type >> 8) as u8;
let message_type = message_type as u8;
let inner = match subsys {
NfConntrackMessage::SUBSYS => NetfilterMessageInner::NfConntrack(
NfConntrackMessage::parse_with_param(buf, message_type)
.context("failed to parse nfconntrack payload")?,
),
NfLogMessage::SUBSYS => NetfilterMessageInner::NfLog(
NfLogMessage::parse_with_param(buf, message_type)
.context("failed to parse nflog payload")?,
Expand Down
29 changes: 29 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,32 @@ pub const NFULA_CT_INFO: u16 = libc::NFULA_CT_INFO as u16;

pub const NFULNL_MSG_CONFIG: u8 = libc::NFULNL_MSG_CONFIG as u8;
pub const NFULNL_MSG_PACKET: u8 = libc::NFULNL_MSG_PACKET as u8;

pub const NFNLGRP_CONNTRACK_NEW: u32 = 1;
pub const NFNLGRP_CONNTRACK_UPDATE: u32 = 2;
pub const NFNLGRP_CONNTRACK_DESTROY: u32 = 3;

pub const IPCTNL_MSG_CT_NEW: u8 = 0;
pub const IPCTNL_MSG_CT_GET: u8 = 1;
pub const IPCTNL_MSG_CT_DELETE: u8 = 2;
pub const IPCTNL_MSG_CT_GET_CTRZERO: u8 = 3;
pub const IPCTNL_MSG_CT_GET_STATS_CPU: u8 = 4;
pub const IPCTNL_MSG_CT_GET_STATS: u8 = 5;
pub const IPCTNL_MSG_CT_GET_DYING: u8 = 6;
pub const IPCTNL_MSG_CT_GET_UNCONFIRMED: u8 = 7;

pub const CTA_TUPLE_ORIG: u16 = 1;
pub const CTA_TUPLE_REPLY: u16 = 2;

pub const CTA_TUPLE_IP: u16 = 1;
pub const CTA_TUPLE_PROTO: u16 = 2;
pub const CTA_TUPLE_ZONE: u16 = 3;

pub const CTA_IP_V4_SRC: u16 = 1;
pub const CTA_IP_V4_DST: u16 = 2;
pub const CTA_IP_V6_SRC: u16 = 3;
pub const CTA_IP_V6_DST: u16 = 4;

pub const CTA_PROTO_NUM: u16 = 1;
pub const CTA_PROTO_SRC_PORT: u16 = 2;
pub const CTA_PROTO_DST_PORT: u16 = 3;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub(crate) mod buffer;
pub mod constants;
mod message;
pub use message::{NetfilterHeader, NetfilterMessage, NetfilterMessageInner};
pub mod nfconntrack;
pub mod nflog;
22 changes: 18 additions & 4 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use netlink_packet_utils::{
ParseableParametrized,
};

use crate::{buffer::NetfilterBuffer, nflog::NfLogMessage};
use crate::{
buffer::NetfilterBuffer, nfconntrack::NfConntrackMessage,
nflog::NfLogMessage,
};

pub const NETFILTER_HEADER_LEN: usize = 4;

Expand Down Expand Up @@ -61,6 +64,7 @@ impl<T: AsRef<[u8]>> Parseable<NetfilterHeaderBuffer<T>> for NetfilterHeader {

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum NetfilterMessageInner {
NfConntrack(NfConntrackMessage),
NfLog(NfLogMessage),
Other {
subsys: u8,
Expand All @@ -69,6 +73,12 @@ pub enum NetfilterMessageInner {
},
}

impl From<NfConntrackMessage> for NetfilterMessageInner {
fn from(message: NfConntrackMessage) -> Self {
Self::NfConntrack(message)
}
}

impl From<NfLogMessage> for NetfilterMessageInner {
fn from(message: NfLogMessage) -> Self {
Self::NfLog(message)
Expand All @@ -78,7 +88,8 @@ impl From<NfLogMessage> for NetfilterMessageInner {
impl Emitable for NetfilterMessageInner {
fn buffer_len(&self) -> usize {
match self {
NetfilterMessageInner::NfLog(message) => message.buffer_len(),
NetfilterMessageInner::NfConntrack(msg) => msg.buffer_len(),
NetfilterMessageInner::NfLog(msg) => msg.buffer_len(),
NetfilterMessageInner::Other { nlas, .. } => {
nlas.as_slice().buffer_len()
}
Expand All @@ -87,7 +98,8 @@ impl Emitable for NetfilterMessageInner {

fn emit(&self, buffer: &mut [u8]) {
match self {
NetfilterMessageInner::NfLog(message) => message.emit(buffer),
NetfilterMessageInner::NfConntrack(msg) => msg.emit(buffer),
NetfilterMessageInner::NfLog(msg) => msg.emit(buffer),
NetfilterMessageInner::Other { nlas, .. } => {
nlas.as_slice().emit(buffer)
}
Expand All @@ -114,14 +126,16 @@ impl NetfilterMessage {

pub fn subsys(&self) -> u8 {
match self.inner {
NetfilterMessageInner::NfConntrack(_) => NfConntrackMessage::SUBSYS,
NetfilterMessageInner::NfLog(_) => NfLogMessage::SUBSYS,
NetfilterMessageInner::Other { subsys, .. } => subsys,
}
}

pub fn message_type(&self) -> u8 {
match self.inner {
NetfilterMessageInner::NfLog(ref message) => message.message_type(),
NetfilterMessageInner::NfConntrack(ref msg) => msg.message_type(),
NetfilterMessageInner::NfLog(ref msg) => msg.message_type(),
NetfilterMessageInner::Other { message_type, .. } => message_type,
}
}
Expand Down
87 changes: 87 additions & 0 deletions src/nfconntrack/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT

use netlink_packet_utils::{
nla::DefaultNla, DecodeError, Emitable, Parseable, ParseableParametrized,
};

use crate::{
buffer::NetfilterBuffer,
constants::{
IPCTNL_MSG_CT_DELETE, IPCTNL_MSG_CT_NEW, NFNL_SUBSYS_CTNETLINK,
},
nfconntrack::nlas::ConnectionNla,
};

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum NfConntrackMessage {
ConnectionNew(Vec<ConnectionNla>),
ConnectionDelete(Vec<ConnectionNla>),
Other {
message_type: u8,
nlas: Vec<DefaultNla>,
},
}

impl NfConntrackMessage {
pub const SUBSYS: u8 = NFNL_SUBSYS_CTNETLINK;

pub fn message_type(&self) -> u8 {
match self {
NfConntrackMessage::ConnectionNew(_) => IPCTNL_MSG_CT_NEW,
NfConntrackMessage::ConnectionDelete(_) => IPCTNL_MSG_CT_DELETE,
NfConntrackMessage::Other { message_type, .. } => *message_type,
}
}
}

impl Emitable for NfConntrackMessage {
fn buffer_len(&self) -> usize {
match self {
NfConntrackMessage::ConnectionNew(nlas)
| NfConntrackMessage::ConnectionDelete(nlas) => {
nlas.as_slice().buffer_len()
}
NfConntrackMessage::Other { nlas, .. } => {
nlas.as_slice().buffer_len()
}
}
}

fn emit(&self, buffer: &mut [u8]) {
match self {
NfConntrackMessage::ConnectionNew(nlas)
| NfConntrackMessage::ConnectionDelete(nlas) => {
nlas.as_slice().emit(buffer)
}
NfConntrackMessage::Other { nlas, .. } => {
nlas.as_slice().emit(buffer)
}
};
}
}

impl<'a, T: AsRef<[u8]> + ?Sized>
ParseableParametrized<NetfilterBuffer<&'a T>, u8> for NfConntrackMessage
{
fn parse_with_param(
buf: &NetfilterBuffer<&'a T>,
message_type: u8,
) -> Result<Self, DecodeError> {
Ok(match message_type {
IPCTNL_MSG_CT_NEW => {
let nlas = buf
.parse_all_nlas(|nla_buf| ConnectionNla::parse(&nla_buf))?;
NfConntrackMessage::ConnectionNew(nlas)
}
IPCTNL_MSG_CT_DELETE => {
let nlas = buf
.parse_all_nlas(|nla_buf| ConnectionNla::parse(&nla_buf))?;
NfConntrackMessage::ConnectionDelete(nlas)
}
_ => NfConntrackMessage::Other {
message_type,
nlas: buf.default_nlas()?,
},
})
}
}
7 changes: 7 additions & 0 deletions src/nfconntrack/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT

mod message;
pub use message::NfConntrackMessage;
pub mod nlas;

pub use nlas::ConnectionNla;
Loading