rtnetlink/traffic_control/
add_filter.rs1use futures::stream::StreamExt;
4use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST};
5use netlink_packet_route::{
6 tc::{
7 self,
8 constants::{
9 TCA_ACT_TAB, TCA_EGRESS_REDIR, TC_ACT_STOLEN, TC_H_CLSACT,
10 TC_H_MAJ_MASK, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS, TC_H_MIN_MASK,
11 TC_H_ROOT, TC_U32_TERMINAL,
12 },
13 },
14 RtnlMessage, TcMessage, TCM_IFINDEX_MAGIC_BLOCK, TC_H_MAKE,
15};
16
17use crate::{try_nl, Error, Handle};
18
19pub struct TrafficFilterNewRequest {
20 handle: Handle,
21 message: TcMessage,
22 flags: u16,
23}
24
25impl TrafficFilterNewRequest {
26 pub(crate) fn new(handle: Handle, ifindex: i32, flags: u16) -> Self {
27 Self {
28 handle,
29 message: TcMessage::with_index(ifindex),
30 flags: NLM_F_REQUEST | flags,
31 }
32 }
33
34 pub async fn execute(self) -> Result<(), Error> {
36 let Self {
37 mut handle,
38 message,
39 flags,
40 } = self;
41
42 let mut req =
43 NetlinkMessage::from(RtnlMessage::NewTrafficFilter(message));
44 req.header.flags = NLM_F_ACK | flags;
45
46 let mut response = handle.request(req)?;
47 while let Some(message) = response.next().await {
48 try_nl!(message);
49 }
50 Ok(())
51 }
52
53 pub fn index(mut self, index: i32) -> Self {
56 self.message.header.index = index;
57 self
58 }
59
60 pub fn block(mut self, block_index: u32) -> Self {
63 self.message.header.index = TCM_IFINDEX_MAGIC_BLOCK as i32;
64 self.message.header.parent = block_index;
65 self
66 }
67
68 pub fn parent(mut self, parent: u32) -> Self {
72 self.message.header.parent = parent;
73 self
74 }
75
76 pub fn root(mut self) -> Self {
78 self.message.header.parent = TC_H_ROOT;
79 self
80 }
81
82 pub fn ingress(mut self) -> Self {
84 self.message.header.parent = TC_H_MAKE!(TC_H_CLSACT, TC_H_MIN_INGRESS);
85 self
86 }
87
88 pub fn egress(mut self) -> Self {
90 self.message.header.parent = TC_H_MAKE!(TC_H_CLSACT, TC_H_MIN_EGRESS);
91 self
92 }
93
94 pub fn priority(mut self, priority: u16) -> Self {
97 self.message.header.info =
98 TC_H_MAKE!((priority as u32) << 16, self.message.header.info);
99 self
100 }
101
102 pub fn protocol(mut self, protocol: u16) -> Self {
106 self.message.header.info =
107 TC_H_MAKE!(self.message.header.info, protocol as u32);
108 self
109 }
110
111 pub fn u32(mut self, data: Vec<tc::u32::Nla>) -> Result<Self, Error> {
114 if self
115 .message
116 .nlas
117 .iter()
118 .any(|nla| matches!(nla, tc::Nla::Kind(_)))
119 {
120 return Err(Error::InvalidNla(
121 "message kind has already been set.".to_string(),
122 ));
123 }
124 self.message
125 .nlas
126 .push(tc::Nla::Kind(tc::u32::KIND.to_string()));
127 self.message.nlas.push(tc::Nla::Options(
128 data.into_iter().map(tc::TcOpt::U32).collect(),
129 ));
130 Ok(self)
131 }
132
133 pub fn redirect(self, dst_index: u32) -> Result<Self, Error> {
139 let mut sel_na = tc::u32::Sel::default();
140 sel_na.flags = TC_U32_TERMINAL;
141 sel_na.nkeys = 1;
142 sel_na.keys = vec![tc::u32::Key::default()];
143 let mut tc_mirror_nla = tc::mirred::TcMirred::default();
144 tc_mirror_nla.action = TC_ACT_STOLEN;
145 tc_mirror_nla.eaction = TCA_EGRESS_REDIR;
146 tc_mirror_nla.ifindex = dst_index;
147 let mut action_nla = tc::Action::default();
148 action_nla.tab = TCA_ACT_TAB;
149 action_nla.nlas = vec![
150 tc::ActNla::Kind(tc::mirred::KIND.to_string()),
151 tc::ActNla::Options(vec![tc::ActOpt::Mirred(
152 tc::mirred::Nla::Parms(tc_mirror_nla),
153 )]),
154 ];
155 let u32_nla = vec![
156 tc::u32::Nla::Sel(sel_na),
157 tc::u32::Nla::Act(vec![action_nla]),
158 ];
159 self.u32(u32_nla)
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use std::{fs::File, os::unix::io::AsRawFd, path::Path};
166
167 use futures::stream::TryStreamExt;
168 use netlink_packet_route::LinkMessage;
169 use nix::sched::{setns, CloneFlags};
170 use tokio::runtime::Runtime;
171
172 use super::*;
173 use crate::{new_connection, NetworkNamespace, NETNS_PATH, SELF_NS_PATH};
174
175 const TEST_NS: &str = "netlink_test_filter_ns";
176 const TEST_VETH_1: &str = "test_veth_1";
177 const TEST_VETH_2: &str = "test_veth_2";
178
179 struct Netns {
180 path: String,
181 _cur: File,
182 last: File,
183 }
184
185 impl Netns {
186 async fn new(path: &str) -> Self {
187 let last = File::open(Path::new(SELF_NS_PATH)).unwrap();
189
190 NetworkNamespace::add(path.to_string()).await.unwrap();
192
193 let ns_path = Path::new(NETNS_PATH);
195 let file = File::open(ns_path.join(path)).unwrap();
196 setns(file.as_raw_fd(), CloneFlags::CLONE_NEWNET).unwrap();
197
198 Self {
199 path: path.to_string(),
200 _cur: file,
201 last,
202 }
203 }
204 }
205 impl Drop for Netns {
206 fn drop(&mut self) {
207 println!("exit ns: {}", self.path);
208 setns(self.last.as_raw_fd(), CloneFlags::CLONE_NEWNET).unwrap();
209
210 let ns_path = Path::new(NETNS_PATH).join(&self.path);
211 nix::mount::umount2(&ns_path, nix::mount::MntFlags::MNT_DETACH)
212 .unwrap();
213 nix::unistd::unlink(&ns_path).unwrap();
214 }
219 }
220
221 async fn setup_env() -> (Handle, LinkMessage, LinkMessage, Netns) {
222 let netns = Netns::new(TEST_NS).await;
223
224 let (connection, handle, _) = new_connection().unwrap();
227 tokio::spawn(connection);
228 handle
229 .link()
230 .add()
231 .veth(TEST_VETH_1.to_string(), TEST_VETH_2.to_string())
232 .execute()
233 .await
234 .unwrap();
235
236 let mut links = handle
237 .link()
238 .get()
239 .match_name(TEST_VETH_1.to_string())
240 .execute();
241 let link1 = links.try_next().await.unwrap();
242 links = handle
243 .link()
244 .get()
245 .match_name(TEST_VETH_2.to_string())
246 .execute();
247 let link2 = links.try_next().await.unwrap();
248 (handle, link1.unwrap(), link2.unwrap(), netns)
249 }
250
251 async fn test_async_new_filter() {
252 let (handle, test1, test2, _netns) = setup_env().await;
253 handle
254 .qdisc()
255 .add(test1.header.index as i32)
256 .ingress()
257 .execute()
258 .await
259 .unwrap();
260
261 handle
262 .qdisc()
263 .add(test2.header.index as i32)
264 .ingress()
265 .execute()
266 .await
267 .unwrap();
268
269 handle
270 .traffic_filter(test1.header.index as i32)
271 .add()
272 .parent(0xffff0000)
273 .protocol(0x0003)
274 .redirect(test2.header.index)
275 .unwrap()
276 .execute()
277 .await
278 .unwrap();
279
280 assert!(handle
282 .traffic_filter(test1.header.index as i32)
283 .add()
284 .parent(0xffff0000)
285 .protocol(0x0003)
286 .redirect(test2.header.index)
287 .unwrap()
288 .redirect(test1.header.index)
289 .is_err());
290
291 let mut filters_iter = handle
292 .traffic_filter(test1.header.index as i32)
293 .get()
294 .root()
295 .execute();
296
297 let mut found = false;
298 while let Some(nl_msg) = filters_iter.try_next().await.unwrap() {
299 if nl_msg.header.handle == 0x80000800 {
301 let mut iter = nl_msg.nlas.iter();
302 assert_eq!(
303 iter.next().unwrap(),
304 &tc::Nla::Kind(String::from(tc::u32::KIND))
305 );
306 assert!(matches!(iter.next().unwrap(), &tc::Nla::Chain(_)));
307 let nla = iter.next().unwrap();
309 let filter = if let tc::Nla::Options(f) = nla {
310 f
311 } else {
312 panic!("expect options nla");
313 };
314 let mut fi = filter.iter();
315 let fa = fi.next().unwrap();
316 let ua = if let tc::TcOpt::U32(u) = fa {
317 u
318 } else {
319 panic!("expect u32 nla");
320 };
321 let sel = if let tc::u32::Nla::Sel(s) = ua {
323 s
324 } else {
325 panic!("expect sel nla");
326 };
327 assert_eq!(sel.flags, TC_U32_TERMINAL);
328 assert_eq!(sel.nkeys, 1);
329 assert_eq!(sel.keys.len(), 1);
330 assert_eq!(sel.keys[0], tc::u32::Key::default());
331 found = true;
332 break;
333 }
334 }
335 if !found {
336 panic!("not found :{} filter.", test1.header.index);
337 }
338 }
339
340 #[test]
341 fn test_new_filter() {
342 Runtime::new().unwrap().block_on(test_async_new_filter());
343 }
344}