use ace_proto::doip::constants::{
DOIP_ROUTING_ACTIVATION_REQ_ISO_LEN, DOIP_ROUTING_ACTIVATION_REQ_OEM_LEN,
};
use crate::payload::{
ActivationCode, ActivationType, RoutingActivationRequest, RoutingActivationResponse,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ActivationDenialReason {
UnknownSourceAddress,
TcpSocketsFull,
AlreadyConnected,
SourceAlreadyActive,
MissingAuthentication,
RejectedConfirmation,
UnsupportedActivationType,
RequiresTls,
}
impl From<ActivationDenialReason> for ActivationCode {
fn from(value: ActivationDenialReason) -> Self {
match value {
ActivationDenialReason::RejectedConfirmation => Self::DeniedRejectedConfirmation,
ActivationDenialReason::MissingAuthentication => Self::DeniedMissingAuthentication,
ActivationDenialReason::UnsupportedActivationType => {
Self::DeniedUnsupportedRoutingActivationType
}
ActivationDenialReason::RequiresTls => Self::DeniedRequestEncryptedTlsConnection,
ActivationDenialReason::SourceAlreadyActive => Self::DeniedSourceIsAlreadyActive,
ActivationDenialReason::TcpSocketsFull => Self::DeniedTcpSocketsFull,
ActivationDenialReason::AlreadyConnected => Self::DeniedTcpSocketAlreadyConnected,
ActivationDenialReason::UnknownSourceAddress => Self::DeniedUnknownSourceAddress,
}
}
}
pub trait ActivationAuthProvider {
fn authenticate(
&mut self,
source_address: u16,
oem_data: &[u8],
) -> Result<(), ActivationDenialReason>;
}
pub struct AlwaysAllow;
impl ActivationAuthProvider for AlwaysAllow {
fn authenticate(
&mut self,
_source_address: u16,
_oem_data: &[u8],
) -> Result<(), ActivationDenialReason> {
Ok(())
}
}
pub struct AlwaysDeny {
pub reason: ActivationDenialReason,
}
impl ActivationAuthProvider for AlwaysDeny {
fn authenticate(
&mut self,
_source_address: u16,
_oem_data: &[u8],
) -> Result<(), ActivationDenialReason> {
Err(self.reason.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ActivationLineState {
Idle,
Pending {
source_address: u16,
activation_type: ActivationType,
},
Active {
source_address: u16,
activation_type: ActivationType,
},
Deactivated { reason: ActivationDenialReason },
}
impl ActivationLineState {
pub fn is_active(&self) -> bool {
matches!(self, Self::Active { .. })
}
pub fn active_source_address(&self) -> Option<u16> {
match self {
Self::Active { source_address, .. } => Some(*source_address),
_ => None,
}
}
}
#[derive(Debug)]
pub struct ActivationStateMachine<A: ActivationAuthProvider> {
gateway_address: u16,
registered_addresses: heapless::Vec<u16, 16>,
supported_types: heapless::Vec<ActivationType, 4>,
auth: A,
pub state: ActivationLineState,
}
impl<A: ActivationAuthProvider> ActivationStateMachine<A> {
pub fn new(
gateway_address: u16,
registered_addresses: heapless::Vec<u16, 16>,
supported_types: heapless::Vec<ActivationType, 4>,
auth: A,
) -> Self {
Self {
gateway_address,
registered_addresses,
supported_types,
auth,
state: ActivationLineState::Idle,
}
}
pub fn process_request(&mut self, req: &RoutingActivationRequest) -> RoutingActivationResponse {
let source_address = u16::from_be_bytes(req.source_address);
let activation_type = req.activation_type.clone();
if !self.registered_addresses.contains(&source_address) {
return self.deny(source_address, ActivationDenialReason::UnknownSourceAddress);
}
if !self.supported_types.contains(&activation_type) {
return self.deny(
source_address,
ActivationDenialReason::UnsupportedActivationType,
);
}
if self.state.is_active() {
return self.deny(source_address, ActivationDenialReason::AlreadyConnected);
}
if activation_type == ActivationType::CentralSecurity {
let mut oem_data =
[0u8; DOIP_ROUTING_ACTIVATION_REQ_ISO_LEN + DOIP_ROUTING_ACTIVATION_REQ_OEM_LEN];
let (iso, oem) = oem_data.split_at_mut(DOIP_ROUTING_ACTIVATION_REQ_ISO_LEN);
iso.copy_from_slice(&req.reserved);
oem.copy_from_slice(&req.reserved_for_oem);
match self.auth.authenticate(source_address, &oem_data) {
Ok(()) => {}
Err(reason) => return self.deny(source_address, reason),
}
}
self.state = ActivationLineState::Active {
source_address,
activation_type,
};
RoutingActivationResponse {
logical_address: req.source_address,
source_address: self.gateway_address.to_be_bytes(),
activation_code: ActivationCode::SuccessfullyActivated,
reserved: [0u8; 4],
reserved_for_oem: None,
}
}
pub fn drop_line(&mut self, reason: ActivationDenialReason) {
self.state = ActivationLineState::Deactivated { reason };
}
fn deny(
&mut self,
source_address: u16,
reason: ActivationDenialReason,
) -> RoutingActivationResponse {
let code = ActivationCode::from(reason.clone());
self.state = ActivationLineState::Deactivated { reason };
RoutingActivationResponse {
logical_address: source_address.to_be_bytes(),
source_address: self.gateway_address.to_be_bytes(),
activation_code: code,
reserved: [0u8; 4],
reserved_for_oem: None,
}
}
}