1use anyhow::Context;
4use byteorder::{ByteOrder, NativeEndian};
5use netlink_packet_utils::{
6 nla::{DefaultNla, Nla, NlaBuffer, NlasIterator},
7 parsers::{parse_string, parse_u32, parse_u8},
8 DecodeError, Emitable, Parseable, ParseableParametrized,
9};
10
11use super::{
12 TcOption, TcStats, TcStats2, TcStatsBuffer, TcXstats, VecTcOption,
13};
14
15const TCA_KIND: u16 = 1;
16const TCA_OPTIONS: u16 = 2;
17const TCA_STATS: u16 = 3;
18const TCA_XSTATS: u16 = 4;
19const TCA_RATE: u16 = 5;
20const TCA_FCNT: u16 = 6;
21const TCA_STATS2: u16 = 7;
22const TCA_STAB: u16 = 8;
23const TCA_DUMP_INVISIBLE: u16 = 10;
25const TCA_CHAIN: u16 = 11;
26const TCA_HW_OFFLOAD: u16 = 12;
27#[derive(Debug, PartialEq, Eq, Clone)]
31#[non_exhaustive]
32pub enum TcAttribute {
33 Kind(String),
35 Options(Vec<TcOption>),
37 Stats(TcStats),
39 Xstats(TcXstats),
41 Rate(Vec<u8>),
43 Fcnt(Vec<u8>),
44 Stats2(Vec<TcStats2>),
45 Stab(Vec<u8>),
46 Chain(u32),
47 HwOffload(u8),
48 DumpInvisible(bool),
49 Other(DefaultNla),
50}
51
52impl Nla for TcAttribute {
53 fn value_len(&self) -> usize {
54 match *self {
55 Self::Rate(ref bytes)
56 | Self::Fcnt(ref bytes)
57 | Self::Stab(ref bytes) => bytes.len(),
58 Self::Chain(_) => 4,
59 Self::Xstats(ref v) => v.buffer_len(),
60 Self::HwOffload(_) => 1,
61 Self::Stats2(ref v) => v.as_slice().buffer_len(),
62 Self::Stats(ref v) => v.buffer_len(),
63 Self::Kind(ref string) => string.len() + 1,
64 Self::Options(ref opt) => opt.as_slice().buffer_len(),
65 Self::DumpInvisible(_) => 0, Self::Other(ref attr) => attr.value_len(),
67 }
68 }
69
70 fn emit_value(&self, buffer: &mut [u8]) {
71 match *self {
72 Self::Rate(ref bytes)
73 | Self::Fcnt(ref bytes)
74 | Self::Stab(ref bytes) => buffer.copy_from_slice(bytes.as_slice()),
75 Self::Chain(v) => NativeEndian::write_u32(buffer, v),
76 Self::Xstats(ref v) => v.emit(buffer),
77 Self::HwOffload(ref val) => buffer[0] = *val,
78 Self::Stats2(ref stats) => stats.as_slice().emit(buffer),
79 Self::Stats(ref stats) => stats.emit(buffer),
80 Self::Kind(ref string) => {
81 buffer[..string.len()].copy_from_slice(string.as_bytes());
82 buffer[string.len()] = 0;
83 }
84 Self::Options(ref opt) => opt.as_slice().emit(buffer),
85 Self::DumpInvisible(_) => (),
86 Self::Other(ref attr) => attr.emit_value(buffer),
87 }
88 }
89
90 fn kind(&self) -> u16 {
91 match *self {
92 Self::Kind(_) => TCA_KIND,
93 Self::Options(_) => TCA_OPTIONS,
94 Self::Stats(_) => TCA_STATS,
95 Self::Xstats(_) => TCA_XSTATS,
96 Self::Rate(_) => TCA_RATE,
97 Self::Fcnt(_) => TCA_FCNT,
98 Self::Stats2(_) => TCA_STATS2,
99 Self::Stab(_) => TCA_STAB,
100 Self::Chain(_) => TCA_CHAIN,
101 Self::HwOffload(_) => TCA_HW_OFFLOAD,
102 Self::DumpInvisible(_) => TCA_DUMP_INVISIBLE,
103 Self::Other(ref nla) => nla.kind(),
104 }
105 }
106}
107
108impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized<NlaBuffer<&'a T>, &str>
109 for TcAttribute
110{
111 fn parse_with_param(
112 buf: &NlaBuffer<&'a T>,
113 kind: &str,
114 ) -> Result<Self, DecodeError> {
115 let payload = buf.value();
116 Ok(match buf.kind() {
117 TCA_KIND => TcAttribute::Kind(
118 parse_string(payload).context("invalid TCA_KIND")?,
119 ),
120 TCA_OPTIONS => TcAttribute::Options(
121 VecTcOption::parse_with_param(buf, kind)
122 .context(format!("Invalid TCA_OPTIONS for kind: {kind}"))?
123 .0,
124 ),
125 TCA_STATS => TcAttribute::Stats(
126 TcStats::parse(
127 &TcStatsBuffer::new_checked(payload)
128 .context("invalid TCA_STATS")?,
129 )
130 .context("failed to parse TCA_STATS")?,
131 ),
132 TCA_XSTATS => TcAttribute::Xstats(
133 TcXstats::parse_with_param(buf, kind)
134 .context("invalid TCA_XSTATS")?,
135 ),
136 TCA_RATE => TcAttribute::Rate(payload.to_vec()),
137 TCA_FCNT => TcAttribute::Fcnt(payload.to_vec()),
138 TCA_STATS2 => {
139 let mut nlas = vec![];
140 for nla in NlasIterator::new(payload) {
141 let nla = nla.context("invalid TCA_STATS2")?;
142 nlas.push(TcStats2::parse_with_param(&nla, kind).context(
143 format!("failed to parse TCA_STATS2 for kind {kind}"),
144 )?);
145 }
146 TcAttribute::Stats2(nlas)
147 }
148 TCA_STAB => TcAttribute::Stab(payload.to_vec()),
149 TCA_CHAIN => TcAttribute::Chain(
150 parse_u32(payload).context("failed to parse TCA_CHAIN")?,
151 ),
152 TCA_HW_OFFLOAD => TcAttribute::HwOffload(
153 parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?,
154 ),
155 TCA_DUMP_INVISIBLE => TcAttribute::DumpInvisible(true),
156 _ => TcAttribute::Other(
157 DefaultNla::parse(buf).context("failed to parse tc nla")?,
158 ),
159 })
160 }
161}