rtnetlink/traffic_control/
add_filter.rs1use futures_util::stream::StreamExt;
4
5use crate::{
6 packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST},
7 packet_route::{
8 tc::{
9 TcAction, TcActionAttribute, TcActionGeneric, TcActionMirror,
10 TcActionMirrorOption, TcActionOption, TcActionType, TcAttribute,
11 TcFilterU32, TcFilterU32Option, TcHandle, TcHeader, TcMessage,
12 TcMirror, TcMirrorActionType, TcOption, TcU32Key, TcU32Selector,
13 TcU32SelectorFlags,
14 },
15 RouteNetlinkMessage,
16 },
17 try_nl, Error, Handle,
18};
19
20#[derive(Debug, Clone)]
21pub struct TrafficFilterNewRequest {
22 handle: Handle,
23 message: TcMessage,
24 flags: u16,
25}
26
27impl TrafficFilterNewRequest {
28 pub(crate) fn new(handle: Handle, ifindex: i32, flags: u16) -> Self {
29 Self {
30 handle,
31 message: TcMessage::with_index(ifindex),
32 flags: NLM_F_REQUEST | flags,
33 }
34 }
35
36 pub async fn execute(self) -> Result<(), Error> {
38 let Self {
39 mut handle,
40 message,
41 flags,
42 } = self;
43
44 let mut req = NetlinkMessage::from(
45 RouteNetlinkMessage::NewTrafficFilter(message),
46 );
47 req.header.flags = NLM_F_ACK | flags;
48
49 let mut response = handle.request(req)?;
50 while let Some(message) = response.next().await {
51 try_nl!(message);
52 }
53 Ok(())
54 }
55
56 pub fn index(mut self, index: i32) -> Self {
59 self.message.header.index = index;
60 self
61 }
62
63 pub fn block(mut self, block_index: u32) -> Self {
66 self.message.header.index = TcHeader::TCM_IFINDEX_MAGIC_BLOCK as i32;
67 self.message.header.parent = block_index.into();
68 self
69 }
70
71 pub fn parent(mut self, parent: u32) -> Self {
75 self.message.header.parent = parent.into();
76 self
77 }
78
79 pub fn root(mut self) -> Self {
81 self.message.header.parent = TcHandle::ROOT;
82 self
83 }
84
85 pub fn ingress(mut self) -> Self {
87 self.message.header.parent = TcHandle {
88 major: 0xffff,
89 minor: TcHandle::MIN_INGRESS,
90 };
91 self
92 }
93
94 pub fn egress(mut self) -> Self {
96 self.message.header.parent = TcHandle {
97 major: 0xffff,
98 minor: TcHandle::MIN_EGRESS,
99 };
100 self
101 }
102
103 pub fn priority(mut self, priority: u16) -> Self {
106 self.message.header.info = u32::from(TcHandle {
107 major: priority,
108 minor: priority,
109 });
110 self
111 }
112
113 pub fn protocol(mut self, protocol: u16) -> Self {
117 self.message.header.info = u32::from(TcHandle {
118 major: (self.message.header.info >> 16) as u16,
119 minor: protocol,
120 });
121 self
122 }
123
124 pub fn u32(mut self, options: &[TcFilterU32Option]) -> Result<Self, Error> {
127 if self
128 .message
129 .attributes
130 .iter()
131 .any(|nla| matches!(nla, TcAttribute::Kind(_)))
132 {
133 return Err(Error::InvalidNla(
134 "message kind has already been set.".to_string(),
135 ));
136 }
137 self.message
138 .attributes
139 .push(TcAttribute::Kind(TcFilterU32::KIND.to_string()));
140 let mut nla_opts = Vec::new();
141 for opt in options {
142 nla_opts.push(TcOption::U32(opt.clone()));
143 }
144 self.message.attributes.push(TcAttribute::Options(nla_opts));
145 Ok(self)
146 }
147
148 pub fn redirect(self, dst_index: u32) -> Result<Self, Error> {
154 let mut sel_na = TcU32Selector::default();
155 sel_na.flags = TcU32SelectorFlags::Terminal;
156 sel_na.nkeys = 1;
157 sel_na.keys = vec![TcU32Key::default()];
158 let mut tc_mirror_nla = TcMirror::default();
159 tc_mirror_nla.generic = TcActionGeneric::default();
160 tc_mirror_nla.generic.action = TcActionType::Stolen;
161 tc_mirror_nla.eaction = TcMirrorActionType::EgressRedir;
162 tc_mirror_nla.ifindex = dst_index;
163 let mut action_nla = TcAction::default();
164 action_nla.attributes = vec![
165 TcActionAttribute::Kind(TcActionMirror::KIND.to_string()),
166 TcActionAttribute::Options(vec![TcActionOption::Mirror(
167 TcActionMirrorOption::Parms(tc_mirror_nla),
168 )]),
169 ];
170 let u32_nla = vec![
171 TcFilterU32Option::Selector(sel_na),
172 TcFilterU32Option::Action(vec![action_nla]),
173 ];
174 self.u32(&u32_nla)
175 }
176}
177
178#[cfg(test)]
179mod test {
180 use std::{fs::File, os::fd::AsFd, path::Path};
181
182 use futures_util::stream::TryStreamExt;
183 use nix::sched::{setns, CloneFlags};
184 use tokio::runtime::Runtime;
185
186 use crate::{
187 new_connection,
188 packet_route::{
189 link::LinkMessage,
190 tc::{
191 TcAttribute, TcFilterU32, TcFilterU32Option, TcOption,
192 TcU32Key, TcU32SelectorFlags,
193 },
194 },
195 Handle, LinkVeth, NetworkNamespace, NETNS_PATH, SELF_NS_PATH,
196 };
197
198 const TEST_NS: &str = "netlink_test_filter_ns";
199 const TEST_VETH_1: &str = "test_veth_1";
200 const TEST_VETH_2: &str = "test_veth_2";
201
202 struct Netns {
203 path: String,
204 _cur: File,
205 last: File,
206 }
207
208 impl Netns {
209 async fn new(path: &str) -> Self {
210 let last = File::open(Path::new(SELF_NS_PATH)).unwrap();
212
213 NetworkNamespace::add(path.to_string()).await.unwrap();
215
216 let ns_path = Path::new(NETNS_PATH);
218 let file = File::open(ns_path.join(path)).unwrap();
219 setns(file.as_fd(), CloneFlags::CLONE_NEWNET).unwrap();
220
221 Self {
222 path: path.to_string(),
223 _cur: file,
224 last,
225 }
226 }
227 }
228 impl Drop for Netns {
229 fn drop(&mut self) {
230 println!("exit ns: {}", self.path);
231 setns(self.last.as_fd(), CloneFlags::CLONE_NEWNET).unwrap();
232
233 let ns_path = Path::new(NETNS_PATH).join(&self.path);
234 nix::mount::umount2(&ns_path, nix::mount::MntFlags::MNT_DETACH)
235 .unwrap();
236 nix::unistd::unlink(&ns_path).unwrap();
237 }
242 }
243
244 async fn setup_env() -> (Handle, LinkMessage, LinkMessage, Netns) {
245 let netns = Netns::new(TEST_NS).await;
246
247 let (connection, handle, _) = new_connection().unwrap();
250 tokio::spawn(connection);
251 handle
252 .link()
253 .add(LinkVeth::new(TEST_VETH_1, TEST_VETH_2).build())
254 .execute()
255 .await
256 .unwrap();
257
258 let mut links = handle
259 .link()
260 .get()
261 .match_name(TEST_VETH_1.to_string())
262 .execute();
263 let link1 = links.try_next().await.unwrap();
264 links = handle
265 .link()
266 .get()
267 .match_name(TEST_VETH_2.to_string())
268 .execute();
269 let link2 = links.try_next().await.unwrap();
270 (handle, link1.unwrap(), link2.unwrap(), netns)
271 }
272
273 async fn test_async_new_filter() {
274 let (handle, test1, test2, _netns) = setup_env().await;
275 handle
276 .qdisc()
277 .add(test1.header.index as i32)
278 .ingress()
279 .execute()
280 .await
281 .unwrap();
282
283 handle
284 .qdisc()
285 .add(test2.header.index as i32)
286 .ingress()
287 .execute()
288 .await
289 .unwrap();
290
291 handle
292 .traffic_filter(test1.header.index as i32)
293 .add()
294 .parent(0xffff0000)
295 .protocol(0x0003)
296 .redirect(test2.header.index)
297 .unwrap()
298 .execute()
299 .await
300 .unwrap();
301
302 assert!(handle
304 .traffic_filter(test1.header.index as i32)
305 .add()
306 .parent(0xffff0000)
307 .protocol(0x0003)
308 .redirect(test2.header.index)
309 .unwrap()
310 .redirect(test1.header.index)
311 .is_err());
312
313 let mut filters_iter = handle
314 .traffic_filter(test1.header.index as i32)
315 .get()
316 .root()
317 .execute();
318
319 let mut found = false;
320 while let Some(nl_msg) = filters_iter.try_next().await.unwrap() {
321 if nl_msg.header.handle == 0x80000800.into() {
323 let mut iter = nl_msg.attributes.iter();
324 assert_eq!(
325 iter.next().unwrap(),
326 &TcAttribute::Kind(TcFilterU32::KIND.to_string()),
327 );
328 assert!(matches!(iter.next().unwrap(), &TcAttribute::Chain(_)));
329 let nla = iter.next().unwrap();
331 let filter = if let TcAttribute::Options(f) = nla {
332 f
333 } else {
334 panic!("expect options nla");
335 };
336 let mut fi = filter.iter();
337 let fa = fi.next().unwrap();
338 let ua = if let TcOption::U32(u) = fa {
339 u
340 } else {
341 panic!("expect u32 nla");
342 };
343 let sel = if let TcFilterU32Option::Selector(s) = ua {
345 s
346 } else {
347 panic!("expect sel nla");
348 };
349 assert_eq!(sel.flags, TcU32SelectorFlags::Terminal);
350 assert_eq!(sel.nkeys, 1);
351 assert_eq!(sel.keys.len(), 1);
352 assert_eq!(sel.keys[0], TcU32Key::default());
353 found = true;
354 break;
355 }
356 }
357 if !found {
358 panic!("not found :{} filter.", test1.header.index);
359 }
360 }
361
362 #[test]
363 fn test_new_filter() {
364 Runtime::new().unwrap().block_on(test_async_new_filter());
365 }
366}