rtnetlink/rule/
add.rs

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