rtnetlink/rule/
add.rs

1// SPDX-License-Identifier: MIT
2
3use futures::stream::StreamExt;
4use std::{
5    marker::PhantomData,
6    net::{Ipv4Addr, Ipv6Addr},
7};
8
9use netlink_packet_core::{
10    NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE,
11    NLM_F_REQUEST,
12};
13
14use netlink_packet_route::{
15    nlas::rule::Nla, RtnlMessage, RuleMessage, AF_INET, AF_INET6,
16    FR_ACT_UNSPEC, RT_TABLE_MAIN,
17};
18
19use crate::{try_nl, Error, Handle};
20
21/// A request to create a new rule. This is equivalent to the `ip rule add`
22/// command.
23pub struct RuleAddRequest<T = ()> {
24    handle: Handle,
25    message: RuleMessage,
26    replace: bool,
27    _phantom: PhantomData<T>,
28}
29
30impl<T> RuleAddRequest<T> {
31    pub(crate) fn new(handle: Handle) -> Self {
32        let mut message = RuleMessage::default();
33
34        message.header.table = RT_TABLE_MAIN;
35        message.header.action = FR_ACT_UNSPEC;
36
37        RuleAddRequest {
38            handle,
39            message,
40            replace: false,
41            _phantom: Default::default(),
42        }
43    }
44
45    /// Sets the input interface name.
46    pub fn input_interface(mut self, ifname: String) -> Self {
47        self.message.nlas.push(Nla::Iifname(ifname));
48        self
49    }
50
51    /// Sets the output interface name.
52    pub fn output_interface(mut self, ifname: String) -> Self {
53        self.message.nlas.push(Nla::OifName(ifname));
54        self
55    }
56
57    /// Sets the rule table.
58    ///
59    /// Default is main rule table.
60    #[deprecated(note = "Please use `table_id` instead")]
61    pub fn table(mut self, table: u8) -> Self {
62        self.message.header.table = table;
63        self
64    }
65
66    /// Sets the rule table ID.
67    ///
68    /// Default is main rule table.
69    pub fn table_id(mut self, table: u32) -> Self {
70        if table > 255 {
71            self.message.nlas.push(Nla::Table(table));
72        } else {
73            self.message.header.table = table as u8;
74        }
75        self
76    }
77
78    /// Set the tos.
79    pub fn tos(mut self, tos: u8) -> Self {
80        self.message.header.tos = tos;
81        self
82    }
83
84    /// Set action.
85    pub fn action(mut self, action: u8) -> Self {
86        self.message.header.action = action;
87        self
88    }
89
90    /// Set the priority.
91    pub fn priority(mut self, priority: u32) -> Self {
92        self.message.nlas.push(Nla::Priority(priority));
93        self
94    }
95
96    /// Build an IP v4 rule
97    pub fn v4(mut self) -> RuleAddRequest<Ipv4Addr> {
98        self.message.header.family = AF_INET as u8;
99        RuleAddRequest {
100            handle: self.handle,
101            message: self.message,
102            replace: false,
103            _phantom: Default::default(),
104        }
105    }
106
107    /// Build an IP v6 rule
108    pub fn v6(mut self) -> RuleAddRequest<Ipv6Addr> {
109        self.message.header.family = AF_INET6 as u8;
110        RuleAddRequest {
111            handle: self.handle,
112            message: self.message,
113            replace: false,
114            _phantom: Default::default(),
115        }
116    }
117
118    /// Replace existing matching rule.
119    pub fn replace(self) -> Self {
120        Self {
121            replace: true,
122            ..self
123        }
124    }
125
126    /// Execute the request.
127    pub async fn execute(self) -> Result<(), Error> {
128        let RuleAddRequest {
129            mut handle,
130            message,
131            replace,
132            ..
133        } = self;
134        let mut req = NetlinkMessage::from(RtnlMessage::NewRule(message));
135        let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL };
136        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE;
137
138        let mut response = handle.request(req)?;
139        while let Some(message) = response.next().await {
140            try_nl!(message);
141        }
142
143        Ok(())
144    }
145
146    pub fn message_mut(&mut self) -> &mut RuleMessage {
147        &mut self.message
148    }
149}
150
151impl RuleAddRequest<Ipv4Addr> {
152    /// Sets the source address prefix.
153    pub fn source_prefix(mut self, addr: Ipv4Addr, prefix_length: u8) -> Self {
154        self.message.header.src_len = prefix_length;
155        let src = addr.octets().to_vec();
156        self.message.nlas.push(Nla::Source(src));
157        self
158    }
159
160    /// Sets the destination address prefix.
161    pub fn destination_prefix(
162        mut self,
163        addr: Ipv4Addr,
164        prefix_length: u8,
165    ) -> Self {
166        self.message.header.dst_len = prefix_length;
167        let dst = addr.octets().to_vec();
168        self.message.nlas.push(Nla::Destination(dst));
169        self
170    }
171}
172
173impl RuleAddRequest<Ipv6Addr> {
174    /// Sets the source address prefix.
175    pub fn source_prefix(mut self, addr: Ipv6Addr, prefix_length: u8) -> Self {
176        self.message.header.src_len = prefix_length;
177        let src = addr.octets().to_vec();
178        self.message.nlas.push(Nla::Source(src));
179        self
180    }
181
182    /// Sets the destination address prefix.
183    pub fn destination_prefix(
184        mut self,
185        addr: Ipv6Addr,
186        prefix_length: u8,
187    ) -> Self {
188        self.message.header.dst_len = prefix_length;
189        let dst = addr.octets().to_vec();
190        self.message.nlas.push(Nla::Destination(dst));
191        self
192    }
193}