Skip to main content

netlink_packet_netfilter/
message.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_core::{
4    DecodeError, DefaultNla, Emitable, ErrorContext, NetlinkDeserializable,
5    NetlinkHeader, NetlinkPayload, NetlinkSerializable, ParseableParametrized,
6};
7use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
8
9use crate::{
10    conntrack::ConntrackMessage, nflog::ULogMessage, nftables::NfTablesMessage,
11    none::ControlMessage,
12};
13
14// NetfilterProtoFamily represents a protocol family in the Netfilter header
15// (nfgenmsg).
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum NetfilterProtoFamily {
19    Unspec,
20    Inet,
21    IPv4,
22    ARP,
23    NetDev,
24    Bridge,
25    IPv6,
26    DECNet,
27    Other(u8),
28}
29
30const NFPROTO_UNSPEC: u8 = 0;
31const NFPROTO_INET: u8 = 1;
32const NFPROTO_IPV4: u8 = 2;
33const NFPROTO_ARP: u8 = 3;
34const NFPROTO_NETDEV: u8 = 5;
35const NFPROTO_BRIDGE: u8 = 7;
36const NFPROTO_IPV6: u8 = 10;
37const NFPROTO_DECNET: u8 = 12;
38
39impl From<NetfilterProtoFamily> for u8 {
40    fn from(proto_family: NetfilterProtoFamily) -> Self {
41        match proto_family {
42            NetfilterProtoFamily::Unspec => NFPROTO_UNSPEC,
43            NetfilterProtoFamily::Inet => NFPROTO_INET,
44            NetfilterProtoFamily::IPv4 => NFPROTO_IPV4,
45            NetfilterProtoFamily::ARP => NFPROTO_ARP,
46            NetfilterProtoFamily::NetDev => NFPROTO_NETDEV,
47            NetfilterProtoFamily::Bridge => NFPROTO_BRIDGE,
48            NetfilterProtoFamily::IPv6 => NFPROTO_IPV6,
49            NetfilterProtoFamily::DECNet => NFPROTO_DECNET,
50            NetfilterProtoFamily::Other(p) => p,
51        }
52    }
53}
54
55impl From<u8> for NetfilterProtoFamily {
56    fn from(proto_family_num: u8) -> Self {
57        match proto_family_num {
58            NFPROTO_UNSPEC => NetfilterProtoFamily::Unspec,
59            NFPROTO_INET => NetfilterProtoFamily::Inet,
60            NFPROTO_IPV4 => NetfilterProtoFamily::IPv4,
61            NFPROTO_ARP => NetfilterProtoFamily::ARP,
62            NFPROTO_NETDEV => NetfilterProtoFamily::NetDev,
63            NFPROTO_BRIDGE => NetfilterProtoFamily::Bridge,
64            NFPROTO_IPV6 => NetfilterProtoFamily::IPv6,
65            NFPROTO_DECNET => NetfilterProtoFamily::DECNet,
66            _ => NetfilterProtoFamily::Other(proto_family_num),
67        }
68    }
69}
70
71pub(crate) const NETFILTER_HEADER_LEN: usize = 4;
72
73#[derive(
74    Debug,
75    PartialEq,
76    Eq,
77    Clone,
78    FromBytes,
79    IntoBytes,
80    KnownLayout,
81    Immutable,
82    Unaligned,
83)]
84#[repr(C, packed)]
85pub struct NetfilterHeaderBuffer {
86    family: u8,
87    version: u8,
88    res_id: u16,
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
92#[non_exhaustive]
93pub struct NetfilterHeader {
94    pub family: NetfilterProtoFamily,
95    pub version: u8,
96    pub res_id: u16,
97}
98
99impl NetfilterHeader {
100    pub fn new(family: NetfilterProtoFamily, version: u8, res_id: u16) -> Self {
101        Self {
102            family,
103            version,
104            res_id,
105        }
106    }
107
108    pub fn parse(payload: &[u8]) -> Result<Self, DecodeError> {
109        let (raw, _) = NetfilterHeaderBuffer::ref_from_prefix(payload)
110            .map_err(|_| {
111                DecodeError::buffer_too_small(
112                    payload.len(),
113                    NETFILTER_HEADER_LEN,
114                )
115            })?;
116        Ok(Self {
117            family: raw.family.into(),
118            version: raw.version,
119            res_id: u16::from_be(raw.res_id),
120        })
121    }
122}
123
124impl From<&NetfilterHeader> for NetfilterHeaderBuffer {
125    fn from(header: &NetfilterHeader) -> Self {
126        Self {
127            family: header.family.into(),
128            version: header.version,
129            res_id: header.res_id.to_be(),
130        }
131    }
132}
133
134impl Emitable for NetfilterHeader {
135    fn buffer_len(&self) -> usize {
136        NETFILTER_HEADER_LEN
137    }
138
139    fn emit(&self, buf: &mut [u8]) {
140        let raw = NetfilterHeaderBuffer::from(self);
141        buf[..NETFILTER_HEADER_LEN].copy_from_slice(raw.as_bytes());
142    }
143}
144
145// Defined in Linux kernel: include/uapi/linux/netfilter/nfnetlink.h
146const NFNL_SUBSYS_NONE: u8 = 0;
147const NFNL_SUBSYS_CTNETLINK: u8 = 1;
148const NFNL_SUBSYS_ULOG: u8 = 4;
149const NFNL_SUBSYS_NFTABLES: u8 = 10;
150
151#[derive(Debug, PartialEq, Eq, Clone, Copy)]
152#[non_exhaustive]
153pub enum NetfilterSubsystem {
154    None,
155    ULog,
156    Conntrack,
157    NfTables,
158    Other(u8),
159}
160
161impl From<u8> for NetfilterSubsystem {
162    fn from(value: u8) -> Self {
163        match value {
164            NFNL_SUBSYS_NONE => Self::None,
165            NFNL_SUBSYS_ULOG => Self::ULog,
166            NFNL_SUBSYS_CTNETLINK => Self::Conntrack,
167            NFNL_SUBSYS_NFTABLES => Self::NfTables,
168            v => Self::Other(v),
169        }
170    }
171}
172
173impl From<NetfilterSubsystem> for u8 {
174    fn from(value: NetfilterSubsystem) -> Self {
175        match value {
176            NetfilterSubsystem::None => NFNL_SUBSYS_NONE,
177            NetfilterSubsystem::ULog => NFNL_SUBSYS_ULOG,
178            NetfilterSubsystem::Conntrack => NFNL_SUBSYS_CTNETLINK,
179            NetfilterSubsystem::NfTables => NFNL_SUBSYS_NFTABLES,
180            NetfilterSubsystem::Other(v) => v,
181        }
182    }
183}
184
185#[derive(Debug, PartialEq, Eq, Clone)]
186#[non_exhaustive]
187pub enum NetfilterMessageInner {
188    None(ControlMessage),
189    ULog(ULogMessage),
190    Conntrack(ConntrackMessage),
191    NfTables(NfTablesMessage),
192    Other {
193        subsys: NetfilterSubsystem,
194        message_type: u8,
195        attributes: Vec<DefaultNla>,
196    },
197}
198
199impl From<ULogMessage> for NetfilterMessageInner {
200    fn from(message: ULogMessage) -> Self {
201        Self::ULog(message)
202    }
203}
204
205impl From<ConntrackMessage> for NetfilterMessageInner {
206    fn from(message: ConntrackMessage) -> Self {
207        Self::Conntrack(message)
208    }
209}
210
211impl From<NfTablesMessage> for NetfilterMessageInner {
212    fn from(message: NfTablesMessage) -> Self {
213        Self::NfTables(message)
214    }
215}
216
217impl From<ControlMessage> for NetfilterMessageInner {
218    fn from(message: ControlMessage) -> Self {
219        Self::None(message)
220    }
221}
222
223impl Emitable for NetfilterMessageInner {
224    fn buffer_len(&self) -> usize {
225        match self {
226            NetfilterMessageInner::None(message) => message.buffer_len(),
227            NetfilterMessageInner::ULog(message) => message.buffer_len(),
228            NetfilterMessageInner::Conntrack(message) => message.buffer_len(),
229            NetfilterMessageInner::NfTables(message) => message.buffer_len(),
230            NetfilterMessageInner::Other { attributes, .. } => {
231                attributes.as_slice().buffer_len()
232            }
233        }
234    }
235
236    fn emit(&self, buffer: &mut [u8]) {
237        match self {
238            NetfilterMessageInner::None(message) => message.emit(buffer),
239            NetfilterMessageInner::ULog(message) => message.emit(buffer),
240            NetfilterMessageInner::Conntrack(message) => message.emit(buffer),
241            NetfilterMessageInner::NfTables(message) => message.emit(buffer),
242            NetfilterMessageInner::Other { attributes, .. } => {
243                attributes.as_slice().emit(buffer)
244            }
245        }
246    }
247}
248
249#[derive(Debug, PartialEq, Eq, Clone)]
250#[non_exhaustive]
251pub struct NetfilterMessage {
252    pub header: NetfilterHeader,
253    pub inner: NetfilterMessageInner,
254}
255
256impl NetfilterMessage {
257    pub fn new<T: Into<NetfilterMessageInner>>(
258        header: NetfilterHeader,
259        inner: T,
260    ) -> Self {
261        Self {
262            header,
263            inner: inner.into(),
264        }
265    }
266
267    pub fn subsys(&self) -> NetfilterSubsystem {
268        match self.inner {
269            NetfilterMessageInner::None(_) => NetfilterSubsystem::None,
270            NetfilterMessageInner::ULog(_) => NetfilterSubsystem::ULog,
271            NetfilterMessageInner::Conntrack(_) => {
272                NetfilterSubsystem::Conntrack
273            }
274            NetfilterMessageInner::NfTables(_) => NetfilterSubsystem::NfTables,
275            NetfilterMessageInner::Other { subsys, .. } => subsys,
276        }
277    }
278
279    fn message_type(&self) -> u8 {
280        match self.inner {
281            NetfilterMessageInner::None(ref message) => {
282                message.message_type().into()
283            }
284            NetfilterMessageInner::ULog(ref message) => {
285                message.message_type().into()
286            }
287            NetfilterMessageInner::Conntrack(ref message) => {
288                message.message_type().into()
289            }
290            NetfilterMessageInner::NfTables(ref message) => {
291                message.message_type().into()
292            }
293            NetfilterMessageInner::Other { message_type, .. } => message_type,
294        }
295    }
296}
297
298impl ParseableParametrized<[u8], u16> for NetfilterMessage {
299    fn parse_with_param(
300        buf: &[u8],
301        message_type: u16,
302    ) -> Result<Self, DecodeError> {
303        let header = NetfilterHeader::parse(buf)
304            .context("failed to parse netfilter header")?;
305        let subsys = (message_type >> 8) as u8;
306        let message_type = message_type as u8;
307        let inner = match NetfilterSubsystem::from(subsys) {
308            NetfilterSubsystem::None => NetfilterMessageInner::None(
309                ControlMessage::parse_with_param(
310                    &buf[NETFILTER_HEADER_LEN..],
311                    message_type,
312                )
313                .context("failed to parse nfnetlink control message payload")?,
314            ),
315            NetfilterSubsystem::ULog => NetfilterMessageInner::ULog(
316                ULogMessage::parse_with_param(
317                    &buf[NETFILTER_HEADER_LEN..],
318                    message_type,
319                )
320                .context("failed to parse nflog payload")?,
321            ),
322            NetfilterSubsystem::Conntrack => NetfilterMessageInner::Conntrack(
323                ConntrackMessage::parse_with_param(
324                    &buf[NETFILTER_HEADER_LEN..],
325                    message_type,
326                )
327                .context("failed to parse conntrack payload")?,
328            ),
329            NetfilterSubsystem::NfTables => NetfilterMessageInner::NfTables(
330                NfTablesMessage::parse_with_param(
331                    &buf[NETFILTER_HEADER_LEN..],
332                    message_type,
333                )
334                .context("failed to parse nftables payload")?,
335            ),
336            subsys_enum @ NetfilterSubsystem::Other(_) => {
337                NetfilterMessageInner::Other {
338                    subsys: subsys_enum,
339                    message_type,
340                    attributes: crate::nlas::default_nlas(
341                        &buf[NETFILTER_HEADER_LEN..],
342                    )?,
343                }
344            }
345        };
346        Ok(NetfilterMessage::new(header, inner))
347    }
348}
349
350impl Emitable for NetfilterMessage {
351    fn buffer_len(&self) -> usize {
352        self.header.buffer_len() + self.inner.buffer_len()
353    }
354
355    fn emit(&self, buffer: &mut [u8]) {
356        self.header.emit(buffer);
357        self.inner.emit(&mut buffer[self.header.buffer_len()..]);
358    }
359}
360
361impl NetlinkSerializable for NetfilterMessage {
362    fn message_type(&self) -> u16 {
363        ((u8::from(self.subsys()) as u16) << 8) | self.message_type() as u16
364    }
365
366    fn buffer_len(&self) -> usize {
367        <Self as Emitable>::buffer_len(self)
368    }
369
370    fn serialize(&self, buffer: &mut [u8]) {
371        self.emit(buffer)
372    }
373}
374
375impl NetlinkDeserializable for NetfilterMessage {
376    type Error = DecodeError;
377    fn deserialize(
378        header: &NetlinkHeader,
379        payload: &[u8],
380    ) -> Result<Self, Self::Error> {
381        NetfilterMessage::parse_with_param(payload, header.message_type)
382    }
383}
384
385impl From<NetfilterMessage> for NetlinkPayload<NetfilterMessage> {
386    fn from(message: NetfilterMessage) -> Self {
387        NetlinkPayload::InnerMessage(message)
388    }
389}