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