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