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