use anyhow::Context;
use netlink_packet_utils::{
byteorder::{ByteOrder, NativeEndian},
nla::{self, DefaultNla, NlaBuffer},
parsers::{parse_string, parse_u32, parse_u8},
DecodeError, Parseable,
};
use crate::{
FRA_DPORT_RANGE, FRA_DST, FRA_FLOW, FRA_FWMARK, FRA_FWMASK, FRA_GOTO,
FRA_IIFNAME, FRA_IP_PROTO, FRA_L3MDEV, FRA_OIFNAME, FRA_PAD, FRA_PRIORITY,
FRA_PROTOCOL, FRA_SPORT_RANGE, FRA_SRC, FRA_SUPPRESS_IFGROUP,
FRA_SUPPRESS_PREFIXLEN, FRA_TABLE, FRA_TUN_ID, FRA_UID_RANGE, FRA_UNSPEC,
};
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum Nla {
Unspec(Vec<u8>),
Destination(Vec<u8>),
Source(Vec<u8>),
Iifname(String),
Goto(u32),
Priority(u32),
FwMark(u32),
FwMask(u32),
Flow(u32),
TunId(u32),
SuppressIfGroup(u32),
SuppressPrefixLen(u32),
Table(u32),
OifName(String),
Pad(Vec<u8>),
L3MDev(u8),
UidRange(Vec<u8>),
Protocol(u8),
IpProto(u8),
SourcePortRange(Vec<u8>),
DestinationPortRange(Vec<u8>),
Other(DefaultNla),
}
impl nla::Nla for Nla {
fn value_len(&self) -> usize {
use self::Nla::*;
match self {
Unspec(ref bytes)
| Destination(ref bytes)
| Source(ref bytes)
| Pad(ref bytes)
| UidRange(ref bytes)
| SourcePortRange(ref bytes)
| DestinationPortRange(ref bytes) => bytes.len(),
Iifname(ref s) | OifName(ref s) => s.as_bytes().len() + 1,
Priority(_) | FwMark(_) | FwMask(_) | Flow(_) | TunId(_)
| Goto(_) | SuppressIfGroup(_) | SuppressPrefixLen(_)
| Table(_) => 4,
L3MDev(_) | Protocol(_) | IpProto(_) => 1,
Other(attr) => attr.value_len(),
}
}
fn kind(&self) -> u16 {
use self::Nla::*;
match self {
Unspec(_) => FRA_UNSPEC,
Destination(_) => FRA_DST,
Source(_) => FRA_SRC,
Iifname(_) => FRA_IIFNAME,
Goto(_) => FRA_GOTO,
Priority(_) => FRA_PRIORITY,
FwMark(_) => FRA_FWMARK,
FwMask(_) => FRA_FWMASK,
Flow(_) => FRA_FLOW,
TunId(_) => FRA_TUN_ID,
SuppressIfGroup(_) => FRA_SUPPRESS_IFGROUP,
SuppressPrefixLen(_) => FRA_SUPPRESS_PREFIXLEN,
Table(_) => FRA_TABLE,
OifName(_) => FRA_OIFNAME,
Pad(_) => FRA_PAD,
L3MDev(_) => FRA_L3MDEV,
UidRange(_) => FRA_UID_RANGE,
Protocol(_) => FRA_PROTOCOL,
IpProto(_) => FRA_IP_PROTO,
SourcePortRange(_) => FRA_SPORT_RANGE,
DestinationPortRange(_) => FRA_DPORT_RANGE,
Other(attr) => attr.kind(),
}
}
fn emit_value(&self, buffer: &mut [u8]) {
use self::Nla::*;
match self {
Unspec(ref bytes)
| Destination(ref bytes)
| Source(ref bytes)
| Pad(ref bytes)
| UidRange(ref bytes)
| SourcePortRange(ref bytes)
| DestinationPortRange(ref bytes) => {
buffer.copy_from_slice(bytes.as_slice())
}
Iifname(ref s) | OifName(ref s) => {
buffer[..s.len()].copy_from_slice(s.as_bytes())
}
Priority(value)
| FwMark(value)
| FwMask(value)
| Flow(value)
| TunId(value)
| Goto(value)
| SuppressIfGroup(value)
| SuppressPrefixLen(value)
| Table(value) => NativeEndian::write_u32(buffer, *value),
L3MDev(value) | Protocol(value) | IpProto(value) => {
buffer[0] = *value
}
Other(attr) => attr.emit_value(buffer),
}
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nla {
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
use Nla::*;
let payload = buf.value();
Ok(match buf.kind() {
FRA_UNSPEC => Unspec(payload.to_vec()),
FRA_DST => Destination(payload.to_vec()),
FRA_SRC => Source(payload.to_vec()),
FRA_IIFNAME => Iifname(
parse_string(payload).context("invalid FRA_IIFNAME value")?,
),
FRA_GOTO => {
Goto(parse_u32(payload).context("invalid FRA_GOTO value")?)
}
FRA_PRIORITY => Priority(
parse_u32(payload).context("invalid FRA_PRIORITY value")?,
),
FRA_FWMARK => {
FwMark(parse_u32(payload).context("invalid FRA_FWMARK value")?)
}
FRA_FLOW => {
Flow(parse_u32(payload).context("invalid FRA_FLOW value")?)
}
FRA_TUN_ID => {
TunId(parse_u32(payload).context("invalid FRA_TUN_ID value")?)
}
FRA_SUPPRESS_IFGROUP => SuppressIfGroup(
parse_u32(payload)
.context("invalid FRA_SUPPRESS_IFGROUP value")?,
),
FRA_SUPPRESS_PREFIXLEN => SuppressPrefixLen(
parse_u32(payload)
.context("invalid FRA_SUPPRESS_PREFIXLEN value")?,
),
FRA_TABLE => {
Table(parse_u32(payload).context("invalid FRA_TABLE value")?)
}
FRA_FWMASK => {
FwMask(parse_u32(payload).context("invalid FRA_FWMASK value")?)
}
FRA_OIFNAME => OifName(
parse_string(payload).context("invalid FRA_OIFNAME value")?,
),
FRA_PAD => Pad(payload.to_vec()),
FRA_L3MDEV => {
L3MDev(parse_u8(payload).context("invalid FRA_L3MDEV value")?)
}
FRA_UID_RANGE => UidRange(payload.to_vec()),
FRA_PROTOCOL => Protocol(
parse_u8(payload).context("invalid FRA_PROTOCOL value")?,
),
FRA_IP_PROTO => IpProto(
parse_u8(payload).context("invalid FRA_IP_PROTO value")?,
),
FRA_SPORT_RANGE => SourcePortRange(payload.to_vec()),
FRA_DPORT_RANGE => DestinationPortRange(payload.to_vec()),
_ => Other(
DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
),
})
}
}