Skip to main content

netcfg/
virtualization.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// Network identifier.
27#[derive(Debug, Clone)]
28pub(super) enum NetworkId {
29    // TODO(https://fxbug.dev/42167037): Implement isolation between bridged
30    // networks and change this to represent more than a single bridged
31    // network.
32    /// The unique bridged network consisting of an upstream-providing
33    /// interface and all guest interfaces.
34    Bridged,
35}
36
37/// Wrapper around a [`fnet_virtualization::NetworkRequest`] that allows for the
38/// server to notify all existing interfaces to shut down when the client closes
39/// the `Network` channel.
40#[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
57// TODO(https://fxbug.dev/42169203): `guests` must always be non-empty. Explore using a non-empty set
58// type to encode this invariant in the type system.
59enum BridgeState {
60    Init,
61    WaitingForGuests {
62        // Invariants: `upstream` is not present in `upstream_candidates`, and
63        // must be online.
64        upstream: u64,
65        upstream_candidates: HashSet<u64>,
66    },
67    WaitingForUpstream {
68        guests: HashSet<u64>,
69    },
70    Bridged {
71        handle: BridgeHandle,
72        // Invariant: `upstream` is not present in `upstream_candidates`.
73        //
74        // Note that `upstream` going offline does not cause a state transition unlike
75        // `WaitingForGuests`, and thus `upstream` may be offline.
76        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
106// TODO(https://github.com/rust-lang/rust/issues/59618): Implement using
107// `drain_filter` to avoid the Copy trait bound.
108// Takes a single element from `set` if `set` is non-empty.
109fn 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    // TODO(https://fxbug.dev/42052026): Use this field as the allowed upstream
121    // device classes when NAT is supported.
122    _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                // TODO(https://fxbug.dev/42168197): send a terminal event on the channel if
157                // the device could not be added to the network due to incompatibility; for
158                // example, if the network is bridged and the device does not support the
159                // same L2 protocol as other devices on the bridge.
160
161                // Get the device this port belongs to, and install it on the netstack.
162                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                // Create an interface on the device, and enable it.
176                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                        // Add this interface to the existing bridge, or create one if none exists.
215                        bridge
216                            .add_guest_to_bridge(id)
217                            .await
218                            .context("adding interface to bridge")?;
219                    }
220                }
221
222                // Wait for a signal that this interface should be removed from the bridge
223                // and the virtual network.
224                let shutdown_fut = async move {
225                    let mut interface_closure = interface
226                        .into_stream()
227                        .map(|request| {
228                            // `fuchsia.net.virtualization/Interface` is a protocol with no
229                            // methods, so `InterfaceRequest` is an uninstantiable enum.
230                            // This prevents us from exhaustively matching on its variants,
231                            // so we just drop the request here.
232                            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                        // The interface channel has been closed by the client.
239                        result = interface_closure => {
240                            format!("interface channel closed by client: {:?}", result)
241                        },
242                        // The device has been detached from the netstack.
243                        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                        // The virtual network has been shut down and is notifying us to
255                        // remove the interface.
256                        result = network_close_rx => {
257                            result.expect("sender should not be dropped");
258                            "network has been shut down".to_string()
259                        },
260                        // A terminal event was sent on the interface control channel,
261                        // signaling that the interface was removed.
262                        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                // Close down the network.
281                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            // If a bridge doesn't exist, but we have an interface with upstream connectivity,
326            // create the bridge.
327            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            // If a bridge doesn't exist, and we don't yet have an interface with upstream
336            // connectivity, just keep track of the interface to be bridged, so we can eventually
337            // include it in the bridge.
338            BridgeState::WaitingForUpstream { mut guests } => {
339                assert!(guests.insert(id));
340                // No change to bridge state.
341                BridgeState::WaitingForUpstream { guests }
342            }
343            // If a bridge already exists, tear it down and create a new one, re-using the interface
344            // that has upstream connectivity and including all the interfaces that were bridged
345            // previously.
346            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                    // No change to bridge state.
372                    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                    // We don't already have an upstream interface that provides connectivity. Build
428                    // a bridge with this one.
429                    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            // If a bridge already exists, tear it down and create a new one, using this new
444            // interface to provide upstream connectivity, and including all the interfaces that
445            // were bridged previously.
446            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                    // We currently ignore the situation where an interface that is providing
485                    // upstream connectivity changes from online to offline. The only signal
486                    // that causes us to destroy an existing bridge is if the interface providing
487                    // upstream connectivity is removed entirely.
488                    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                    // Removal from the `guests` map will occur when the guest removal is
525                    // actually handled in `remove_guest_from_bridge`.
526                    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                    // Removal from the `guests` map will occur when the guest removal is
547                    // actually handled in `remove_guest_from_bridge`.
548                    info!("guest interface {} removed", removed_id);
549                }
550                if handle.id == removed_id {
551                    // The bridge interface installed by netcfg should not be removed by any other
552                    // entity.
553                    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                // Create a oneshot channel we can use when the `Network` channel is closed,
626                // to notify each interface task to close its corresponding `Interface` channel
627                // as well.
628                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                        // Remove this interface from the existing bridge.
658                        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                    // Online did not change; do nothing.
722                    (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/// An abstraction over the logic involved to instruct the netstack to create or destroy a bridge.
773///
774/// Allows for testing the virtualization handler by providing an instrumented implementation of
775/// `BridgeHandler` in order to observe its behavior.
776#[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    // Starts a DHCPv4 client.
797    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        // Start a DHCPv4 client.
835        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        // Enable the bridge we just created.
844        let did_enable = bridge
845            .control
846            .enable()
847            .await
848            .context("call enable")
849            .map_err(errors::Error::Fatal)?
850            // If we created a bridge but the interface wasn't successfully enabled, the bridging
851            // state machine has become inconsistent with the netstack, so we return an
852            // unrecoverable error.
853            .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        // We don't really care the reason the stack gives us here, only that
874        // the termination completes.
875        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        // Verify that we wait to create a bridge until an interface is added to a virtual bridged
1018        // network.
1019        [
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        // Verify that we wait to create a bridge until there is an interface that provides upstream
1030        // connectivity.
1031        [
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        // Verify that the bridge is destroyed when the upstream interface is removed and there are
1042        // no more candidates to provide upstream connectivity.
1043        [
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        // Verify that when we add multiple interfaces to the virtual network, they are added to the
1055        // bridge one by one, which is implemented by tearing down the bridge and rebuilding it
1056        // every time an interface is added or removed.
1057        [
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        // Verify that even if all guests on a network are removed and the bridge is therefore
1086        // destroyed, if an interface is added again, the bridge is re-created with the same
1087        // upstream.
1088        [
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        // Verify that the handler keeps track of which interfaces are added and removed to the
1104        // bridged network even when there is no existing bridge due to a lack of upstream
1105        // connectivity.
1106        [
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        // Verify that the bridge is destroyed when upstream is removed.
1124        [
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        // Verify that the upstream-providing interface going offline while a
1136        // bridge is present does not cause the bridge to be destroyed.
1137        [
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        // Verify that an otherwise eligible but offline upstream interface is
1149        // not used to create a bridge.
1150        [
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        // Verify that when we replace the interface providing upstream connectivity with another
1163        // interface, the bridge is correctly destroyed and recreated with the new upstream.
1164        [
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        // Verify that upstream-providing interface changes are tracked even when there are no
1183        // guests yet.
1184        [
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        // At most 2 events will need to be sent before the test can process them: in the case that
1198        // a bridge is modified, the bridge is destroyed and then built again.
1199        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}