1use std::collections::HashSet;
6use std::pin::{Pin, pin};
7
8use fidl::endpoints::Proxy as _;
9use fidl_fuchsia_hardware_network as fhardware_network;
10use fidl_fuchsia_net_interfaces as fnet_interfaces;
11use fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin;
12use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
13use fidl_fuchsia_net_stack as fnet_stack;
14use fidl_fuchsia_net_virtualization as fnet_virtualization;
15
16use anyhow::{Context as _, anyhow};
17use async_trait::async_trait;
18use derivative::Derivative;
19use futures::channel::oneshot;
20use futures::{FutureExt as _, StreamExt as _, TryStreamExt as _, future};
21use log::{debug, error, info, warn};
22
23use crate::errors::{self, ContextExt as _};
24use crate::{DeviceClass, exit_with_fidl_error};
25
26#[derive(Debug, Clone)]
28pub(super) enum NetworkId {
29 Bridged,
35}
36
37#[derive(Debug)]
41pub(super) enum NetworkRequest {
42 Request(fnet_virtualization::NetworkRequest, future::Shared<oneshot::Receiver<()>>),
43 Finished(oneshot::Sender<()>),
44}
45
46#[derive(Derivative)]
47#[derivative(Debug)]
48pub(super) enum Event {
49 ControlRequestStream(#[derivative(Debug = "ignore")] fnet_virtualization::ControlRequestStream),
50 ControlRequest(fnet_virtualization::ControlRequest),
51 NetworkRequest(NetworkId, NetworkRequest),
52 InterfaceClose(NetworkId, u64),
53}
54
55pub(super) type EventStream = Pin<Box<dyn futures::stream::Stream<Item = Event>>>;
56
57enum BridgeState {
60 Init,
61 WaitingForGuests {
62 upstream: u64,
65 upstream_candidates: HashSet<u64>,
66 },
67 WaitingForUpstream {
68 guests: HashSet<u64>,
69 },
70 Bridged {
71 handle: BridgeHandle,
72 upstream: u64,
77 upstream_candidates: HashSet<u64>,
78 guests: HashSet<u64>,
79 },
80}
81
82impl Default for BridgeState {
83 fn default() -> Self {
84 BridgeState::Init
85 }
86}
87
88#[async_trait(?Send)]
89pub(super) trait Handler {
90 async fn handle_event(
91 &'async_trait mut self,
92 event: Event,
93 events: &'async_trait mut futures::stream::SelectAll<EventStream>,
94 ) -> Result<(), errors::Error>;
95
96 async fn handle_interface_update_result(
97 &mut self,
98 update_result: &fnet_interfaces_ext::UpdateResult<
99 '_,
100 (),
101 fnet_interfaces_ext::DefaultInterest,
102 >,
103 ) -> Result<(), errors::Error>;
104}
105
106fn take_any<T: std::marker::Copy + std::cmp::Eq + std::hash::Hash>(
110 set: &mut HashSet<T>,
111) -> Option<T> {
112 set.iter().copied().next().map(|elem| {
113 assert!(set.remove(&elem));
114 elem
115 })
116}
117
118pub(super) struct Virtualization<'a, B: BridgeHandler> {
119 installer: fnet_interfaces_admin::InstallerProxy,
120 _allowed_upstream_device_classes: &'a HashSet<DeviceClass>,
123 bridge: Bridge<B>,
124}
125
126impl<'a, B: BridgeHandler> Virtualization<'a, B> {
127 pub fn new(
128 _allowed_upstream_device_classes: &'a HashSet<DeviceClass>,
129 allowed_bridge_upstream_device_classes: HashSet<DeviceClass>,
130 bridge_handler: B,
131 installer: fnet_interfaces_admin::InstallerProxy,
132 ) -> Self {
133 Self {
134 installer,
135 _allowed_upstream_device_classes,
136 bridge: Bridge {
137 allowed_bridge_upstream_device_classes,
138 bridge_handler,
139 bridge_state: Default::default(),
140 },
141 }
142 }
143
144 async fn handle_network_request(
145 &mut self,
146 network_id: NetworkId,
147 request: NetworkRequest,
148 events: &mut futures::stream::SelectAll<EventStream>,
149 ) -> Result<(), errors::Error> {
150 let Self { installer, bridge, _allowed_upstream_device_classes } = self;
151 match request {
152 NetworkRequest::Request(
153 fnet_virtualization::NetworkRequest::AddPort { port, interface, control_handle: _ },
154 mut network_close_rx,
155 ) => {
156 let (device, server_end) =
163 fidl::endpoints::create_endpoints::<fhardware_network::DeviceMarker>();
164 let port = port.into_proxy();
165 port.get_device(server_end)
166 .context("call get device")
167 .map_err(errors::Error::NonFatal)?;
168
169 let (device_control, server_end) =
170 fidl::endpoints::create_proxy::<fnet_interfaces_admin::DeviceControlMarker>();
171 installer
172 .install_device(device, server_end)
173 .unwrap_or_else(|err| exit_with_fidl_error(err));
174
175 let fhardware_network::PortInfo { id: port_id, .. } = port
177 .get_info()
178 .await
179 .context("get port info")
180 .map_err(errors::Error::NonFatal)?;
181 let port_id = port_id
182 .context("port id not included in port info")
183 .map_err(errors::Error::NonFatal)?;
184 let (control, server_end) = fnet_interfaces_ext::admin::Control::create_endpoints()
185 .context("create Control endpoints")
186 .map_err(errors::Error::NonFatal)?;
187 device_control
188 .create_interface(
189 &port_id,
190 server_end,
191 fnet_interfaces_admin::Options::default(),
192 )
193 .context("call create interface")
194 .map_err(errors::Error::NonFatal)?;
195 let id = control
196 .get_id()
197 .await
198 .context("call get id")
199 .map_err(errors::Error::NonFatal)?;
200
201 if !control
202 .enable()
203 .await
204 .context("call enable")
205 .map_err(errors::Error::NonFatal)?
206 .map_err(|e| anyhow!("failed to enable interface: {:?}", e))
207 .map_err(errors::Error::NonFatal)?
208 {
209 warn!("added interface {} was already enabled", id);
210 }
211
212 match network_id {
213 NetworkId::Bridged => {
214 bridge
216 .add_guest_to_bridge(id)
217 .await
218 .context("adding interface to bridge")?;
219 }
220 }
221
222 let shutdown_fut = async move {
225 let mut interface_closure = interface
226 .into_stream()
227 .map(|request| {
228 request.map(|_request: fnet_virtualization::InterfaceRequest| ())
233 })
234 .try_collect::<()>();
235 let mut device_control_closure = pin!(device_control.on_closed().fuse());
236 let mut control_termination = pin!(control.wait_termination().fuse());
237 let reason = futures::select! {
238 result = interface_closure => {
240 format!("interface channel closed by client: {:?}", result)
241 },
242 result = device_control_closure => {
244 match result {
245 Ok(zx::Signals::CHANNEL_PEER_CLOSED) => {},
246 result => error!(
247 "got unexpected result waiting for device control \
248 channel closure: {:?}",
249 result,
250 ),
251 }
252 "device detached from netstack".to_string()
253 }
254 result = network_close_rx => {
257 result.expect("sender should not be dropped");
258 "network has been shut down".to_string()
259 },
260 terminal_error = control_termination => {
263 format!(
264 "interface control channel closed: {:?}",
265 terminal_error
266 )
267 }
268 };
269 info!("interface {}: {}, removing interface", id, reason);
270 id
271 };
272 events.push(
273 futures::stream::once(
274 shutdown_fut.map(|id| Event::InterfaceClose(network_id, id)),
275 )
276 .boxed(),
277 );
278 }
279 NetworkRequest::Finished(network_close_tx) => {
280 match network_close_tx.send(()) {
282 Ok(()) => {}
283 Err(()) => {
284 info!("removing virtualized network with no devices attached")
285 }
286 }
287 }
288 }
289 Ok(())
290 }
291}
292
293struct Bridge<B: BridgeHandler> {
294 allowed_bridge_upstream_device_classes: HashSet<DeviceClass>,
295 bridge_handler: B,
296 bridge_state: BridgeState,
297}
298
299impl<B: BridgeHandler> Bridge<B> {
300 fn is_device_class_allowed_for_bridge_upstream(
301 &self,
302 port_class: fnet_interfaces_ext::PortClass,
303 ) -> bool {
304 let device_class = match port_class {
305 fnet_interfaces_ext::PortClass::Loopback
306 | fnet_interfaces_ext::PortClass::Blackhole => None,
307 fnet_interfaces_ext::PortClass::Virtual => Some(DeviceClass::Virtual),
308 fnet_interfaces_ext::PortClass::Ethernet => Some(DeviceClass::Ethernet),
309 fnet_interfaces_ext::PortClass::WlanClient => Some(DeviceClass::WlanClient),
310 fnet_interfaces_ext::PortClass::WlanAp => Some(DeviceClass::WlanAp),
311 fnet_interfaces_ext::PortClass::Ppp => Some(DeviceClass::Ppp),
312 fnet_interfaces_ext::PortClass::Bridge => Some(DeviceClass::Bridge),
313 fnet_interfaces_ext::PortClass::Lowpan => Some(DeviceClass::Lowpan),
314 };
315 device_class.is_some_and(|device_class| {
316 self.allowed_bridge_upstream_device_classes.contains(&device_class)
317 })
318 }
319
320 async fn add_guest_to_bridge(&mut self, id: u64) -> Result<(), errors::Error> {
321 info!("got a request to add interface {} to bridge", id);
322 let Self { bridge_state, bridge_handler, allowed_bridge_upstream_device_classes: _ } = self;
323 *bridge_state = match std::mem::take(bridge_state) {
324 BridgeState::Init => BridgeState::WaitingForUpstream { guests: HashSet::from([id]) },
325 BridgeState::WaitingForGuests { upstream, upstream_candidates } => {
328 let guests = HashSet::from([id]);
329 let handle = bridge_handler
330 .build_bridge(guests.iter().copied(), upstream)
331 .await
332 .context("building bridge")?;
333 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
334 }
335 BridgeState::WaitingForUpstream { mut guests } => {
339 assert!(guests.insert(id));
340 BridgeState::WaitingForUpstream { guests }
342 }
343 BridgeState::Bridged { handle, upstream, upstream_candidates, mut guests } => {
347 bridge_handler.destroy_bridge(handle).await.context("destroying bridge")?;
348 assert!(guests.insert(id));
349 let handle = bridge_handler
350 .build_bridge(guests.iter().copied(), upstream)
351 .await
352 .context("building bridge")?;
353 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
354 }
355 };
356 Ok(())
357 }
358
359 async fn remove_guest_from_bridge(&mut self, id: u64) -> Result<(), errors::Error> {
360 info!("got a request to remove interface {} from bridge", id);
361 let Self { bridge_state, bridge_handler, allowed_bridge_upstream_device_classes: _ } = self;
362 *bridge_state = match std::mem::take(bridge_state) {
363 BridgeState::Init | BridgeState::WaitingForGuests { .. } => {
364 panic!("cannot remove guest interface {} since it was not previously added", id)
365 }
366 BridgeState::WaitingForUpstream { mut guests } => {
367 assert!(guests.remove(&id));
368 if guests.is_empty() {
369 BridgeState::Init
370 } else {
371 BridgeState::WaitingForUpstream { guests }
373 }
374 }
375 BridgeState::Bridged { handle, upstream, upstream_candidates, mut guests } => {
376 bridge_handler.destroy_bridge(handle).await.context("destroying bridge")?;
377 assert!(guests.remove(&id));
378 if guests.is_empty() {
379 BridgeState::WaitingForGuests { upstream, upstream_candidates }
380 } else {
381 let handle = bridge_handler
382 .build_bridge(guests.iter().copied(), upstream)
383 .await
384 .context("building bridge")?;
385 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
386 }
387 }
388 };
389 Ok(())
390 }
391
392 async fn handle_interface_online(
393 &mut self,
394 id: u64,
395 allowed_for_bridge_upstream: bool,
396 ) -> Result<(), errors::Error> {
397 info!("interface {} (allowed for upstream: {}) is online", id, allowed_for_bridge_upstream);
398 let Self { bridge_state, bridge_handler, allowed_bridge_upstream_device_classes: _ } = self;
399 *bridge_state = match std::mem::take(bridge_state) {
400 BridgeState::Init => {
401 if allowed_for_bridge_upstream {
402 BridgeState::WaitingForGuests {
403 upstream: id,
404 upstream_candidates: Default::default(),
405 }
406 } else {
407 BridgeState::Init
408 }
409 }
410 BridgeState::WaitingForGuests { upstream, mut upstream_candidates } => {
411 if allowed_for_bridge_upstream {
412 assert_ne!(
413 upstream, id,
414 "interface {} expected to provide upstream but was offline and came online",
415 id
416 );
417 assert!(
418 upstream_candidates.insert(id),
419 "upstream candidate {} already present",
420 id
421 );
422 }
423 BridgeState::WaitingForGuests { upstream, upstream_candidates }
424 }
425 BridgeState::WaitingForUpstream { guests } => {
426 if allowed_for_bridge_upstream && !guests.contains(&id) {
427 let handle = bridge_handler
430 .build_bridge(guests.iter().copied(), id)
431 .await
432 .context("building bridge")?;
433 BridgeState::Bridged {
434 handle,
435 upstream: id,
436 upstream_candidates: Default::default(),
437 guests,
438 }
439 } else {
440 BridgeState::WaitingForUpstream { guests }
441 }
442 }
443 BridgeState::Bridged { handle, upstream, mut upstream_candidates, guests } => {
447 if id == upstream {
448 info!("upstream-providing interface {} went online", id);
449 } else if id == handle.id {
450 info!("bridge interface {} went online", handle.id);
451 } else if !guests.contains(&id) && allowed_for_bridge_upstream {
452 assert!(
453 upstream_candidates.insert(id),
454 "upstream candidate {} already present",
455 id
456 );
457 }
458 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
459 }
460 };
461 Ok(())
462 }
463
464 async fn handle_interface_offline(
465 &mut self,
466 id: u64,
467 allowed_for_bridge_upstream: bool,
468 ) -> Result<(), errors::Error> {
469 info!(
470 "interface {} (allowed for upstream: {}) is offline",
471 id, allowed_for_bridge_upstream
472 );
473 let Self { bridge_state, allowed_bridge_upstream_device_classes: _, bridge_handler: _ } =
474 self;
475 *bridge_state = match std::mem::take(bridge_state) {
476 BridgeState::Init => BridgeState::Init,
477 BridgeState::WaitingForUpstream { guests } => {
478 BridgeState::WaitingForUpstream { guests }
479 }
480 BridgeState::Bridged { handle, upstream, mut upstream_candidates, guests } => {
481 if id == handle.id {
482 warn!("bridge interface {} went offline", id);
483 } else if id == upstream {
484 warn!("upstream interface {} went offline", id);
489 } else if !guests.contains(&id) && allowed_for_bridge_upstream {
490 assert!(upstream_candidates.remove(&id), "upstream candidate {} not found", id);
491 }
492 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
493 }
494 BridgeState::WaitingForGuests { upstream, mut upstream_candidates } => {
495 if id == upstream {
496 match take_any(&mut upstream_candidates) {
497 Some(id) => {
498 BridgeState::WaitingForGuests { upstream: id, upstream_candidates }
499 }
500 None => BridgeState::Init,
501 }
502 } else {
503 if allowed_for_bridge_upstream {
504 assert!(
505 upstream_candidates.remove(&id),
506 "upstream candidate {} not found",
507 id
508 );
509 }
510 BridgeState::WaitingForGuests { upstream, upstream_candidates }
511 }
512 }
513 };
514 Ok(())
515 }
516
517 async fn handle_interface_removed(&mut self, removed_id: u64) -> Result<(), errors::Error> {
518 info!("interface {} removed", removed_id);
519 let Self { bridge_state, bridge_handler, allowed_bridge_upstream_device_classes: _ } = self;
520 *bridge_state = match std::mem::take(bridge_state) {
521 BridgeState::Init => BridgeState::Init,
522 BridgeState::WaitingForUpstream { guests } => {
523 if guests.contains(&removed_id) {
524 info!("guest interface {} removed", removed_id);
527 }
528 BridgeState::WaitingForUpstream { guests }
529 }
530 BridgeState::WaitingForGuests { upstream, mut upstream_candidates } => {
531 if upstream == removed_id {
532 match take_any(&mut upstream_candidates) {
533 Some(new_upstream_id) => BridgeState::WaitingForGuests {
534 upstream: new_upstream_id,
535 upstream_candidates,
536 },
537 None => BridgeState::Init,
538 }
539 } else {
540 let _: bool = upstream_candidates.remove(&removed_id);
541 BridgeState::WaitingForGuests { upstream, upstream_candidates }
542 }
543 }
544 BridgeState::Bridged { handle, upstream, mut upstream_candidates, guests } => {
545 if guests.contains(&removed_id) {
546 info!("guest interface {} removed", removed_id);
549 }
550 if handle.id == removed_id {
551 error!("bridge interface {} removed; rebuilding", handle.id);
554 let handle = bridge_handler
555 .build_bridge(guests.iter().copied(), upstream)
556 .await
557 .context("building bridge")?;
558 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
559 } else if upstream == removed_id {
560 bridge_handler.destroy_bridge(handle).await.context("destroying bridge")?;
561 match take_any(&mut upstream_candidates) {
562 Some(new_upstream_id) => {
563 let handle = bridge_handler
564 .build_bridge(guests.iter().copied(), new_upstream_id)
565 .await
566 .context("building bridge")?;
567 BridgeState::Bridged {
568 handle,
569 upstream: new_upstream_id,
570 upstream_candidates,
571 guests,
572 }
573 }
574 None => BridgeState::WaitingForUpstream { guests },
575 }
576 } else {
577 let _: bool = upstream_candidates.remove(&removed_id);
578 BridgeState::Bridged { handle, upstream, upstream_candidates, guests }
579 }
580 }
581 };
582 Ok(())
583 }
584}
585
586#[async_trait(?Send)]
587impl<'a, B: BridgeHandler> Handler for Virtualization<'a, B> {
588 async fn handle_event(
589 &'async_trait mut self,
590 event: Event,
591 events: &'async_trait mut futures::stream::SelectAll<EventStream>,
592 ) -> Result<(), errors::Error> {
593 match event {
594 Event::ControlRequestStream(stream) => {
595 events.push(
596 stream
597 .filter_map(|request| {
598 future::ready(match request {
599 Ok(request) => Some(Event::ControlRequest(request)),
600 Err(e) => {
601 error!("control request error: {:?}", e);
602 None
603 }
604 })
605 })
606 .boxed(),
607 );
608 }
609 Event::ControlRequest(fnet_virtualization::ControlRequest::CreateNetwork {
610 config,
611 network,
612 control_handle: _,
613 }) => {
614 let network_id = match config {
615 fnet_virtualization::Config::Bridged(fnet_virtualization::Bridged {
616 ..
617 }) => {
618 info!("got a request to create a bridged network");
619 NetworkId::Bridged
620 }
621 config => {
622 panic!("unsupported network config type {:?}", config);
623 }
624 };
625 let (close_channel_tx, close_channel_rx) = oneshot::channel();
629 let close_channel_rx = close_channel_rx.shared();
630 let stream = network
631 .into_stream()
632 .filter_map(move |request| {
633 future::ready(match request {
634 Ok(request) => {
635 Some(NetworkRequest::Request(request, close_channel_rx.clone()))
636 }
637 Err(e) => {
638 error!("network request error: {:?}", e);
639 None
640 }
641 })
642 })
643 .chain(futures::stream::once(futures::future::ready(
644 NetworkRequest::Finished(close_channel_tx),
645 )));
646 events.push(
647 stream.map(move |r| Event::NetworkRequest(network_id.clone(), r)).boxed(),
648 );
649 }
650 Event::NetworkRequest(network_id, request) => self
651 .handle_network_request(network_id, request, events)
652 .await
653 .context("handle network request")?,
654 Event::InterfaceClose(network_id, id) => {
655 match network_id {
656 NetworkId::Bridged => {
657 self.bridge
659 .remove_guest_from_bridge(id)
660 .await
661 .context("removing interface from bridge")?;
662 }
663 }
664 }
665 }
666 Ok(())
667 }
668
669 async fn handle_interface_update_result(
670 &mut self,
671 update_result: &fnet_interfaces_ext::UpdateResult<
672 '_,
673 (),
674 fnet_interfaces_ext::DefaultInterest,
675 >,
676 ) -> Result<(), errors::Error> {
677 let Self { bridge, installer: _, _allowed_upstream_device_classes } = self;
678 match update_result {
679 fnet_interfaces_ext::UpdateResult::Added { properties, state: _ }
680 | fnet_interfaces_ext::UpdateResult::Existing { properties, state: _ } => {
681 let fnet_interfaces_ext::Properties { id, online, port_class, .. } = **properties;
682 let allowed_for_bridge_upstream =
683 bridge.is_device_class_allowed_for_bridge_upstream(port_class);
684
685 if online {
686 bridge
687 .handle_interface_online(id.get(), allowed_for_bridge_upstream)
688 .await
689 .context("handle new interface online")?;
690 }
691 }
692 fnet_interfaces_ext::UpdateResult::Changed {
693 previous: fnet_interfaces::Properties { online: previously_online, .. },
694 current: current_properties,
695 state: _,
696 } => {
697 let fnet_interfaces_ext::Properties { id, online, port_class, .. } =
698 **current_properties;
699 let allowed_for_bridge_upstream =
700 bridge.is_device_class_allowed_for_bridge_upstream(port_class);
701
702 match (*previously_online, online) {
703 (Some(false), true) => {
704 bridge
705 .handle_interface_online(id.get(), allowed_for_bridge_upstream)
706 .await
707 .context("handle interface online")?;
708 }
709 (Some(true), false) => {
710 bridge
711 .handle_interface_offline(id.get(), allowed_for_bridge_upstream)
712 .await
713 .context("handle interface offline")?;
714 }
715 (Some(true), true) | (Some(false), false) => {
716 error!(
717 "interface {} changed event indicates no actual change to online ({} before and after)",
718 id, online
719 );
720 }
721 (None, true) => {}
723 (None, false) => {}
724 }
725 }
726 fnet_interfaces_ext::UpdateResult::Removed(
727 fnet_interfaces_ext::PropertiesAndState {
728 properties: fnet_interfaces_ext::Properties { id, .. },
729 state: _,
730 },
731 ) => {
732 bridge
733 .handle_interface_removed(id.get())
734 .await
735 .context("handle interface removed")?;
736 }
737 fnet_interfaces_ext::UpdateResult::NoChange => {}
738 }
739 Ok(())
740 }
741}
742
743pub(super) struct Stub;
744
745#[async_trait(?Send)]
746impl Handler for Stub {
747 async fn handle_event(
748 &'async_trait mut self,
749 event: Event,
750 _events: &'async_trait mut futures::stream::SelectAll<EventStream>,
751 ) -> Result<(), errors::Error> {
752 panic!("stub handler requested to handle a virtualization event: {:#?}", event)
753 }
754
755 async fn handle_interface_update_result(
756 &mut self,
757 _update_result: &fnet_interfaces_ext::UpdateResult<
758 '_,
759 (),
760 fnet_interfaces_ext::DefaultInterest,
761 >,
762 ) -> Result<(), errors::Error> {
763 Ok(())
764 }
765}
766
767pub(super) struct BridgeHandle {
768 id: u64,
769 control: fnet_interfaces_ext::admin::Control,
770}
771
772#[async_trait(?Send)]
777pub(super) trait BridgeHandler {
778 async fn build_bridge(
779 &self,
780 interfaces: impl Iterator<Item = u64> + 'async_trait,
781 upstream_interface: u64,
782 ) -> Result<BridgeHandle, errors::Error>;
783
784 async fn destroy_bridge(&self, handle: BridgeHandle) -> Result<(), errors::Error>;
785}
786
787pub(super) struct BridgeHandlerImpl {
788 stack: fnet_stack::StackProxy,
789}
790
791impl BridgeHandlerImpl {
792 pub fn new(stack: fnet_stack::StackProxy) -> Self {
793 Self { stack }
794 }
795
796 async fn start_dhcpv4_client(&self, bridge_id: u64) -> Result<(), errors::Error> {
798 self.stack
799 .set_dhcp_client_enabled(bridge_id, true)
800 .await
801 .unwrap_or_else(|err| exit_with_fidl_error(err))
802 .map_err(|e| anyhow!("failed to start dhcp client: {:?}", e))
803 .map_err(errors::Error::NonFatal)
804 }
805}
806
807#[async_trait(?Send)]
808impl BridgeHandler for BridgeHandlerImpl {
809 async fn build_bridge(
810 &self,
811 interfaces: impl Iterator<Item = u64> + 'async_trait,
812 upstream_interface: u64,
813 ) -> Result<BridgeHandle, errors::Error> {
814 let bridge = {
815 let interfaces: Vec<_> =
816 interfaces.chain(std::iter::once(upstream_interface)).collect();
817 info!(
818 "building bridge with upstream={}, interfaces={:?}",
819 upstream_interface, interfaces
820 );
821
822 let (control, server_end) = fnet_interfaces_ext::admin::Control::create_endpoints()
823 .context("create bridge endpoints")
824 .map_err(errors::Error::Fatal)?;
825 self.stack
826 .bridge_interfaces(&interfaces[..], server_end)
827 .context("calling bridge interfaces")
828 .map_err(errors::Error::Fatal)?;
829 let id =
830 control.get_id().await.context("get bridge id").map_err(errors::Error::Fatal)?;
831 BridgeHandle { id, control }
832 };
833
834 match self.start_dhcpv4_client(bridge.id).await {
836 Ok(()) => {}
837 Err(errors::Error::NonFatal(e)) => {
838 error!("failed to start DHCPv4 client on bridge: {}", e)
839 }
840 Err(errors::Error::Fatal(e)) => return Err(errors::Error::Fatal(e)),
841 }
842
843 let did_enable = bridge
845 .control
846 .enable()
847 .await
848 .context("call enable")
849 .map_err(errors::Error::Fatal)?
850 .map_err(|e| anyhow!("failed to enable interface: {:?}", e))
854 .map_err(errors::Error::Fatal)?;
855 assert!(
856 did_enable,
857 "the bridge should have been disabled on creation and then enabled by Control.Enable",
858 );
859 debug!("enabled bridge interface {}", bridge.id);
860 Ok(bridge)
861 }
862
863 async fn destroy_bridge(&self, handle: BridgeHandle) -> Result<(), errors::Error> {
864 let BridgeHandle { id: _, control } = handle;
865 control
866 .remove()
867 .await
868 .context("calling remove bridge")
869 .map_err(errors::Error::Fatal)?
870 .map_err(|err: fnet_interfaces_admin::ControlRemoveError| {
871 errors::Error::Fatal(anyhow::anyhow!("failed to remove bridge: {:?}", err))
872 })?;
873 let _: fnet_interfaces_ext::admin::TerminalError<_> = control.wait_termination().await;
876 Ok(())
877 }
878}
879
880#[cfg(test)]
881mod tests {
882 use futures::SinkExt as _;
883 use futures::channel::mpsc;
884 use test_case::test_case;
885
886 use super::*;
887
888 #[derive(Copy, Clone, Debug, PartialEq)]
889 enum Guest {
890 A,
891 B,
892 }
893
894 impl Guest {
895 fn id(&self) -> u64 {
896 match self {
897 Self::A => 1,
898 Self::B => 2,
899 }
900 }
901 }
902
903 #[derive(Copy, Clone, Debug, PartialEq)]
904 enum Upstream {
905 A,
906 B,
907 }
908
909 impl Upstream {
910 fn id(&self) -> u64 {
911 match self {
912 Self::A => 11,
913 Self::B => 12,
914 }
915 }
916 }
917
918 #[derive(Debug, PartialEq)]
919 enum BridgeEvent {
920 Destroyed,
921 Created { interfaces: HashSet<u64>, upstream_interface: u64 },
922 }
923
924 impl BridgeEvent {
925 fn created(interfaces: Vec<Guest>, upstream_interface: Upstream) -> Self {
926 Self::Created {
927 interfaces: interfaces.iter().map(Guest::id).collect(),
928 upstream_interface: upstream_interface.id(),
929 }
930 }
931
932 fn destroyed() -> Self {
933 Self::Destroyed
934 }
935 }
936
937 struct BridgeServer {
938 id: u64,
939 _request_stream: fnet_interfaces_admin::ControlRequestStream,
940 }
941
942 impl std::fmt::Debug for BridgeServer {
943 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
944 f.debug_struct("BridgeServer")
945 .field("id", &self.id)
946 .field("request_stream", &"_")
947 .finish()
948 }
949 }
950
951 struct BridgeHandlerTestImplInner {
952 bridge: Option<BridgeServer>,
953 events: mpsc::Sender<BridgeEvent>,
954 }
955
956 struct BridgeHandlerTestImpl {
957 inner: std::cell::RefCell<BridgeHandlerTestImplInner>,
958 }
959
960 impl BridgeHandlerTestImpl {
961 fn new(events: mpsc::Sender<BridgeEvent>) -> Self {
962 BridgeHandlerTestImpl {
963 inner: std::cell::RefCell::new(BridgeHandlerTestImplInner { bridge: None, events }),
964 }
965 }
966 }
967
968 #[async_trait(?Send)]
969 impl BridgeHandler for BridgeHandlerTestImpl {
970 async fn destroy_bridge(
971 &self,
972 BridgeHandle { id, control: _ }: BridgeHandle,
973 ) -> Result<(), errors::Error> {
974 let BridgeHandlerTestImplInner { bridge, events } = &mut *self.inner.borrow_mut();
975 let bridge = bridge.take();
976 assert_eq!(
977 bridge.map(|BridgeServer { id, _request_stream: _ }| id),
978 Some(id),
979 "cannot destroy a non-existent bridge"
980 );
981 events.send(BridgeEvent::Destroyed).await.expect("send event");
982 Ok(())
983 }
984
985 async fn build_bridge(
986 &self,
987 interfaces: impl Iterator<Item = u64> + 'async_trait,
988 upstream_interface: u64,
989 ) -> Result<BridgeHandle, errors::Error> {
990 let BridgeHandlerTestImplInner { bridge, events } = &mut *self.inner.borrow_mut();
991 assert_matches::assert_matches!(
992 *bridge,
993 None,
994 "cannot create a bridge since there is already an existing bridge",
995 );
996 const BRIDGE_IF: u64 = 99;
997 let (control, server) =
998 fnet_interfaces_ext::admin::Control::create_endpoints().expect("create endpoints");
999 *bridge = Some(BridgeServer { id: BRIDGE_IF, _request_stream: server.into_stream() });
1000 events
1001 .send(BridgeEvent::Created { interfaces: interfaces.collect(), upstream_interface })
1002 .await
1003 .expect("send event");
1004 Ok(BridgeHandle { id: BRIDGE_IF, control })
1005 }
1006 }
1007
1008 enum Action {
1009 AddGuest(Guest),
1010 RemoveGuest(Guest),
1011 UpstreamOnline(Upstream),
1012 UpstreamOffline(Upstream),
1013 RemoveUpstream(Upstream),
1014 }
1015
1016 #[test_case(
1017 [
1020 (Action::UpstreamOnline(Upstream::A), vec![]),
1021 (
1022 Action::AddGuest(Guest::A),
1023 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1024 ),
1025 ];
1026 "wait for guest"
1027 )]
1028 #[test_case(
1029 [
1032 (Action::AddGuest(Guest::A), vec![]),
1033 (
1034 Action::UpstreamOnline(Upstream::A),
1035 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1036 ),
1037 ];
1038 "wait for upstream"
1039 )]
1040 #[test_case(
1041 [
1044 (Action::UpstreamOnline(Upstream::A), vec![]),
1045 (
1046 Action::AddGuest(Guest::A),
1047 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1048 ),
1049 (Action::RemoveUpstream(Upstream::A), vec![BridgeEvent::destroyed()]),
1050 ];
1051 "destroy bridge when no upstream"
1052 )]
1053 #[test_case(
1054 [
1058 (Action::UpstreamOnline(Upstream::A), vec![]),
1059 (
1060 Action::AddGuest(Guest::A),
1061 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1062 ),
1063 (
1064 Action::AddGuest(Guest::B),
1065 vec![
1066 BridgeEvent::destroyed(),
1067 BridgeEvent::created([Guest::A, Guest::B].into(), Upstream::A),
1068 ],
1069 ),
1070 (
1071 Action::RemoveGuest(Guest::B),
1072 vec![
1073 BridgeEvent::destroyed(),
1074 BridgeEvent::created([Guest::A].into(), Upstream::A),
1075 ],
1076 ),
1077 (
1078 Action::RemoveGuest(Guest::A),
1079 vec![BridgeEvent::destroyed()],
1080 ),
1081 ];
1082 "multiple interfaces"
1083 )]
1084 #[test_case(
1085 [
1089 (Action::UpstreamOnline(Upstream::A), vec![]),
1090 (
1091 Action::AddGuest(Guest::A),
1092 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1093 ),
1094 (Action::RemoveGuest(Guest::A), vec![BridgeEvent::destroyed()]),
1095 (
1096 Action::AddGuest(Guest::A),
1097 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1098 ),
1099 ];
1100 "remember upstream"
1101 )]
1102 #[test_case(
1103 [
1107 (Action::UpstreamOnline(Upstream::A), vec![]),
1108 (
1109 Action::AddGuest(Guest::A),
1110 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1111 ),
1112 (Action::RemoveUpstream(Upstream::A), vec![BridgeEvent::destroyed()]),
1113 (Action::AddGuest(Guest::B), vec![]),
1114 (Action::RemoveGuest(Guest::A), vec![]),
1115 (
1116 Action::UpstreamOnline(Upstream::A),
1117 vec![BridgeEvent::created([Guest::B].into(), Upstream::A)],
1118 ),
1119 ];
1120 "remember guest interfaces"
1121 )]
1122 #[test_case(
1123 [
1125 (Action::UpstreamOnline(Upstream::A), vec![]),
1126 (
1127 Action::AddGuest(Guest::A),
1128 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1129 ),
1130 (Action::RemoveUpstream(Upstream::A), vec![BridgeEvent::destroyed()]),
1131 ];
1132 "remove upstream"
1133 )]
1134 #[test_case(
1135 [
1138 (Action::UpstreamOnline(Upstream::A), vec![]),
1139 (
1140 Action::AddGuest(Guest::A),
1141 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1142 ),
1143 (Action::UpstreamOffline(Upstream::A), vec![]),
1144 ];
1145 "upstream offline not removed"
1146 )]
1147 #[test_case(
1148 [
1151 (Action::UpstreamOnline(Upstream::A), vec![]),
1152 (Action::UpstreamOffline(Upstream::A), vec![]),
1153 (Action::AddGuest(Guest::A), vec![]),
1154 (
1155 Action::UpstreamOnline(Upstream::A),
1156 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1157 ),
1158 ];
1159 "do not bridge with offline upstream"
1160 )]
1161 #[test_case(
1162 [
1165 (Action::UpstreamOnline(Upstream::A), vec![]),
1166 (
1167 Action::AddGuest(Guest::A),
1168 vec![BridgeEvent::created([Guest::A].into(), Upstream::A)],
1169 ),
1170 (Action::UpstreamOnline(Upstream::B), vec![]),
1171 (
1172 Action::RemoveUpstream(Upstream::A),
1173 vec![
1174 BridgeEvent::destroyed(),
1175 BridgeEvent::created([Guest::A].into(), Upstream::B),
1176 ],
1177 ),
1178 ];
1179 "replace upstream"
1180 )]
1181 #[test_case(
1182 [
1185 (Action::UpstreamOnline(Upstream::A), vec![]),
1186 (Action::UpstreamOnline(Upstream::B), vec![]),
1187 (Action::UpstreamOffline(Upstream::A), vec![]),
1188 (
1189 Action::AddGuest(Guest::A),
1190 vec![BridgeEvent::created([Guest::A].into(), Upstream::B)],
1191 ),
1192 ];
1193 "replace upstream with no guests"
1194 )]
1195 #[fuchsia::test]
1196 async fn bridge(steps: impl IntoIterator<Item = (Action, Vec<BridgeEvent>)>) {
1197 let (events_tx, mut events_rx) = mpsc::channel(2);
1200 let mut bridge = Bridge {
1201 allowed_bridge_upstream_device_classes: Default::default(),
1202 bridge_handler: BridgeHandlerTestImpl::new(events_tx),
1203 bridge_state: Default::default(),
1204 };
1205
1206 for (action, expected_events) in steps {
1207 match action {
1208 Action::AddGuest(guest) => {
1209 bridge.add_guest_to_bridge(guest.id()).await.expect("add guest to bridge");
1210 }
1211 Action::RemoveGuest(guest) => {
1212 bridge
1213 .remove_guest_from_bridge(guest.id())
1214 .await
1215 .expect("remove guest from bridge");
1216 }
1217 Action::UpstreamOnline(upstream) => {
1218 bridge
1219 .handle_interface_online(upstream.id(), true)
1220 .await
1221 .expect("upstream interface online");
1222 }
1223 Action::UpstreamOffline(upstream) => {
1224 bridge
1225 .handle_interface_offline(upstream.id(), true)
1226 .await
1227 .expect("upstream interface offline");
1228 }
1229 Action::RemoveUpstream(upstream) => {
1230 bridge
1231 .handle_interface_removed(upstream.id())
1232 .await
1233 .expect("upstream interface removed");
1234 }
1235 }
1236 for event in expected_events {
1237 assert_eq!(events_rx.next().await.expect("receive event"), event);
1238 }
1239 let _: mpsc::TryRecvError =
1240 events_rx.try_next().expect_err("got unexpected bridge event");
1241 }
1242 }
1243}