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