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