1use crate::compat::private::OptionCompatLevelMut;
4use crate::{
5 uapi, Access, AddRuleError, AddRulesError, CompatError, CompatLevel, CompatResult, CompatState,
6 Compatible, HandleAccessError, HandleAccessesError, HandledAccess, PrivateHandledAccess,
7 PrivateRule, Rule, Ruleset, RulesetCreated, TailoredCompatLevel, TryCompat, ABI,
8};
9use enumflags2::{bitflags, BitFlags};
10use std::mem::zeroed;
11
12#[bitflags]
42#[repr(u64)]
43#[derive(Copy, Clone, Debug, PartialEq, Eq)]
44#[non_exhaustive]
45pub enum AccessNet {
46 BindTcp = uapi::LANDLOCK_ACCESS_NET_BIND_TCP as u64,
48 ConnectTcp = uapi::LANDLOCK_ACCESS_NET_CONNECT_TCP as u64,
50 BindUdp = uapi::LANDLOCK_ACCESS_NET_BIND_UDP as u64,
56 ConnectSendUdp = uapi::LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP as u64,
71}
72
73impl Access for AccessNet {
78 fn from_all(abi: ABI) -> BitFlags<Self> {
79 match abi {
80 ABI::Unsupported | ABI::V1 | ABI::V2 | ABI::V3 => BitFlags::EMPTY,
81 ABI::V4 | ABI::V5 | ABI::V6 | ABI::V7 | ABI::V8 | ABI::V9 => {
82 AccessNet::BindTcp | AccessNet::ConnectTcp
83 }
84 ABI::V10 => Self::from_all(ABI::V9) | AccessNet::BindUdp | AccessNet::ConnectSendUdp,
85 }
86 }
87}
88
89#[test]
90fn access_net_from_all() {
91 let tcp = AccessNet::BindTcp | AccessNet::ConnectTcp;
92 let udp = AccessNet::BindUdp | AccessNet::ConnectSendUdp;
93
94 assert_eq!(AccessNet::from_all(ABI::V3), BitFlags::EMPTY);
95 assert_eq!(AccessNet::from_all(ABI::V4), tcp);
96 assert_eq!(AccessNet::from_all(ABI::V9), tcp);
97 assert_eq!(AccessNet::from_all(ABI::V10), tcp | udp);
98}
99
100impl HandledAccess for AccessNet {}
101
102impl PrivateHandledAccess for AccessNet {
103 fn ruleset_handle_access(
104 ruleset: &mut Ruleset,
105 access: BitFlags<Self>,
106 ) -> Result<(), HandleAccessesError> {
107 ruleset.requested_handled_net |= access;
109 ruleset.actual_handled_net |= match access
110 .try_compat(
111 ruleset.compat.abi(),
112 ruleset.compat.level,
113 &mut ruleset.compat.state,
114 )
115 .map_err(HandleAccessError::Compat)?
116 {
117 Some(a) => a,
118 None => return Ok(()),
119 };
120 Ok(())
121 }
122
123 fn into_add_rules_error(error: AddRuleError<Self>) -> AddRulesError {
124 AddRulesError::Net(error)
125 }
126
127 fn into_handle_accesses_error(error: HandleAccessError<Self>) -> HandleAccessesError {
128 HandleAccessesError::Net(error)
129 }
130}
131
132#[derive(Debug)]
144pub struct NetPort {
145 attr: uapi::landlock_net_port_attr,
146 port: u16,
148 allowed_access: BitFlags<AccessNet>,
149 compat_level: Option<CompatLevel>,
150}
151
152impl NetPort {
155 pub fn new<A>(port: u16, access: A) -> Self
160 where
161 A: Into<BitFlags<AccessNet>>,
162 {
163 NetPort {
164 attr: unsafe { zeroed() },
166 port,
167 allowed_access: access.into(),
168 compat_level: None,
169 }
170 }
171}
172
173impl Rule<AccessNet> for NetPort {}
174
175impl PrivateRule<AccessNet> for NetPort {
176 const TYPE_ID: uapi::landlock_rule_type = uapi::landlock_rule_type_LANDLOCK_RULE_NET_PORT;
177
178 fn as_ptr(&mut self) -> *const libc::c_void {
179 self.attr.port = self.port as u64;
180 self.attr.allowed_access = self.allowed_access.bits();
181 &self.attr as *const _ as _
182 }
183
184 fn check_consistency(&self, ruleset: &RulesetCreated) -> Result<(), AddRulesError> {
185 if ruleset.requested_handled_net.contains(self.allowed_access) {
190 Ok(())
191 } else {
192 Err(AddRuleError::UnhandledAccess {
193 access: self.allowed_access,
194 incompatible: self.allowed_access & !ruleset.requested_handled_net,
195 }
196 .into())
197 }
198 }
199}
200
201#[test]
202fn net_port_check_consistency() {
203 use crate::*;
204
205 let bind = AccessNet::BindTcp;
206 let bind_connect = bind | AccessNet::ConnectTcp;
207
208 assert!(matches!(
209 Ruleset::from(ABI::Unsupported)
210 .handle_access(bind)
211 .unwrap()
212 .create()
213 .unwrap()
214 .add_rule(NetPort::new(1, bind_connect))
215 .unwrap_err(),
216 RulesetError::AddRules(AddRulesError::Net(AddRuleError::UnhandledAccess { access, incompatible }))
217 if access == bind_connect && incompatible == AccessNet::ConnectTcp
218 ));
219}
220
221impl TryCompat<AccessNet> for NetPort {
222 fn try_compat_children<L>(
223 mut self,
224 abi: ABI,
225 parent_level: L,
226 compat_state: &mut CompatState,
227 ) -> Result<Option<Self>, CompatError<AccessNet>>
228 where
229 L: Into<CompatLevel>,
230 {
231 self.allowed_access = match self.allowed_access.try_compat(
233 abi,
234 self.tailored_compat_level(parent_level),
235 compat_state,
236 )? {
237 Some(a) => a,
238 None => return Ok(None),
239 };
240 Ok(Some(self))
241 }
242
243 fn try_compat_inner(
244 &mut self,
245 _abi: ABI,
246 ) -> Result<CompatResult<AccessNet>, CompatError<AccessNet>> {
247 Ok(CompatResult::Full)
248 }
249}
250
251impl OptionCompatLevelMut for NetPort {
252 fn as_option_compat_level_mut(&mut self) -> &mut Option<CompatLevel> {
253 &mut self.compat_level
254 }
255}
256
257impl OptionCompatLevelMut for &mut NetPort {
258 fn as_option_compat_level_mut(&mut self) -> &mut Option<CompatLevel> {
259 &mut self.compat_level
260 }
261}
262
263impl Compatible for NetPort {}
264
265impl Compatible for &mut NetPort {}