use std::collections::VecDeque;
use anyhow::Result;
use log::{debug, error, warn};
use crate::{
Addr, Disc, Dm, Frmr, Iframe, Packet, PacketType, Rej, Rnr, Rr, Sabm, Sabme, Srej, Test, Ua,
Ui, Xid,
};
#[derive(Debug, PartialEq)]
pub enum Event {
Connect { addr: Addr, ext: bool },
Disconnect,
Data(Vec<u8>),
T1,
T3,
Sabm(Sabm, Addr),
Sabme(Sabme, Addr),
Dm(Dm),
Rr(Rr, bool),
Rnr(Rnr),
Ui(Ui, bool),
Disc(Disc),
Iframe(Iframe, bool),
Ua(Ua),
Frmr(Frmr),
Rej(Rej),
Srej(Srej),
Test(Test),
Xid(Xid),
}
#[derive(Debug, PartialEq)]
pub enum ReturnEvent {
Packet(Packet),
DlError(DlError),
Data(Res),
}
impl ReturnEvent {
pub fn serialize(&self, ext: bool) -> Option<Vec<u8>> {
match self {
ReturnEvent::Packet(p) => Some(p.serialize(ext)),
ReturnEvent::DlError(e) => {
eprintln!("TODO: DLERROR: {e}");
None
}
ReturnEvent::Data(d) => {
debug!("Data received: {d:?}");
None
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum DlError {
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
}
impl std::fmt::Display for DlError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"{}",
match self {
DlError::A => "A: F=1 received but P=1 not outstanding",
DlError::B => "B: Unexpected DM with F=1 in states 3,4,5",
DlError::C => "C: Unexpected UA in states 3 (Connected), 4 (TimerRecovery), 5 (Awaiting v2.2 Connection)",
DlError::D => "D: UA received without F=1 when SABM or DISC was sent P=1",
DlError::E => "E: DM received in states 3 (Connected), 4 (TimerRecovery), 5 (Awaiting v2.2 Connection)",
DlError::F => "F: Data link reset; i.e., SABM received in state 3 (Connected), 4 (TimerRecovery), 5 (Awaiting v2.2 Connection)",
DlError::G => "G: Connection timed out",
DlError::H => "H: Undocumented. May mean connection timed out while disconnecting",
DlError::I => "I: N2 timeouts; unacknowledged data",
DlError::J => "J: N(r) sequence error",
DlError::K => "K: Undocumented. May mean unexpected frame received",
DlError::L => "L: Control field invalid or not implemented",
DlError::M => "M: Information field was received in a U- or S-type frame",
DlError::N => "N: Length of frame incorrect for frame type",
DlError::O => "O: I frame exceeded maximum allowed length",
DlError::P => "P: N(s) out of the window",
DlError::Q => "Q: UI response received, or UI command with P=1 received",
DlError::R => "R: UI frame exceeded maximum allowed length",
DlError::S => "S: I response received",
DlError::T => "T: N2 timeout; no response to enquiry",
DlError::U => "U: N2 timeouts; extended pere busy condition",
DlError::V => "V: No DL machines available to establish connection",
}
)
}
}
pub enum Action {
State(Box<dyn State>),
DlError(DlError),
SendUa { pf: bool },
SendRr { pf: bool, nr: u8, command: bool },
SendRej { pf: bool, nr: u8 },
SendRnr { pf: bool, nr: u8, command: bool },
SendDisc { pf: bool },
SendIframe(Iframe),
SendDm { pf: bool },
SendSabm { pf: bool },
Deliver(Vec<u8>),
EOF,
}
const DEFAULT_SRT: std::time::Duration = std::time::Duration::from_secs(3);
const DEFAULT_MTU: usize = 200;
const MAX_OBUF_SIZE: usize = 100_000_000;
const DEFAULT_T3V: std::time::Duration = std::time::Duration::from_secs(3);
const DEFAULT_N2: u8 = 3;
#[derive(Debug)]
pub struct Timer {
running: bool,
expiry: std::time::Instant,
}
impl Default for Timer {
fn default() -> Self {
Self {
running: false,
expiry: std::time::Instant::now(),
}
}
}
impl Timer {
fn start(&mut self, v: std::time::Duration) {
self.expiry = std::time::Instant::now() + v;
self.running = true;
}
pub fn is_expired(&self) -> Option<bool> {
if !self.running {
return None;
}
Some(std::time::Instant::now() > self.expiry)
}
pub fn remaining(&self) -> Option<std::time::Duration> {
if !self.running {
return None;
}
Some(
self.expiry
.saturating_duration_since(std::time::Instant::now()),
)
}
fn stop(&mut self) {
self.running = false;
}
fn restart(&mut self, v: std::time::Duration) {
self.start(v);
}
}
#[derive(Debug)]
pub struct Data {
pub(crate) me: Addr,
pub(crate) peer: Option<Addr>,
layer3_initiated: bool,
pub(crate) t1: Timer,
pub(crate) t3: Timer,
t3v: std::time::Duration,
vs: u8,
va: u8,
vr: u8,
pub(crate) srt_default: std::time::Duration,
srt: std::time::Duration,
t1v: std::time::Duration,
n1: usize,
n2: u8,
rc: u8,
modulus: u8,
peer_receiver_busy: bool,
reject_exception: bool,
pub(crate) able_to_establish: bool,
sreject_exception: u32,
own_receiver_busy: bool,
acknowledge_pending: bool,
srej_enabled: bool,
k: u8,
iframe_queue: Vec<Vec<u8>>,
obuf: VecDeque<u8>,
mtu: usize,
iframe_resend_queue: VecDeque<Iframe>,
}
impl Data {
pub fn new(me: Addr) -> Self {
Self {
me,
peer: None,
n1: 65000, layer3_initiated: false,
t1: Timer::default(),
t3: Timer::default(),
vs: 0,
va: 0,
vr: 0,
srt_default: DEFAULT_SRT,
srt: DEFAULT_SRT,
t1v: DEFAULT_SRT,
t3v: DEFAULT_T3V,
n2: DEFAULT_N2,
rc: 0,
k: 7,
modulus: 8,
peer_receiver_busy: false,
reject_exception: false,
sreject_exception: 0,
srej_enabled: false,
acknowledge_pending: false,
own_receiver_busy: false,
iframe_queue: Vec::new(),
mtu: DEFAULT_MTU,
obuf: VecDeque::new(),
iframe_resend_queue: VecDeque::new(),
able_to_establish: false,
}
}
#[must_use]
pub fn ext(&self) -> bool {
self.modulus == 128
}
#[must_use]
pub fn t1_expired(&self) -> bool {
self.t1.is_expired().unwrap_or(false)
}
#[must_use]
pub fn t3_expired(&self) -> bool {
self.t3.is_expired().unwrap_or(false)
}
#[must_use]
pub fn active_timers(&self) -> Vec<Event> {
let mut ret = Vec::new();
if self.t1_expired() {
ret.push(Event::T1);
}
if self.t3_expired() {
ret.push(Event::T3);
}
ret
}
#[must_use]
pub fn next_timer_remaining(&self) -> Option<std::time::Duration> {
match (self.t1.remaining(), self.t3.remaining()) {
(Some(t1), Some(t3)) => Some(std::cmp::min(t1, t3)),
(None, Some(t)) => Some(t),
(Some(t), None) => Some(t),
(None, None) => None,
}
}
#[must_use]
fn ui_check(&self, command: bool, len: usize) -> Vec<Action> {
if !command {
return vec![Action::DlError(DlError::Q)];
}
if len > self.n1 {
return vec![Action::DlError(DlError::K)];
}
debug!("DL-UNIT_DATA indication");
vec![]
}
#[must_use]
fn nr_error_recovery(&mut self) -> Vec<Action> {
self.layer3_initiated = false;
vec![Action::DlError(DlError::J), self.establish_data_link()]
}
#[must_use]
fn check_need_for_response(&mut self, command: bool, pf: bool) -> Vec<Action> {
match (command, pf) {
(true, true) => vec![self.enquiry_response(true)],
(false, true) => vec![Action::DlError(DlError::A)],
(_, _) => vec![],
}
}
#[must_use]
fn enquiry_response(&mut self, pf: bool) -> Action {
self.acknowledge_pending = false;
if self.own_receiver_busy {
Action::SendRnr {
pf,
nr: self.vr,
command: false,
}
} else {
Action::SendRr {
pf,
nr: self.vr,
command: false,
}
}
}
#[must_use]
fn invoke_retransmission(&mut self, _nr: u8) -> Vec<Action> {
self.iframe_resend_queue
.iter()
.map(|i| Action::SendIframe(i.clone()))
.collect()
}
fn select_t1_value(&mut self) {
if self.rc == 0 {
self.srt = self.srt_default;
} else if self.t1_expired() {
let t = std::time::Duration::from_millis(self.rc as u64 * 250);
self.srt = t + self.srt + self.srt;
}
}
#[must_use]
fn transmit_enquiry(&mut self) -> Action {
self.acknowledge_pending = false;
self.t1.start(self.t1v); if self.own_receiver_busy {
Action::SendRnr {
pf: true,
nr: self.vr,
command: true,
}
} else {
Action::SendRr {
pf: true,
nr: self.vr,
command: true,
}
}
}
#[must_use]
fn check_iframe_acked(&mut self, nr: u8) -> Vec<Action> {
if self.peer_receiver_busy {
self.t3.stop();
if !self.t1.running {
self.t1.start(self.srt); }
self.update_ack(nr)
} else if nr == self.vs {
self.t1.stop();
self.t3.start(self.t3v);
self.select_t1_value();
self.update_ack(nr)
} else if nr != self.va {
self.t1.restart(self.srt);
self.update_ack(nr)
} else {
vec![]
}
}
#[must_use]
fn update_ack(&mut self, nr: u8) -> Vec<Action> {
while self.va != nr {
assert!(!self.iframe_resend_queue.is_empty());
self.iframe_resend_queue.pop_front();
self.va = (self.va + 1) % self.modulus;
}
self.flush()
}
fn clear_iframe_queue(&mut self) {
self.iframe_queue.clear();
self.iframe_resend_queue.clear();
}
fn clear_exception_conditions(&mut self) {
self.peer_receiver_busy = false;
self.reject_exception = false;
self.own_receiver_busy = false;
self.acknowledge_pending = false;
self.sreject_exception = 0;
self.iframe_queue.clear();
}
#[must_use]
fn establish_data_link(&mut self) -> Action {
self.clear_exception_conditions();
self.rc = 1;
self.t3.stop();
self.t1.restart(self.srt);
Action::SendSabm { pf: true }
}
pub(crate) fn set_version_2_2(&mut self) {
self.modulus = 128;
self.k = 32;
self.n2 = 10;
}
fn set_version_2(&mut self) {
self.modulus = 8;
self.k = 4;
self.n2 = 10;
}
#[must_use]
fn flush(&mut self) -> Vec<Action> {
if self.peer_receiver_busy {
return vec![];
}
let mut act = Vec::new();
loop {
if self.obuf.is_empty() {
break;
}
if self.vs == (self.va + self.k) % self.modulus {
debug!(
"tx window full with more data ({} bytes) to send!",
self.obuf.len()
);
break;
}
let payload = self
.obuf
.drain(..std::cmp::min(self.mtu, self.obuf.len()))
.collect::<Vec<_>>();
let ns = self.vs;
self.vs = (self.vs + 1) % self.modulus;
self.acknowledge_pending = false;
if self.t1.running {
self.t3.stop();
self.t1.start(self.srt);
}
let i = Iframe {
ns,
nr: self.vr,
poll: false,
pid: 0xF0,
payload,
};
self.iframe_resend_queue.push_back(i.clone());
act.push(Action::SendIframe(i));
}
act
}
}
pub trait State {
fn name(&self) -> String;
fn is_state_connected(&self) -> bool {
false
}
fn is_state_disconnected(&self) -> bool {
false
}
#[must_use]
fn connect(&self, _data: &mut Data, _addr: &Addr, _ext: bool) -> Vec<Action> {
eprintln!("TODO: unexpected DLConnect");
vec![]
}
#[must_use]
fn disconnect(&self, _data: &mut Data) -> Vec<Action> {
eprintln!("TODO: unexpected DLDisconnect in state {}", self.name());
vec![]
}
#[must_use]
fn data(&self, _data: &mut Data, _payload: &[u8]) -> Vec<Action> {
eprintln!("writing data while not connected!");
vec![]
}
#[must_use]
fn t1(&self, data: &mut Data) -> Vec<Action> {
data.t1.stop();
eprintln!("TODO: unexpected T1 expire");
vec![]
}
#[must_use]
fn t3(&self, data: &mut Data) -> Vec<Action> {
data.t3.stop();
eprintln!("TODO: unexpected T3 expire");
vec![]
}
#[must_use]
fn rr(&self, _data: &mut Data, _packet: &Rr, _command: bool) -> Vec<Action> {
eprintln!("TODO: unexpected RR");
vec![]
}
#[must_use]
fn rej(&self, _data: &mut Data, _packet: &Rej) -> Vec<Action> {
eprintln!("TODO: unexpected REJ");
vec![]
}
#[must_use]
fn xid(&self, _data: &mut Data, _packet: &Xid) -> Vec<Action> {
eprintln!("TODO: unexpected XID");
vec![]
}
#[must_use]
fn test(&self, _data: &mut Data, _packet: &Test) -> Vec<Action> {
eprintln!("TODO: unexpected TEST");
vec![]
}
#[must_use]
fn srej(&self, _data: &mut Data, _packet: &Srej) -> Vec<Action> {
eprintln!("TODO: unexpected SREJ");
vec![]
}
#[must_use]
fn frmr(&self, _data: &mut Data) -> Vec<Action> {
eprintln!("TODO: unexpected FRMR");
vec![]
}
#[must_use]
fn rnr(&self, _data: &mut Data, _packet: &Rnr) -> Vec<Action> {
eprintln!("TODO: unexpected RNR");
vec![]
}
#[must_use]
fn sabm(&self, _data: &mut Data, _src: &Addr, _packet: &Sabm) -> Vec<Action> {
eprintln!("TODO: unexpected SABM");
vec![]
}
#[must_use]
fn sabme(&self, _data: &mut Data, _src: &Addr, _packet: &Sabme) -> Vec<Action> {
eprintln!("TODO: unexpected SABME");
vec![]
}
#[must_use]
fn iframe(&self, _data: &mut Data, _packet: &Iframe, _cr: bool) -> Vec<Action> {
eprintln!("TODO; unexpected iframe");
vec![]
}
#[must_use]
fn ui(&self, _data: &mut Data, _cr: bool, _packet: &Ui) -> Vec<Action> {
vec![]
}
#[must_use]
fn ua(&self, _data: &mut Data, _packet: &Ua) -> Vec<Action> {
eprintln!("TODO; unexpected UA");
vec![]
}
#[must_use]
fn dm(&self, _data: &mut Data, _packet: &Dm) -> Vec<Action> {
eprintln!("TODO: unexpected DM");
vec![]
}
#[must_use]
fn disc(&self, _data: &mut Data, _packet: &Disc) -> Vec<Action> {
eprintln!("TODO: unexpected DISC");
vec![]
}
}
struct Disconnected {}
impl Disconnected {
#[must_use]
pub fn new() -> Self {
Self {}
}
#[must_use]
fn sabm_and_sabme(&self, data: &mut Data, src: Addr, pf: bool) -> Vec<Action> {
debug!("DL-Connect indication");
if !data.able_to_establish {
return vec![Action::SendDm { pf }];
}
data.clear_exception_conditions();
data.vs = 0;
data.va = 0;
data.vr = 0;
data.srt = data.srt_default;
data.t1v = data.srt + data.srt;
data.t3.start(data.t3v);
data.rc = 0;
data.peer = Some(src);
vec![
Action::SendUa { pf },
Action::State(Box::new(Connected::new(ConnectedState::Connected))),
]
}
}
impl State for Disconnected {
fn name(&self) -> String {
"Disconnected".to_string()
}
fn is_state_disconnected(&self) -> bool {
true
}
fn connect(&self, data: &mut Data, addr: &Addr, ext: bool) -> Vec<Action> {
data.modulus = match ext {
true => 128,
false => 8,
};
data.peer = Some(addr.clone());
data.srt = data.srt_default;
data.t1v = 2 * data.srt;
data.layer3_initiated = true;
vec![
Action::State(Box::new(AwaitingConnection::new())),
data.establish_data_link(),
]
}
fn disconnect(&self, _data: &mut Data) -> Vec<Action> {
eprintln!("Disconnect while already disconnected");
vec![]
}
fn ui(&self, data: &mut Data, cr: bool, packet: &Ui) -> Vec<Action> {
let mut ret = data.ui_check(cr, packet.payload.len());
if packet.push {
ret.push(Action::SendDm { pf: true });
}
ret
}
fn sabm(&self, data: &mut Data, src: &Addr, sabm: &Sabm) -> Vec<Action> {
data.set_version_2();
self.sabm_and_sabme(data, src.clone(), sabm.poll)
}
fn sabme(&self, data: &mut Data, src: &Addr, packet: &Sabme) -> Vec<Action> {
data.set_version_2_2();
self.sabm_and_sabme(data, src.clone(), packet.poll)
}
fn dm(&self, _data: &mut Data, _packet: &Dm) -> Vec<Action> {
vec![]
}
fn ua(&self, _data: &mut Data, _packet: &Ua) -> Vec<Action> {
vec![Action::DlError(DlError::C), Action::DlError(DlError::D)]
}
fn disc(&self, _data: &mut Data, packet: &Disc) -> Vec<Action> {
vec![Action::SendDm { pf: packet.poll }]
}
}
struct AwaitingConnection {}
impl AwaitingConnection {
#[must_use]
fn new() -> Self {
Self {}
}
}
impl State for AwaitingConnection {
fn name(&self) -> String {
"AwaitingConnection".to_string()
}
fn t1(&self, data: &mut Data) -> Vec<Action> {
eprintln!("t1 expired while connecting, retrying");
data.t1.stop();
if data.rc == data.n2 {
data.clear_iframe_queue();
vec![
Action::DlError(DlError::G),
Action::State(Box::new(Disconnected::new())),
]
} else {
data.rc += 1;
data.select_t1_value();
data.t1.start(data.srt);
vec![Action::SendSabm { pf: true }]
}
}
fn ua(&self, data: &mut Data, packet: &Ua) -> Vec<Action> {
let f = packet.poll;
if !f {
return vec![Action::DlError(DlError::D)];
}
if data.layer3_initiated {
debug!("DL-CONNECT CONFIRM");
} else if data.vs != data.va {
data.srt = data.srt_default;
data.t1v = data.srt + data.srt;
debug!("DL-CONNECT CONFIRM, vs!=va");
warn!("Strange state entered: UA received while vs != va");
}
data.t1.stop();
data.t3.start(data.t3v);
data.vs = 0;
data.va = 0;
data.vr = 0;
data.select_t1_value();
vec![Action::State(Box::new(Connected::new(
ConnectedState::Connected,
)))]
}
fn sabm(&self, _data: &mut Data, _src: &Addr, packet: &Sabm) -> Vec<Action> {
vec![Action::SendUa { pf: packet.poll }]
}
fn sabme(&self, _data: &mut Data, _src: &Addr, packet: &Sabme) -> Vec<Action> {
vec![Action::SendDm { pf: packet.poll }]
}
}
struct AwaitingRelease {}
impl AwaitingRelease {
#[must_use]
fn new() -> Self {
Self {}
}
}
impl State for AwaitingRelease {
fn name(&self) -> String {
"AwaitingRelease".to_string()
}
fn dm(&self, data: &mut Data, p: &Dm) -> Vec<Action> {
if !p.poll {
return vec![];
}
data.t1.stop();
vec![Action::State(Box::new(Disconnected::new()))]
}
fn ua(&self, data: &mut Data, p: &Ua) -> Vec<Action> {
if !p.poll {
return vec![Action::DlError(DlError::D)];
}
debug!("DL-DISCONNECT confirm");
data.t1.stop();
vec![Action::State(Box::new(Disconnected::new()))]
}
fn t1(&self, data: &mut Data) -> Vec<Action> {
data.t1.stop();
if data.rc == data.n2 {
debug!("DL-DISCONNECT confirm");
data.t3.stop();
return vec![
Action::DlError(DlError::H),
Action::State(Box::new(Disconnected::new())),
];
}
data.rc += 1;
data.select_t1_value();
data.t1.start(data.t1v);
vec![Action::SendDisc { pf: true }]
}
}
enum ConnectedState {
Connected,
TimerRecovery,
}
struct Connected {
connected_state: ConnectedState,
}
impl Connected {
#[must_use]
fn new(connected_state: ConnectedState) -> Self {
Self { connected_state }
}
#[must_use]
fn rr_connected(&self, data: &mut Data, packet: &Rr, cr: bool) -> Vec<Action> {
data.peer_receiver_busy = false;
let mut act = data.check_need_for_response(cr, packet.poll);
if !in_range(data.va, packet.nr, data.vs, data.modulus) {
act.extend(data.nr_error_recovery());
act.push(Action::State(Box::new(AwaitingConnection::new())));
} else {
act.extend(data.check_iframe_acked(packet.nr));
}
act
}
#[must_use]
fn rr_timer_recovery(&self, data: &mut Data, packet: &Rr, cr: bool) -> Vec<Action> {
data.peer_receiver_busy = false;
if !cr && packet.poll {
data.t1.stop();
data.select_t1_value();
if !in_range(data.va, packet.nr, data.vs, data.modulus) {
let mut act = data.nr_error_recovery();
act.push(Action::State(Box::new(AwaitingConnection::new())));
return act;
}
let mut act = data.update_ack(packet.nr);
if data.vs == data.va {
data.t3.start(data.t3v);
data.rc = 0; act.push(Action::State(Box::new(Connected::new(
ConnectedState::Connected,
))));
} else {
act.extend(data.invoke_retransmission(packet.nr));
data.t3.stop();
data.t1.start(data.t1v);
data.acknowledge_pending = true;
}
return act;
}
let mut act = Vec::new();
if cr && packet.poll {
act.push(data.enquiry_response(true));
}
if in_range(data.va, packet.nr, data.vs, data.modulus) {
act.extend(data.update_ack(packet.nr));
} else {
act.extend(data.nr_error_recovery());
act.push(Action::State(Box::new(AwaitingConnection::new())));
}
act
}
fn sabm_or_sabme(&self, data: &mut Data, poll: bool) -> Vec<Action> {
data.clear_exception_conditions();
if data.vs != data.va {
data.iframe_queue.clear();
debug!("DL-Connect indication");
}
data.t1.stop();
data.t3.start(data.t3v);
data.va = 0;
data.vs = 0;
data.vr = 0; if let ConnectedState::Connected = self.connected_state {
data.rc = 0;
}
vec![
Action::DlError(DlError::F),
Action::SendUa { pf: poll },
Action::State(Box::new(Connected::new(ConnectedState::Connected))),
]
}
}
impl State for Connected {
fn name(&self) -> String {
match self.connected_state {
ConnectedState::Connected => "Connected".to_string(),
ConnectedState::TimerRecovery => "TimerRecovery".to_string(),
}
}
fn is_state_connected(&self) -> bool {
true
}
fn disconnect(&self, data: &mut Data) -> Vec<Action> {
data.clear_iframe_queue();
data.rc = 0;
data.t1.start(data.srt); data.t3.stop();
vec![
Action::SendDisc { pf: true },
Action::State(Box::new(AwaitingRelease::new())),
]
}
fn data(&self, data: &mut Data, payload: &[u8]) -> Vec<Action> {
data.obuf.extend(payload);
if data.obuf.len() > MAX_OBUF_SIZE {
panic!(
"TODO: handle better. Output buffer got too large. {} > {}",
data.obuf.len(),
MAX_OBUF_SIZE
);
}
data.flush()
}
fn sabm(&self, data: &mut Data, _src: &Addr, packet: &Sabm) -> Vec<Action> {
data.set_version_2();
self.sabm_or_sabme(data, packet.poll)
}
fn sabme(&self, data: &mut Data, _src: &Addr, packet: &Sabme) -> Vec<Action> {
data.set_version_2_2();
self.sabm_or_sabme(data, packet.poll)
}
fn dm(&self, data: &mut Data, _packet: &Dm) -> Vec<Action> {
debug!("DL-DISCONNECT");
data.clear_iframe_queue();
data.t1.stop();
data.t3.stop();
vec![
Action::DlError(DlError::E),
Action::State(Box::new(Disconnected::new())),
]
}
fn disc(&self, data: &mut Data, p: &Disc) -> Vec<Action> {
data.clear_iframe_queue();
data.t1.stop();
data.t3.stop();
vec![
Action::SendUa { pf: p.poll },
Action::EOF,
Action::State(Box::new(Disconnected::new())),
]
}
fn iframe(&self, data: &mut Data, p: &Iframe, command_response: bool) -> Vec<Action> {
if !command_response {
return vec![Action::DlError(DlError::S)];
}
if p.payload.len() > data.n1 {
data.layer3_initiated = false;
debug!("Discarding frame for being too big");
return vec![
data.establish_data_link(),
Action::DlError(DlError::O),
Action::State(Box::new(AwaitingConnection::new())),
];
}
if !in_range(data.va, p.nr, data.vs, data.modulus) {
debug!("Discarding frame for being out of range");
let mut acts = data.nr_error_recovery();
acts.push(Action::State(Box::new(AwaitingConnection::new())));
return acts;
}
let mut actions = vec![];
match self.connected_state {
ConnectedState::Connected => actions.extend(data.check_iframe_acked(p.nr)),
ConnectedState::TimerRecovery => actions.extend(data.update_ack(p.nr)),
}
if data.own_receiver_busy {
debug!("Discarding iframe because busy and being polled");
if p.poll {
actions.push(Action::SendRnr {
pf: true,
nr: data.vr,
command: false,
});
data.acknowledge_pending = false;
}
return actions;
}
if p.ns == data.vr {
debug!("iframe in order {}", p.ns);
data.vr = (data.vr + 1) % data.modulus;
data.reject_exception = false;
if data.sreject_exception > 0 {
data.sreject_exception -= 1;
}
actions.push(Action::Deliver(p.payload.clone()));
while
false {
data.vr = (data.vr + 1) % data.modulus;
}
if p.poll {
actions.push(Action::SendRr {
pf: true,
nr: data.vr,
command: false,
});
data.acknowledge_pending = false;
return actions;
}
if !data.acknowledge_pending {
data.acknowledge_pending = true;
}
return actions;
}
debug!("Iframe not in order got={} want={}", p.ns, data.vr);
if data.reject_exception {
if p.poll {
actions.push(Action::SendRr {
pf: true,
nr: data.vr,
command: false,
});
data.acknowledge_pending = false;
}
return actions;
}
if !data.srej_enabled {
data.reject_exception = true;
actions.push(Action::SendRej {
pf: p.poll,
nr: data.vr,
});
data.acknowledge_pending = false;
return actions;
}
if data.sreject_exception > 0 {
data.sreject_exception += 1;
data.acknowledge_pending = false;
return actions;
}
if p.ns != (data.vr + 1) % data.modulus {
actions.push(Action::SendRej {
pf: p.poll,
nr: data.vr,
});
data.acknowledge_pending = false;
return actions;
}
data.sreject_exception += 1;
data.acknowledge_pending = false;
actions
}
fn t1(&self, data: &mut Data) -> Vec<Action> {
data.t1.stop();
data.rc = match self.connected_state {
ConnectedState::Connected => 1,
ConnectedState::TimerRecovery => data.rc + 1,
};
if data.rc != data.n2 {
return vec![
data.transmit_enquiry(),
Action::State(Box::new(Connected::new(ConnectedState::TimerRecovery))),
];
}
data.clear_iframe_queue(); debug!("DL-DISCONNECT request");
vec![
Action::DlError(match (data.vs == data.va, data.peer_receiver_busy) {
(false, _) => DlError::I,
(true, true) => DlError::U,
(true, false) => DlError::T,
}),
Action::SendDm { pf: false },
Action::State(Box::new(Disconnected::new())),
]
}
fn t3(&self, data: &mut Data) -> Vec<Action> {
data.t3.stop();
if let ConnectedState::TimerRecovery = self.connected_state {
error!("T3 should not be running in TimerRecovery");
}
data.rc = 0;
vec![
Action::State(Box::new(Connected::new(ConnectedState::TimerRecovery))),
data.transmit_enquiry(),
]
}
fn ua(&self, data: &mut Data, _ua: &Ua) -> Vec<Action> {
data.layer3_initiated = false;
vec![
Action::DlError(DlError::C),
data.establish_data_link(),
Action::State(Box::new(AwaitingConnection::new())),
]
}
fn frmr(&self, data: &mut Data) -> Vec<Action> {
data.layer3_initiated = false;
vec![
Action::DlError(DlError::K),
data.establish_data_link(),
Action::State(Box::new(AwaitingConnection::new())),
]
}
fn ui(&self, data: &mut Data, cr: bool, packet: &Ui) -> Vec<Action> {
let mut act = data.ui_check(cr, packet.payload.len());
if packet.push {
act.push(data.enquiry_response(true));
}
act
}
fn rr(&self, data: &mut Data, packet: &Rr, cr: bool) -> Vec<Action> {
match self.connected_state {
ConnectedState::Connected => self.rr_connected(data, packet, cr),
ConnectedState::TimerRecovery => self.rr_timer_recovery(data, packet, cr),
}
}
}
#[must_use]
fn in_range(va: u8, nr: u8, vs: u8, modulus: u8) -> bool {
let mut t = va;
loop {
if t == nr {
return true;
}
if t == vs {
return false;
}
t = (t + 1) % modulus;
}
}
#[must_use]
pub fn new() -> Box<dyn State> {
Box::new(Disconnected::new())
}
#[derive(Debug, PartialEq)]
pub enum Res {
None,
EOF,
Some(Vec<u8>),
}
#[must_use]
pub fn handle(
state: &dyn State,
data: &mut Data,
packet: &Event,
) -> (Option<Box<dyn State>>, Vec<ReturnEvent>) {
let actions = match packet {
Event::Connect { addr, ext } => state.connect(data, addr, *ext),
Event::Disconnect => state.disconnect(data),
Event::Data(payload) => state.data(data, payload),
Event::T1 => state.t1(data),
Event::T3 => state.t3(data),
Event::Sabm(p, src) => state.sabm(data, src, p),
Event::Sabme(p, src) => state.sabme(data, src, p),
Event::Dm(dm) => state.dm(data, dm),
Event::Ui(p, cr) => state.ui(data, *cr, p),
Event::Disc(p) => state.disc(data, p),
Event::Iframe(p, command_response) => state.iframe(data, p, *command_response),
Event::Ua(p) => state.ua(data, p),
Event::Rr(p, command) => state.rr(data, p, *command),
Event::Rnr(p) => state.rnr(data, p),
Event::Frmr(_) => state.frmr(data),
Event::Rej(p) => state.rej(data, p),
Event::Srej(p) => state.srej(data, p),
Event::Xid(p) => state.xid(data, p),
Event::Test(p) => state.test(data, p),
};
let mut ret = Vec::new();
for act in &actions {
use Action::*;
match act {
Action::State(_) => {} DlError(code) => ret.push(ReturnEvent::DlError(*code)),
SendIframe(iframe) => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: true, command_response_la: false, digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Iframe(iframe.clone()),
})),
SendDisc { pf } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: true, command_response_la: false, digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Disc(Disc { poll: *pf }),
})),
SendUa { pf } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Ua(Ua { poll: *pf }),
})),
SendRej { pf, nr } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rej(Rej { poll: *pf, nr: *nr }),
})),
SendRr { pf, nr, command } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: *command,
command_response_la: !*command,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rr(Rr { poll: *pf, nr: *nr }),
})),
SendRnr { pf, nr, command } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: *command,
command_response_la: !*command,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rnr(Rnr { poll: *pf, nr: *nr }),
})),
SendDm { pf } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: true, command_response_la: false, digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Dm(Dm { poll: *pf }),
})),
SendSabm { pf } => ret.push(ReturnEvent::Packet(Packet {
src: data.me.clone(),
dst: data.peer.clone().unwrap().clone(),
command_response: true, command_response_la: false, digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Sabm(Sabm { poll: *pf }),
})),
Deliver(p) => ret.push(ReturnEvent::Data(Res::Some(p.to_vec()))),
EOF => ret.push(ReturnEvent::Data(Res::EOF)),
}
}
for act in actions {
if let Action::State(new_state) = act {
return (Some(new_state), ret);
}
}
(None, ret)
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_all(want: &[ReturnEvent], got: &[ReturnEvent], more: &str) {
for w in want {
let mut found = false;
for g in got {
if g == w {
found = true;
break;
}
}
assert!(found, "Did not find {w:?}\ngot: {got:?}");
}
assert_eq!(
want.len(),
got.len(),
"got and want different lengths for {more}:\nwant: {want:?}\ngot: {got:?}"
);
}
#[test]
fn disconnected_outgoing_timeout() -> Result<()> {
let mut data = Data::new(Addr::new("M0THC-1")?);
let con = Disconnected::new();
dbg!("First attempt");
let (con, events) = handle(
&con,
&mut data,
&Event::Connect {
addr: Addr::new("M0THC-2")?,
ext: false,
},
);
let con = con.unwrap();
assert_eq!(con.name(), "AwaitingConnection");
assert_eq!(data.peer, Some(Addr::new("M0THC-2")?));
assert_all(
&[ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: true,
command_response_la: false,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Sabm(Sabm { poll: true }),
})],
&events,
"connect",
);
for retry in 1.. {
dbg!("Retry", retry);
let (c2, events) = handle(&*con, &mut data, &Event::T1);
if retry == 3 {
assert_eq!(c2.unwrap().name(), "Disconnected");
break;
} else {
assert!(matches![c2, None]);
assert_eq!(data.peer, Some(Addr::new("M0THC-2")?));
assert_all(
&[ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: true,
command_response_la: false,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Sabm(Sabm { poll: true }),
})],
&events,
"connect",
);
}
}
Ok(())
}
#[test]
fn disconnected_incoming() -> Result<()> {
let mut data = Data::new(Addr::new("M0THC-1")?);
data.able_to_establish = true; let con = Disconnected::new();
let (con, events) = handle(
&con,
&mut data,
&Event::Sabm(Sabm { poll: true }, Addr::new("M0THC-2")?),
);
let con = con.unwrap();
assert_eq!(con.name(), "Connected");
assert_eq!(data.peer, Some(Addr::new("M0THC-2")?));
assert_all(
&[ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Ua(Ua { poll: true }),
})],
&events,
"connect",
);
Ok(())
}
#[test]
fn connected() -> Result<()> {
let mut data = Data::new(Addr::new("M0THC-1")?);
data.peer = Some(Addr::new("M0THC-2")?);
let con = Connected::new(ConnectedState::Connected);
eprintln!("Receive data packet");
let (c2, events) = handle(
&con,
&mut data,
&Event::Iframe(
Iframe {
nr: 0,
ns: 0,
poll: true, pid: 0xF0,
payload: vec![1, 2, 3],
},
true,
),
);
assert!(matches![c2, None]);
assert_all(
&[
ReturnEvent::Data(Res::Some(vec![1, 2, 3])),
ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rr(Rr { poll: true, nr: 1 }),
}),
],
&events,
"iframe",
);
eprintln!("Receive repeated packet");
let (c2, events) = handle(
&con,
&mut data,
&Event::Iframe(
Iframe {
nr: 0,
ns: 0,
poll: true, pid: 0xF0,
payload: vec![1, 2, 3],
},
true,
),
);
assert!(matches![c2, None]);
assert_all(
&[ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rej(Rej { poll: true, nr: 1 }),
})],
&events,
"iframe",
);
eprintln!("Receive next packet");
let (c2, events) = handle(
&con,
&mut data,
&Event::Iframe(
Iframe {
nr: 0,
ns: 1,
poll: true, pid: 0xF0,
payload: vec![11, 22, 33],
},
true,
),
);
assert!(matches![c2, None]);
assert_all(
&[
ReturnEvent::Data(Res::Some(vec![11, 22, 33])),
ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Rr(Rr { poll: true, nr: 2 }),
}),
],
&events,
"iframe",
);
Ok(())
}
#[test]
fn disconnect() -> Result<()> {
let mut data = Data::new(Addr::new("M0THC-1")?);
data.peer = Some(Addr::new("M0THC-2")?);
let con = Connected::new(ConnectedState::Connected);
let (c2, events) = handle(&con, &mut data, &Event::Disc(Disc { poll: true }));
assert_eq!(c2.unwrap().name(), "Disconnected");
assert_all(
&[
ReturnEvent::Data(Res::EOF),
ReturnEvent::Packet(Packet {
src: Addr::new("M0THC-1")?,
dst: Addr::new("M0THC-2")?,
command_response: false,
command_response_la: true,
digipeater: vec![],
rr_dist1: false,
rr_extseq: false,
packet_type: PacketType::Ua(Ua { poll: true }),
}),
],
&events,
"disconnect",
);
Ok(())
}
}