netlink_packet_route/rule/
attribute.rs

1// SPDX-License-Identifier: MIT
2
3use std::net::IpAddr;
4
5use anyhow::Context;
6use netlink_packet_utils::{
7    byteorder::{ByteOrder, NativeEndian},
8    nla::{DefaultNla, Nla, NlaBuffer},
9    parsers::{parse_string, parse_u32, parse_u8},
10    DecodeError, Emitable, Parseable,
11};
12
13use crate::{
14    ip::{emit_ip_addr, ip_addr_len, parse_ip_addr, IpProtocol},
15    route::{RouteProtocol, RouteRealm},
16    rule::{RulePortRange, RuleUidRange},
17};
18
19const FRA_DST: u16 = 1;
20const FRA_SRC: u16 = 2;
21const FRA_IIFNAME: u16 = 3;
22const FRA_GOTO: u16 = 4;
23// const FRA_UNUSED2: u16 = 5;
24const FRA_PRIORITY: u16 = 6;
25// const FRA_UNUSED3: u16 = 7;
26// const FRA_UNUSED4: u16 = 8;
27// const FRA_UNUSED5: u16 = 9;
28const FRA_FWMARK: u16 = 10;
29const FRA_FLOW: u16 = 11;
30const FRA_TUN_ID: u16 = 12;
31const FRA_SUPPRESS_IFGROUP: u16 = 13;
32const FRA_SUPPRESS_PREFIXLEN: u16 = 14;
33const FRA_TABLE: u16 = 15;
34const FRA_FWMASK: u16 = 16;
35const FRA_OIFNAME: u16 = 17;
36// const FRA_PAD: u16 = 18;
37const FRA_L3MDEV: u16 = 19;
38const FRA_UID_RANGE: u16 = 20;
39const FRA_PROTOCOL: u16 = 21;
40const FRA_IP_PROTO: u16 = 22;
41const FRA_SPORT_RANGE: u16 = 23;
42const FRA_DPORT_RANGE: u16 = 24;
43
44#[derive(Debug, PartialEq, Eq, Clone)]
45#[non_exhaustive]
46pub enum RuleAttribute {
47    /// destination address
48    Destination(IpAddr),
49    /// source address
50    Source(IpAddr),
51    /// input interface name
52    Iifname(String),
53    /// The priority number of another rule for [super::RuleAction::Goto]
54    Goto(u32),
55    Priority(u32),
56    FwMark(u32),
57    FwMask(u32),
58    /// IPv4 route realm
59    Realm(RouteRealm),
60    TunId(u32),
61    SuppressIfGroup(u32),
62    SuppressPrefixLen(u32),
63    Table(u32),
64    /// output interface name
65    Oifname(String),
66    L3MDev(bool),
67    UidRange(RuleUidRange),
68    Protocol(RouteProtocol),
69    IpProtocol(IpProtocol),
70    SourcePortRange(RulePortRange),
71    DestinationPortRange(RulePortRange),
72    Other(DefaultNla),
73}
74
75impl Nla for RuleAttribute {
76    fn value_len(&self) -> usize {
77        match self {
78            Self::Destination(ip) | Self::Source(ip) => ip_addr_len(ip),
79            Self::UidRange(v) => v.buffer_len(),
80            Self::SourcePortRange(v) | Self::DestinationPortRange(v) => {
81                v.buffer_len()
82            }
83            Self::Iifname(s) | Self::Oifname(s) => s.len() + 1,
84            Self::Priority(_)
85            | Self::FwMark(_)
86            | Self::FwMask(_)
87            | Self::TunId(_)
88            | Self::Goto(_)
89            | Self::SuppressIfGroup(_)
90            | Self::SuppressPrefixLen(_)
91            | Self::Table(_) => 4,
92            Self::Realm(v) => v.buffer_len(),
93            Self::L3MDev(_) | Self::Protocol(_) | Self::IpProtocol(_) => 1,
94            Self::Other(attr) => attr.value_len(),
95        }
96    }
97
98    fn kind(&self) -> u16 {
99        match self {
100            Self::Destination(_) => FRA_DST,
101            Self::Source(_) => FRA_SRC,
102            Self::Iifname(_) => FRA_IIFNAME,
103            Self::Goto(_) => FRA_GOTO,
104            Self::Priority(_) => FRA_PRIORITY,
105            Self::FwMark(_) => FRA_FWMARK,
106            Self::FwMask(_) => FRA_FWMASK,
107            Self::Realm(_) => FRA_FLOW,
108            Self::TunId(_) => FRA_TUN_ID,
109            Self::SuppressIfGroup(_) => FRA_SUPPRESS_IFGROUP,
110            Self::SuppressPrefixLen(_) => FRA_SUPPRESS_PREFIXLEN,
111            Self::Table(_) => FRA_TABLE,
112            Self::Oifname(_) => FRA_OIFNAME,
113            Self::L3MDev(_) => FRA_L3MDEV,
114            Self::UidRange(_) => FRA_UID_RANGE,
115            Self::Protocol(_) => FRA_PROTOCOL,
116            Self::IpProtocol(_) => FRA_IP_PROTO,
117            Self::SourcePortRange(_) => FRA_SPORT_RANGE,
118            Self::DestinationPortRange(_) => FRA_DPORT_RANGE,
119            Self::Other(attr) => attr.kind(),
120        }
121    }
122
123    fn emit_value(&self, buffer: &mut [u8]) {
124        match self {
125            Self::Destination(ip) | Self::Source(ip) => {
126                emit_ip_addr(ip, buffer)
127            }
128            Self::SourcePortRange(v) | Self::DestinationPortRange(v) => {
129                v.emit(buffer)
130            }
131            Self::UidRange(v) => v.emit(buffer),
132            Self::Iifname(s) | Self::Oifname(s) => {
133                buffer[..s.len()].copy_from_slice(s.as_bytes())
134            }
135            Self::Realm(v) => v.emit(buffer),
136            Self::Priority(value)
137            | Self::FwMark(value)
138            | Self::FwMask(value)
139            | Self::TunId(value)
140            | Self::Goto(value)
141            | Self::SuppressIfGroup(value)
142            | Self::SuppressPrefixLen(value)
143            | Self::Table(value) => NativeEndian::write_u32(buffer, *value),
144            Self::L3MDev(value) => buffer[0] = (*value).into(),
145            Self::IpProtocol(value) => buffer[0] = i32::from(*value) as u8,
146            Self::Protocol(value) => buffer[0] = u8::from(*value),
147            Self::Other(attr) => attr.emit_value(buffer),
148        }
149    }
150}
151
152impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
153    for RuleAttribute
154{
155    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
156        let payload = buf.value();
157
158        Ok(match buf.kind() {
159            FRA_DST => Self::Destination(
160                parse_ip_addr(payload)
161                    .context(format!("Invalid FRA_DST value {payload:?}"))?,
162            ),
163            FRA_SRC => Self::Source(
164                parse_ip_addr(payload)
165                    .context(format!("Invalid FRA_SRC value {payload:?}"))?,
166            ),
167            FRA_IIFNAME => Self::Iifname(
168                parse_string(payload).context("invalid FRA_IIFNAME value")?,
169            ),
170            FRA_GOTO => Self::Goto(
171                parse_u32(payload).context("invalid FRA_GOTO value")?,
172            ),
173            FRA_PRIORITY => Self::Priority(
174                parse_u32(payload).context("invalid FRA_PRIORITY value")?,
175            ),
176            FRA_FWMARK => Self::FwMark(
177                parse_u32(payload).context("invalid FRA_FWMARK value")?,
178            ),
179            FRA_FLOW => Self::Realm(
180                RouteRealm::parse(payload).context("invalid FRA_FLOW value")?,
181            ),
182            FRA_TUN_ID => Self::TunId(
183                parse_u32(payload).context("invalid FRA_TUN_ID value")?,
184            ),
185            FRA_SUPPRESS_IFGROUP => Self::SuppressIfGroup(
186                parse_u32(payload)
187                    .context("invalid FRA_SUPPRESS_IFGROUP value")?,
188            ),
189            FRA_SUPPRESS_PREFIXLEN => Self::SuppressPrefixLen(
190                parse_u32(payload)
191                    .context("invalid FRA_SUPPRESS_PREFIXLEN value")?,
192            ),
193            FRA_TABLE => Self::Table(
194                parse_u32(payload).context("invalid FRA_TABLE value")?,
195            ),
196            FRA_FWMASK => Self::FwMask(
197                parse_u32(payload).context("invalid FRA_FWMASK value")?,
198            ),
199            FRA_OIFNAME => Self::Oifname(
200                parse_string(payload).context("invalid FRA_OIFNAME value")?,
201            ),
202            FRA_L3MDEV => Self::L3MDev(
203                parse_u8(payload).context("invalid FRA_L3MDEV value")? > 0,
204            ),
205            FRA_UID_RANGE => Self::UidRange(
206                RuleUidRange::parse(payload)
207                    .context("invalid FRA_UID_RANGE value")?,
208            ),
209            FRA_PROTOCOL => Self::Protocol(
210                parse_u8(payload)
211                    .context("invalid FRA_PROTOCOL value")?
212                    .into(),
213            ),
214            FRA_IP_PROTO => Self::IpProtocol(IpProtocol::from(
215                parse_u8(payload).context("invalid FRA_IP_PROTO value")? as i32,
216            )),
217            FRA_SPORT_RANGE => Self::SourcePortRange(
218                RulePortRange::parse(payload)
219                    .context("invalid FRA_SPORT_RANGE value")?,
220            ),
221            FRA_DPORT_RANGE => Self::DestinationPortRange(
222                RulePortRange::parse(payload)
223                    .context("invalid FRA_DPORT_RANGE value")?,
224            ),
225            _ => Self::Other(
226                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
227            ),
228        })
229    }
230}