use std::fmt::Debug;
use std::ops::RangeInclusive;
use async_utils::{fold, stream};
use fidl::endpoints::{DiscoverableProtocolMarker, ProtocolMarker, Proxy as _};
use fidl_fuchsia_net_ext::{IntoExt as _, TryIntoExt as _};
use futures::future::Either;
use futures::{Stream, TryStreamExt as _};
use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
use thiserror::Error;
use {
fidl_fuchsia_net as fnet, fidl_fuchsia_net_routes as fnet_routes,
fidl_fuchsia_net_routes_admin as fnet_routes_admin,
};
use crate::{impl_responder, FidlRouteIpExt, Responder, SliceResponder, WatcherCreationError};
pub trait FidlRuleIpExt: Ip {
type RuleWatcherMarker: ProtocolMarker<RequestStream = Self::RuleWatcherRequestStream>;
type RuleWatcherRequestStream: fidl::endpoints::RequestStream<Ok: Send, ControlHandle: Send>;
type RuleEvent: From<RuleEvent<Self>>
+ TryInto<RuleEvent<Self>, Error = RuleFidlConversionError>
+ Unpin;
type RuleWatcherWatchResponder: SliceResponder<Self::RuleEvent>;
fn into_rule_watcher_request(
request: fidl::endpoints::Request<Self::RuleWatcherMarker>,
) -> RuleWatcherRequest<Self>;
}
impl_responder!(fnet_routes::RuleWatcherV4WatchResponder, &[fnet_routes::RuleEventV4]);
impl_responder!(fnet_routes::RuleWatcherV6WatchResponder, &[fnet_routes::RuleEventV6]);
impl FidlRuleIpExt for Ipv4 {
type RuleWatcherMarker = fnet_routes::RuleWatcherV4Marker;
type RuleWatcherRequestStream = fnet_routes::RuleWatcherV4RequestStream;
type RuleEvent = fnet_routes::RuleEventV4;
type RuleWatcherWatchResponder = fnet_routes::RuleWatcherV4WatchResponder;
fn into_rule_watcher_request(
request: fidl::endpoints::Request<Self::RuleWatcherMarker>,
) -> RuleWatcherRequest<Self> {
RuleWatcherRequest::from(request)
}
}
impl FidlRuleIpExt for Ipv6 {
type RuleWatcherMarker = fnet_routes::RuleWatcherV6Marker;
type RuleWatcherRequestStream = fnet_routes::RuleWatcherV6RequestStream;
type RuleEvent = fnet_routes::RuleEventV6;
type RuleWatcherWatchResponder = fnet_routes::RuleWatcherV6WatchResponder;
fn into_rule_watcher_request(
request: fidl::endpoints::Request<Self::RuleWatcherMarker>,
) -> RuleWatcherRequest<Self> {
RuleWatcherRequest::from(request)
}
}
pub enum RuleWatcherRequest<I: FidlRuleIpExt> {
Watch {
responder: I::RuleWatcherWatchResponder,
},
}
impl From<fnet_routes::RuleWatcherV4Request> for RuleWatcherRequest<Ipv4> {
fn from(req: fnet_routes::RuleWatcherV4Request) -> Self {
match req {
fnet_routes::RuleWatcherV4Request::Watch { responder } => {
RuleWatcherRequest::Watch { responder }
}
}
}
}
impl From<fnet_routes::RuleWatcherV6Request> for RuleWatcherRequest<Ipv6> {
fn from(req: fnet_routes::RuleWatcherV6Request) -> Self {
match req {
fnet_routes::RuleWatcherV6Request::Watch { responder } => {
RuleWatcherRequest::Watch { responder }
}
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct InstalledRule<I: Ip> {
pub priority: RuleSetPriority,
pub index: RuleIndex,
pub matcher: RuleMatcher<I>,
pub action: RuleAction,
}
impl TryFrom<fnet_routes::InstalledRuleV4> for InstalledRule<Ipv4> {
type Error = RuleFidlConversionError;
fn try_from(
fnet_routes::InstalledRuleV4 {
rule_set_priority,
rule_index,
matcher,
action,
}: fnet_routes::InstalledRuleV4,
) -> Result<Self, Self::Error> {
Ok(Self {
priority: rule_set_priority.into(),
index: rule_index.into(),
matcher: matcher.try_into()?,
action: action.into(),
})
}
}
impl TryFrom<fnet_routes::InstalledRuleV6> for InstalledRule<Ipv6> {
type Error = RuleFidlConversionError;
fn try_from(
fnet_routes::InstalledRuleV6 {
rule_set_priority,
rule_index,
matcher,
action,
}: fnet_routes::InstalledRuleV6,
) -> Result<Self, Self::Error> {
Ok(Self {
priority: rule_set_priority.into(),
index: rule_index.into(),
matcher: matcher.try_into()?,
action: action.into(),
})
}
}
impl From<InstalledRule<Ipv4>> for fnet_routes::InstalledRuleV4 {
fn from(InstalledRule { priority, index, matcher, action }: InstalledRule<Ipv4>) -> Self {
Self {
rule_set_priority: priority.into(),
rule_index: index.into(),
matcher: matcher.into(),
action: action.into(),
}
}
}
impl From<InstalledRule<Ipv6>> for fnet_routes::InstalledRuleV6 {
fn from(InstalledRule { priority, index, matcher, action }: InstalledRule<Ipv6>) -> Self {
Self {
rule_set_priority: priority.into(),
rule_index: index.into(),
matcher: matcher.into(),
action: action.into(),
}
}
}
#[derive(Debug, Clone)]
pub enum RuleEvent<I: Ip> {
Existing(InstalledRule<I>),
Idle,
Added(InstalledRule<I>),
Removed(InstalledRule<I>),
}
impl TryFrom<fnet_routes::RuleEventV4> for RuleEvent<Ipv4> {
type Error = RuleFidlConversionError;
fn try_from(event: fnet_routes::RuleEventV4) -> Result<Self, Self::Error> {
match event {
fnet_routes::RuleEventV4::Existing(rule) => Ok(RuleEvent::Existing(rule.try_into()?)),
fnet_routes::RuleEventV4::Idle(fnet_routes::Empty) => Ok(RuleEvent::Idle),
fnet_routes::RuleEventV4::Added(rule) => Ok(RuleEvent::Added(rule.try_into()?)),
fnet_routes::RuleEventV4::Removed(rule) => Ok(RuleEvent::Removed(rule.try_into()?)),
fnet_routes::RuleEventV4::__SourceBreaking { unknown_ordinal } => {
Err(RuleFidlConversionError::UnknownOrdinal {
name: "RuleEventV4",
unknown_ordinal,
})
}
}
}
}
impl TryFrom<fnet_routes::RuleEventV6> for RuleEvent<Ipv6> {
type Error = RuleFidlConversionError;
fn try_from(event: fnet_routes::RuleEventV6) -> Result<Self, Self::Error> {
match event {
fnet_routes::RuleEventV6::Existing(rule) => Ok(RuleEvent::Existing(rule.try_into()?)),
fnet_routes::RuleEventV6::Idle(fnet_routes::Empty) => Ok(RuleEvent::Idle),
fnet_routes::RuleEventV6::Added(rule) => Ok(RuleEvent::Added(rule.try_into()?)),
fnet_routes::RuleEventV6::Removed(rule) => Ok(RuleEvent::Removed(rule.try_into()?)),
fnet_routes::RuleEventV6::__SourceBreaking { unknown_ordinal } => {
Err(RuleFidlConversionError::UnknownOrdinal {
name: "RuleEventV6",
unknown_ordinal,
})
}
}
}
}
impl From<RuleEvent<Ipv4>> for fnet_routes::RuleEventV4 {
fn from(event: RuleEvent<Ipv4>) -> Self {
match event {
RuleEvent::Existing(r) => Self::Existing(r.into()),
RuleEvent::Idle => Self::Idle(fnet_routes::Empty),
RuleEvent::Added(r) => Self::Added(r.into()),
RuleEvent::Removed(r) => Self::Removed(r.into()),
}
}
}
impl From<RuleEvent<Ipv6>> for fnet_routes::RuleEventV6 {
fn from(event: RuleEvent<Ipv6>) -> Self {
match event {
RuleEvent::Existing(r) => Self::Existing(r.into()),
RuleEvent::Idle => Self::Idle(fnet_routes::Empty),
RuleEvent::Added(r) => Self::Added(r.into()),
RuleEvent::Removed(r) => Self::Removed(r.into()),
}
}
}
pub trait FidlRuleAdminIpExt: Ip {
type RuleTableMarker: DiscoverableProtocolMarker<RequestStream = Self::RuleTableRequestStream>;
type RuleSetMarker: ProtocolMarker<RequestStream = Self::RuleSetRequestStream>;
type RuleTableRequestStream: fidl::endpoints::RequestStream<Ok: Send, ControlHandle: Send>;
type RuleSetRequestStream: fidl::endpoints::RequestStream<Ok: Send, ControlHandle: Send>;
type RuleSetAddRuleResponder: Responder<
Payload = Result<(), fnet_routes_admin::RuleSetError>,
ControlHandle = Self::RuleSetControlHandle,
>;
type RuleSetRemoveRuleResponder: Responder<
Payload = Result<(), fnet_routes_admin::RuleSetError>,
ControlHandle = Self::RuleSetControlHandle,
>;
type RuleSetAuthenticateForRouteTableResponder: Responder<
Payload = Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
ControlHandle = Self::RuleSetControlHandle,
>;
type RuleTableControlHandle: fidl::endpoints::ControlHandle + Send + Clone;
type RuleSetControlHandle: fidl::endpoints::ControlHandle + Send + Clone;
fn into_rule_set_request(
request: fidl::endpoints::Request<Self::RuleSetMarker>,
) -> RuleSetRequest<Self>;
fn into_rule_table_request(
request: fidl::endpoints::Request<Self::RuleTableMarker>,
) -> RuleTableRequest<Self>;
}
impl FidlRuleAdminIpExt for Ipv4 {
type RuleTableMarker = fnet_routes_admin::RuleTableV4Marker;
type RuleSetMarker = fnet_routes_admin::RuleSetV4Marker;
type RuleTableRequestStream = fnet_routes_admin::RuleTableV4RequestStream;
type RuleSetRequestStream = fnet_routes_admin::RuleSetV4RequestStream;
type RuleSetAddRuleResponder = fnet_routes_admin::RuleSetV4AddRuleResponder;
type RuleSetRemoveRuleResponder = fnet_routes_admin::RuleSetV4RemoveRuleResponder;
type RuleSetAuthenticateForRouteTableResponder =
fnet_routes_admin::RuleSetV4AuthenticateForRouteTableResponder;
type RuleTableControlHandle = fnet_routes_admin::RuleTableV4ControlHandle;
type RuleSetControlHandle = fnet_routes_admin::RuleSetV4ControlHandle;
fn into_rule_set_request(
request: fidl::endpoints::Request<Self::RuleSetMarker>,
) -> RuleSetRequest<Self> {
RuleSetRequest::from(request)
}
fn into_rule_table_request(
request: fidl::endpoints::Request<Self::RuleTableMarker>,
) -> RuleTableRequest<Self> {
RuleTableRequest::from(request)
}
}
impl FidlRuleAdminIpExt for Ipv6 {
type RuleTableMarker = fnet_routes_admin::RuleTableV6Marker;
type RuleSetMarker = fnet_routes_admin::RuleSetV6Marker;
type RuleTableRequestStream = fnet_routes_admin::RuleTableV6RequestStream;
type RuleSetRequestStream = fnet_routes_admin::RuleSetV6RequestStream;
type RuleSetAddRuleResponder = fnet_routes_admin::RuleSetV6AddRuleResponder;
type RuleSetRemoveRuleResponder = fnet_routes_admin::RuleSetV6RemoveRuleResponder;
type RuleSetAuthenticateForRouteTableResponder =
fnet_routes_admin::RuleSetV6AuthenticateForRouteTableResponder;
type RuleTableControlHandle = fnet_routes_admin::RuleTableV6ControlHandle;
type RuleSetControlHandle = fnet_routes_admin::RuleSetV6ControlHandle;
fn into_rule_set_request(
request: fidl::endpoints::Request<Self::RuleSetMarker>,
) -> RuleSetRequest<Self> {
RuleSetRequest::from(request)
}
fn into_rule_table_request(
request: fidl::endpoints::Request<Self::RuleTableMarker>,
) -> RuleTableRequest<Self> {
RuleTableRequest::from(request)
}
}
impl_responder!(
fnet_routes_admin::RuleSetV4AddRuleResponder,
Result<(), fnet_routes_admin::RuleSetError>,
);
impl_responder!(
fnet_routes_admin::RuleSetV4RemoveRuleResponder,
Result<(), fnet_routes_admin::RuleSetError>,
);
impl_responder!(
fnet_routes_admin::RuleSetV4AuthenticateForRouteTableResponder,
Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
);
impl_responder!(
fnet_routes_admin::RuleSetV6AddRuleResponder,
Result<(), fnet_routes_admin::RuleSetError>,
);
impl_responder!(
fnet_routes_admin::RuleSetV6RemoveRuleResponder,
Result<(), fnet_routes_admin::RuleSetError>,
);
impl_responder!(
fnet_routes_admin::RuleSetV6AuthenticateForRouteTableResponder,
Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
);
#[derive(Debug, Error, Clone, Copy, PartialEq)]
pub enum RuleFidlConversionError {
#[error("BaseMatcher is missing from the RuleMatcher")]
BaseMatcherMissing,
#[error("failed to convert `destination` to net_types subnet: {0:?}")]
DestinationSubnet(net_types::ip::SubnetError),
#[error("unexpected union variant for {name}, got ordinal = ({unknown_ordinal})")]
#[allow(missing_docs)]
UnknownOrdinal { name: &'static str, unknown_ordinal: u64 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleSetPriority(u32);
pub const DEFAULT_RULE_SET_PRIORITY: RuleSetPriority =
RuleSetPriority(fnet_routes::DEFAULT_RULE_SET_PRIORITY);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleIndex(u32);
impl RuleIndex {
pub const fn new(x: u32) -> Self {
Self(x)
}
}
impl From<RuleSetPriority> for u32 {
fn from(RuleSetPriority(x): RuleSetPriority) -> Self {
x
}
}
impl From<u32> for RuleSetPriority {
fn from(x: u32) -> Self {
Self(x)
}
}
impl From<RuleIndex> for u32 {
fn from(RuleIndex(x): RuleIndex) -> Self {
x
}
}
impl From<u32> for RuleIndex {
fn from(x: u32) -> Self {
Self(x)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InterfaceMatcher {
DeviceName(String),
}
impl TryFrom<fnet_routes::InterfaceMatcher> for InterfaceMatcher {
type Error = RuleFidlConversionError;
fn try_from(matcher: fnet_routes::InterfaceMatcher) -> Result<Self, Self::Error> {
match matcher {
fnet_routes::InterfaceMatcher::DeviceName(name) => Ok(Self::DeviceName(name)),
fnet_routes::InterfaceMatcher::__SourceBreaking { unknown_ordinal } => {
Err(RuleFidlConversionError::UnknownOrdinal {
name: "InterfaceMatcher",
unknown_ordinal,
})
}
}
}
}
impl From<InterfaceMatcher> for fnet_routes::InterfaceMatcher {
fn from(matcher: InterfaceMatcher) -> Self {
match matcher {
InterfaceMatcher::DeviceName(name) => fnet_routes::InterfaceMatcher::DeviceName(name),
}
}
}
#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)]
pub struct RuleMatcher<I: Ip> {
pub from: Option<Subnet<I::Addr>>,
pub locally_generated: Option<bool>,
pub bound_device: Option<InterfaceMatcher>,
pub mark_1: Option<MarkMatcher>,
pub mark_2: Option<MarkMatcher>,
}
impl TryFrom<fnet_routes::RuleMatcherV4> for RuleMatcher<Ipv4> {
type Error = RuleFidlConversionError;
fn try_from(
fnet_routes::RuleMatcherV4 {
from,
base,
__source_breaking: fidl::marker::SourceBreaking,
}: fnet_routes::RuleMatcherV4,
) -> Result<Self, Self::Error> {
let fnet_routes::BaseMatcher {
locally_generated,
bound_device,
mark_1,
mark_2,
__source_breaking: fidl::marker::SourceBreaking,
} = base.ok_or(RuleFidlConversionError::BaseMatcherMissing)?;
Ok(Self {
from: from
.map(|from| from.try_into_ext().map_err(RuleFidlConversionError::DestinationSubnet))
.transpose()?,
locally_generated,
bound_device: bound_device.map(InterfaceMatcher::try_from).transpose()?,
mark_1: mark_1.map(MarkMatcher::try_from).transpose()?,
mark_2: mark_2.map(MarkMatcher::try_from).transpose()?,
})
}
}
impl From<RuleMatcher<Ipv4>> for fnet_routes::RuleMatcherV4 {
fn from(
RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv4>,
) -> Self {
fnet_routes::RuleMatcherV4 {
from: from.map(|from| fnet::Ipv4AddressWithPrefix {
addr: from.network().into_ext(),
prefix_len: from.prefix(),
}),
base: Some(fnet_routes::BaseMatcher {
locally_generated,
bound_device: bound_device.map(fnet_routes::InterfaceMatcher::from),
mark_1: mark_1.map(Into::into),
mark_2: mark_2.map(Into::into),
__source_breaking: fidl::marker::SourceBreaking,
}),
__source_breaking: fidl::marker::SourceBreaking,
}
}
}
impl TryFrom<fnet_routes::RuleMatcherV6> for RuleMatcher<Ipv6> {
type Error = RuleFidlConversionError;
fn try_from(
fnet_routes::RuleMatcherV6 {
from,
base,
__source_breaking: fidl::marker::SourceBreaking,
}: fnet_routes::RuleMatcherV6,
) -> Result<Self, Self::Error> {
let fnet_routes::BaseMatcher {
locally_generated,
bound_device,
mark_1,
mark_2,
__source_breaking: fidl::marker::SourceBreaking,
} = base.ok_or(RuleFidlConversionError::BaseMatcherMissing)?;
Ok(Self {
from: from
.map(|from| from.try_into_ext().map_err(RuleFidlConversionError::DestinationSubnet))
.transpose()?,
locally_generated,
bound_device: bound_device.map(InterfaceMatcher::try_from).transpose()?,
mark_1: mark_1.map(MarkMatcher::try_from).transpose()?,
mark_2: mark_2.map(MarkMatcher::try_from).transpose()?,
})
}
}
impl From<RuleMatcher<Ipv6>> for fnet_routes::RuleMatcherV6 {
fn from(
RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv6>,
) -> Self {
fnet_routes::RuleMatcherV6 {
from: from.map(|from| fnet::Ipv6AddressWithPrefix {
addr: from.network().into_ext(),
prefix_len: from.prefix(),
}),
base: Some(fnet_routes::BaseMatcher {
locally_generated,
bound_device: bound_device.map(fnet_routes::InterfaceMatcher::from),
mark_1: mark_1.map(Into::into),
mark_2: mark_2.map(Into::into),
__source_breaking: fidl::marker::SourceBreaking,
}),
__source_breaking: fidl::marker::SourceBreaking,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum MarkMatcher {
Unmarked,
Marked {
mask: u32,
between: RangeInclusive<u32>,
},
}
impl TryFrom<fnet_routes::MarkMatcher> for MarkMatcher {
type Error = RuleFidlConversionError;
fn try_from(sel: fnet_routes::MarkMatcher) -> Result<Self, Self::Error> {
match sel {
fnet_routes::MarkMatcher::Unmarked(fnet_routes::Unmarked) => Ok(MarkMatcher::Unmarked),
fnet_routes::MarkMatcher::Marked(fnet_routes::Marked {
mask,
between: fnet_routes::Between { start, end },
}) => Ok(MarkMatcher::Marked { mask, between: RangeInclusive::new(start, end) }),
fnet_routes::MarkMatcher::__SourceBreaking { unknown_ordinal } => {
Err(RuleFidlConversionError::UnknownOrdinal {
name: "MarkMatcher",
unknown_ordinal,
})
}
}
}
}
impl From<MarkMatcher> for fnet_routes::MarkMatcher {
fn from(sel: MarkMatcher) -> Self {
match sel {
MarkMatcher::Unmarked => fnet_routes::MarkMatcher::Unmarked(fnet_routes::Unmarked),
MarkMatcher::Marked { mask, between } => {
let (start, end) = between.into_inner();
fnet_routes::MarkMatcher::Marked(fnet_routes::Marked {
mask,
between: fnet_routes::Between { start, end },
})
}
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum RuleAction {
Unreachable,
Lookup(u32),
}
impl From<fnet_routes::RuleAction> for RuleAction {
fn from(action: fnet_routes::RuleAction) -> Self {
match action {
fnet_routes::RuleAction::Lookup(table_id) => RuleAction::Lookup(table_id),
fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable) => {
RuleAction::Unreachable
}
fnet_routes::RuleAction::__SourceBreaking { unknown_ordinal } => {
panic!("unexpected mark matcher variant, unknown ordinal: {unknown_ordinal}")
}
}
}
}
impl From<RuleAction> for fnet_routes::RuleAction {
fn from(action: RuleAction) -> Self {
match action {
RuleAction::Unreachable => {
fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable)
}
RuleAction::Lookup(table_id) => fnet_routes::RuleAction::Lookup(table_id),
}
}
}
#[derive(GenericOverIp, Debug)]
#[generic_over_ip(I, Ip)]
pub enum RuleTableRequest<I: FidlRuleAdminIpExt> {
NewRuleSet {
priority: RuleSetPriority,
rule_set: fidl::endpoints::ServerEnd<I::RuleSetMarker>,
control_handle: I::RuleTableControlHandle,
},
}
impl From<fnet_routes_admin::RuleTableV4Request> for RuleTableRequest<Ipv4> {
fn from(value: fnet_routes_admin::RuleTableV4Request) -> Self {
match value {
fnet_routes_admin::RuleTableV4Request::NewRuleSet {
priority,
rule_set,
control_handle,
} => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
}
}
}
impl From<fnet_routes_admin::RuleTableV6Request> for RuleTableRequest<Ipv6> {
fn from(value: fnet_routes_admin::RuleTableV6Request) -> Self {
match value {
fnet_routes_admin::RuleTableV6Request::NewRuleSet {
priority,
rule_set,
control_handle,
} => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
}
}
}
#[derive(GenericOverIp, Debug)]
#[generic_over_ip(I, Ip)]
pub enum RuleSetRequest<I: FidlRuleAdminIpExt> {
AddRule {
index: RuleIndex,
matcher: Result<RuleMatcher<I>, RuleFidlConversionError>,
action: RuleAction,
responder: I::RuleSetAddRuleResponder,
},
RemoveRule {
index: RuleIndex,
responder: I::RuleSetRemoveRuleResponder,
},
AuthenticateForRouteTable {
table: u32,
token: fidl::Event,
responder: I::RuleSetAuthenticateForRouteTableResponder,
},
Close {
control_handle: I::RuleSetControlHandle,
},
}
impl From<fnet_routes_admin::RuleSetV4Request> for RuleSetRequest<Ipv4> {
fn from(value: fnet_routes_admin::RuleSetV4Request) -> Self {
match value {
fnet_routes_admin::RuleSetV4Request::AddRule { index, matcher, action, responder } => {
RuleSetRequest::AddRule {
index: RuleIndex(index),
matcher: matcher.try_into(),
action: action.into(),
responder,
}
}
fnet_routes_admin::RuleSetV4Request::RemoveRule { index, responder } => {
RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
}
fnet_routes_admin::RuleSetV4Request::AuthenticateForRouteTable {
table,
token,
responder,
} => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
fnet_routes_admin::RuleSetV4Request::Close { control_handle } => {
RuleSetRequest::Close { control_handle }
}
}
}
}
impl From<fnet_routes_admin::RuleSetV6Request> for RuleSetRequest<Ipv6> {
fn from(value: fnet_routes_admin::RuleSetV6Request) -> Self {
match value {
fnet_routes_admin::RuleSetV6Request::AddRule { index, matcher, action, responder } => {
RuleSetRequest::AddRule {
index: RuleIndex(index),
matcher: matcher.try_into(),
action: action.into(),
responder,
}
}
fnet_routes_admin::RuleSetV6Request::RemoveRule { index, responder } => {
RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
}
fnet_routes_admin::RuleSetV6Request::AuthenticateForRouteTable {
table,
token,
responder,
} => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
fnet_routes_admin::RuleSetV6Request::Close { control_handle } => {
RuleSetRequest::Close { control_handle }
}
}
}
}
#[derive(Clone, Debug, Error)]
pub enum RuleSetCreationError {
#[error("failed to create proxy: {0}")]
CreateProxy(fidl::Error),
#[error("failed to create route set: {0}")]
RuleSet(fidl::Error),
}
pub fn new_rule_set<I: Ip + FidlRuleAdminIpExt>(
rule_table_proxy: &<I::RuleTableMarker as ProtocolMarker>::Proxy,
priority: RuleSetPriority,
) -> Result<<I::RuleSetMarker as ProtocolMarker>::Proxy, RuleSetCreationError> {
let (rule_set_proxy, rule_set_server_end) = fidl::endpoints::create_proxy::<I::RuleSetMarker>();
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct NewRuleSetInput<'a, I: FidlRuleAdminIpExt> {
rule_set_server_end: fidl::endpoints::ServerEnd<I::RuleSetMarker>,
rule_table_proxy: &'a <I::RuleTableMarker as ProtocolMarker>::Proxy,
}
let result = I::map_ip_in(
NewRuleSetInput::<'_, I> { rule_set_server_end, rule_table_proxy },
|NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
},
|NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
},
);
result.map_err(RuleSetCreationError::RuleSet)?;
Ok(rule_set_proxy)
}
pub async fn authenticate_for_route_table<I: Ip + FidlRuleAdminIpExt>(
rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
table_id: u32,
token: fidl::Event,
) -> Result<Result<(), fnet_routes_admin::AuthenticateForRouteTableError>, fidl::Error> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct AuthenticateForRouteTableInput<'a, I: FidlRuleAdminIpExt> {
rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
table_id: u32,
token: fidl::Event,
}
I::map_ip_in(
AuthenticateForRouteTableInput { rule_set, table_id, token },
|AuthenticateForRouteTableInput { rule_set, table_id, token }| {
Either::Left(rule_set.authenticate_for_route_table(table_id, token))
},
|AuthenticateForRouteTableInput { rule_set, table_id, token }| {
Either::Right(rule_set.authenticate_for_route_table(table_id, token))
},
)
.await
}
pub async fn add_rule<I: Ip + FidlRuleAdminIpExt>(
rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
index: RuleIndex,
matcher: RuleMatcher<I>,
action: RuleAction,
) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct AddRuleInput<'a, I: FidlRuleAdminIpExt> {
rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
index: RuleIndex,
matcher: RuleMatcher<I>,
action: RuleAction,
}
I::map_ip_in(
AddRuleInput { rule_set, index, matcher, action },
|AddRuleInput { rule_set, index, matcher, action }| {
Either::Left(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
},
|AddRuleInput { rule_set, index, matcher, action }| {
Either::Right(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
},
)
.await
}
pub async fn remove_rule<I: Ip + FidlRuleAdminIpExt>(
rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
index: RuleIndex,
) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct RemoveRuleInput<'a, I: FidlRuleAdminIpExt> {
rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
index: RuleIndex,
}
I::map_ip_in(
RemoveRuleInput { rule_set, index },
|RemoveRuleInput { rule_set, index }| Either::Left(rule_set.remove_rule(index.into())),
|RemoveRuleInput { rule_set, index }| Either::Right(rule_set.remove_rule(index.into())),
)
.await
}
pub async fn close_rule_set<I: Ip + FidlRuleAdminIpExt>(
rule_set: <I::RuleSetMarker as ProtocolMarker>::Proxy,
) -> Result<(), fidl::Error> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct CloseInput<'a, I: FidlRuleAdminIpExt> {
rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
}
let result = I::map_ip_in(
CloseInput { rule_set: &rule_set },
|CloseInput { rule_set }| rule_set.close(),
|CloseInput { rule_set }| rule_set.close(),
);
assert!(rule_set
.on_closed()
.await
.expect("failed to wait for signals")
.contains(fidl::Signals::CHANNEL_PEER_CLOSED));
result
}
pub fn get_rule_watcher<I: FidlRuleIpExt + FidlRouteIpExt>(
state_proxy: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
) -> Result<<I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy, WatcherCreationError>
{
let (watcher_proxy, watcher_server_end) =
fidl::endpoints::create_proxy::<I::RuleWatcherMarker>();
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct GetWatcherInputs<'a, I: FidlRuleIpExt + FidlRouteIpExt> {
watcher_server_end: fidl::endpoints::ServerEnd<I::RuleWatcherMarker>,
state_proxy: &'a <I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
}
let result = I::map_ip_in(
GetWatcherInputs::<'_, I> { watcher_server_end, state_proxy },
|GetWatcherInputs { watcher_server_end, state_proxy }| {
state_proxy.get_rule_watcher_v4(
watcher_server_end,
&fnet_routes::RuleWatcherOptionsV4::default(),
)
},
|GetWatcherInputs { watcher_server_end, state_proxy }| {
state_proxy.get_rule_watcher_v6(
watcher_server_end,
&fnet_routes::RuleWatcherOptionsV6::default(),
)
},
);
result.map_err(WatcherCreationError::GetWatcher)?;
Ok(watcher_proxy)
}
pub async fn watch<'a, I: FidlRuleIpExt>(
watcher_proxy: &'a <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
) -> Result<Vec<I::RuleEvent>, fidl::Error> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct WatchInputs<'a, I: FidlRuleIpExt> {
watcher_proxy: &'a <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
}
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct WatchOutputs<I: FidlRuleIpExt> {
watch_fut: fidl::client::QueryResponseFut<Vec<I::RuleEvent>>,
}
let WatchOutputs { watch_fut } = net_types::map_ip_twice!(
I,
WatchInputs { watcher_proxy },
|WatchInputs { watcher_proxy }| { WatchOutputs { watch_fut: watcher_proxy.watch() } }
);
watch_fut.await
}
#[derive(Clone, Debug, Error)]
pub enum RuleWatchError {
#[error("the call to `Watch()` failed: {0}")]
Fidl(fidl::Error),
#[error("failed to convert event returned by `Watch()`: {0}")]
Conversion(RuleFidlConversionError),
#[error("the call to `Watch()` returned an empty batch of events")]
EmptyEventBatch,
}
pub fn rule_event_stream_from_state<I: FidlRuleIpExt + FidlRouteIpExt>(
state: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>>, WatcherCreationError> {
let watcher = get_rule_watcher::<I>(state)?;
rule_event_stream_from_watcher(watcher)
}
pub fn rule_event_stream_from_watcher<I: FidlRuleIpExt>(
watcher: <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>>, WatcherCreationError> {
Ok(stream::ShortCircuit::new(
futures::stream::try_unfold(watcher, |watcher| async {
let events_batch = watch::<I>(&watcher).await.map_err(RuleWatchError::Fidl)?;
if events_batch.is_empty() {
return Err(RuleWatchError::EmptyEventBatch);
}
let events_batch = events_batch
.into_iter()
.map(|event| event.try_into().map_err(RuleWatchError::Conversion));
let event_stream = futures::stream::iter(events_batch);
Ok(Some((event_stream, watcher)))
})
.try_flatten(),
))
}
#[derive(Clone, Debug, Error)]
pub enum CollectRulesUntilIdleError<I: FidlRuleIpExt> {
#[error("there was an error in the event stream: {0}")]
ErrorInStream(RuleWatchError),
#[error("there was an unexpected event in the event stream: {0:?}")]
UnexpectedEvent(RuleEvent<I>),
#[error("the event stream unexpectedly ended")]
StreamEnded,
}
pub async fn collect_rules_until_idle<I: FidlRuleIpExt, C: Extend<InstalledRule<I>> + Default>(
event_stream: impl futures::Stream<Item = Result<RuleEvent<I>, RuleWatchError>> + Unpin,
) -> Result<C, CollectRulesUntilIdleError<I>> {
fold::fold_while(
event_stream,
Ok(C::default()),
|existing_rules: Result<C, CollectRulesUntilIdleError<I>>, event| {
futures::future::ready(match existing_rules {
Err(_) => {
unreachable!("`existing_rules` must be `Ok`, because we stop folding on err")
}
Ok(mut existing_rules) => match event {
Err(e) => {
fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::ErrorInStream(e)))
}
Ok(e) => match e {
RuleEvent::Existing(e) => {
existing_rules.extend([e]);
fold::FoldWhile::Continue(Ok(existing_rules))
}
RuleEvent::Idle => fold::FoldWhile::Done(Ok(existing_rules)),
e @ RuleEvent::Added(_) | e @ RuleEvent::Removed(_) => {
fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::UnexpectedEvent(
e,
)))
}
},
},
})
},
)
.await
.short_circuited()
.map_err(|_accumulated_thus_far: Result<C, CollectRulesUntilIdleError<I>>| {
CollectRulesUntilIdleError::StreamEnded
})?
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use fnet_routes::BaseMatcher;
use super::*;
#[test]
fn missing_base_matcher_v4() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV4 {
from: None,
base: None,
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Err(RuleFidlConversionError::BaseMatcherMissing)
);
}
#[test]
fn missing_base_matcher_v6() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
from: None,
base: None,
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Err(RuleFidlConversionError::BaseMatcherMissing)
);
}
#[test]
fn invalid_destination_subnet_v4() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV4 {
from: Some(net_declare::fidl_ip_v4_with_prefix!("192.168.0.1/24")),
base: Some(BaseMatcher::default()),
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Err(RuleFidlConversionError::DestinationSubnet(_))
);
}
#[test]
fn invalid_destination_subnet_v6() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
from: Some(net_declare::fidl_ip_v6_with_prefix!("fe80::1/64")),
base: Some(BaseMatcher::default()),
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Err(RuleFidlConversionError::DestinationSubnet(_))
);
}
#[test]
fn all_unspecified_matcher_v4() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV4 {
from: None,
base: Some(BaseMatcher {
locally_generated: None,
bound_device: None,
mark_1: None,
mark_2: None,
__source_breaking: fidl::marker::SourceBreaking,
}),
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Ok(RuleMatcher {
from: None,
locally_generated: None,
bound_device: None,
mark_1: None,
mark_2: None,
})
);
}
#[test]
fn all_unspecified_matcher_v6() {
let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
from: None,
base: Some(BaseMatcher {
locally_generated: None,
bound_device: None,
mark_1: None,
mark_2: None,
__source_breaking: fidl::marker::SourceBreaking,
}),
__source_breaking: fidl::marker::SourceBreaking,
};
assert_matches!(
RuleMatcher::try_from(fidl_matcher),
Ok(RuleMatcher {
from: None,
locally_generated: None,
bound_device: None,
mark_1: None,
mark_2: None,
})
);
}
}