fidl_fuchsia_net_filter_ext/
sync.rs1#![cfg(not(feature = "fdomain"))]
6
7use crate::{
8 Change, CommitError, ControllerCreationError, ControllerId, PushChangesError,
9 RegisterEbpfProgramError, handle_change_validation_result, handle_commit_result,
10};
11use fidl::marker::SourceBreaking;
12use flex_fuchsia_ebpf as febpf;
13use flex_fuchsia_net_filter as fnet_filter;
14
15pub struct Controller {
17 controller: fnet_filter::NamespaceControllerSynchronousProxy,
18 id: ControllerId,
22 pending_changes: Vec<Change>,
26}
27
28impl Controller {
29 pub fn new(
35 control: &fnet_filter::ControlSynchronousProxy,
36 ControllerId(id): &ControllerId,
37 deadline: zx::MonotonicInstant,
38 ) -> Result<Self, ControllerCreationError> {
39 let (controller, server_end) = fidl::endpoints::create_sync_proxy();
40 control.open_controller(id, server_end).map_err(ControllerCreationError::OpenController)?;
41
42 let fnet_filter::NamespaceControllerEvent::OnIdAssigned { id } =
43 controller.wait_for_event(deadline).map_err(ControllerCreationError::IdAssignment)?;
44 Ok(Self { controller, id: ControllerId(id), pending_changes: Vec::new() })
45 }
46
47 pub fn id(&self) -> &ControllerId {
48 &self.id
49 }
50
51 pub fn register_ebpf_program(
52 &mut self,
53 handle: febpf::ProgramHandle,
54 program: febpf::VerifiedProgram,
55 deadline: zx::MonotonicInstant,
56 ) -> Result<(), RegisterEbpfProgramError> {
57 self.controller
58 .register_ebpf_program(handle, program, deadline)
59 .map_err(RegisterEbpfProgramError::CallMethod)?
60 .map_err(RegisterEbpfProgramError::from)
61 }
62
63 pub fn push_changes(
64 &mut self,
65 changes: Vec<Change>,
66 deadline: zx::MonotonicInstant,
67 ) -> Result<(), PushChangesError> {
68 let fidl_changes = changes.iter().cloned().map(Into::into).collect::<Vec<_>>();
69 let result = self
70 .controller
71 .push_changes(&fidl_changes, deadline)
72 .map_err(PushChangesError::CallMethod)?;
73 handle_change_validation_result(result, &changes)?;
74 self.pending_changes.extend(changes);
78 Ok(())
79 }
80
81 pub fn commit_with_options(
82 &mut self,
83 options: fnet_filter::CommitOptions,
84 deadline: zx::MonotonicInstant,
85 ) -> Result<(), CommitError> {
86 let committed_changes = std::mem::take(&mut self.pending_changes);
87 let result = self.controller.commit(options, deadline).map_err(CommitError::CallMethod)?;
88 handle_commit_result(result, committed_changes)
89 }
90
91 pub fn commit(&mut self, deadline: zx::MonotonicInstant) -> Result<(), CommitError> {
92 self.commit_with_options(fnet_filter::CommitOptions::default(), deadline)
93 }
94
95 pub fn commit_idempotent(&mut self, deadline: zx::MonotonicInstant) -> Result<(), CommitError> {
96 self.commit_with_options(
97 fnet_filter::CommitOptions {
98 idempotent: Some(true),
99 __source_breaking: SourceBreaking,
100 },
101 deadline,
102 )
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::tests::{
110 handle_commit, handle_open_controller, handle_push_changes, pretend_invalid_resource,
111 test_resource, test_resource_id, unknown_resource_id,
112 };
113 use crate::{ChangeCommitError, ChangeValidationError};
114 use assert_matches::assert_matches;
115
116 #[fuchsia::test(threads = 2)]
117 async fn controller_push_changes_reports_invalid_change() {
118 let (control_sync, request_stream) =
119 fidl::endpoints::create_sync_proxy_and_stream::<fnet_filter::ControlMarker>();
120
121 let run_controller = fuchsia_async::Task::spawn(async {
122 let mut stream = handle_open_controller(request_stream).await;
123 handle_push_changes(
124 &mut stream,
125 fnet_filter::ChangeValidationResult::ErrorOnChange(vec![
126 fnet_filter::ChangeValidationError::Ok,
127 fnet_filter::ChangeValidationError::InvalidPortMatcher,
128 fnet_filter::ChangeValidationError::NotReached,
129 ]),
130 )
131 .await;
132 });
133
134 let mut controller = Controller::new(
135 &control_sync,
136 &ControllerId(String::from("test")),
137 zx::MonotonicInstant::INFINITE,
138 )
139 .expect("create controller");
140 let result = controller.push_changes(
141 vec![
142 Change::Create(test_resource()),
143 Change::Create(pretend_invalid_resource()),
146 Change::Remove(test_resource_id()),
147 ],
148 zx::MonotonicInstant::INFINITE,
149 );
150
151 assert_matches!(
152 result,
153 Err(PushChangesError::ErrorOnChange(errors)) if errors == vec![(
154 Change::Create(pretend_invalid_resource()),
155 ChangeValidationError::InvalidPortMatcher
156 )]
157 );
158
159 run_controller.await;
160 }
161
162 #[fuchsia::test(threads = 2)]
163 async fn controller_commit_reports_invalid_change() {
164 let (control_sync, request_stream) =
165 fidl::endpoints::create_sync_proxy_and_stream::<fnet_filter::ControlMarker>();
166
167 let run_controller = fuchsia_async::Task::spawn(async {
168 let mut stream = handle_open_controller(request_stream).await;
169 handle_push_changes(
170 &mut stream,
171 fnet_filter::ChangeValidationResult::Ok(fnet_filter::Empty {}),
172 )
173 .await;
174 handle_commit(
175 &mut stream,
176 fnet_filter::CommitResult::ErrorOnChange(vec![
177 fnet_filter::CommitError::Ok,
178 fnet_filter::CommitError::NamespaceNotFound,
179 fnet_filter::CommitError::Ok,
180 ]),
181 )
182 .await;
183 });
184
185 let mut controller = Controller::new(
186 &control_sync,
187 &ControllerId(String::from("test")),
188 zx::MonotonicInstant::INFINITE,
189 )
190 .expect("create controller");
191 controller
192 .push_changes(
193 vec![
194 Change::Create(test_resource()),
195 Change::Remove(unknown_resource_id()),
196 Change::Remove(test_resource_id()),
197 ],
198 zx::MonotonicInstant::INFINITE,
199 )
200 .expect("push changes");
201
202 let result = controller.commit(zx::MonotonicInstant::INFINITE);
203 assert_matches!(
204 result,
205 Err(CommitError::ErrorOnChange(errors)) if errors == vec![(
206 Change::Remove(unknown_resource_id()),
207 ChangeCommitError::NamespaceNotFound,
208 )]
209 );
210
211 run_controller.await;
212 }
213}