netlink_packet_route/rule/
attribute.rs

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