Skip to main content

iscsi_client_rs/models/
common.rs

1//! This module defines common traits and constants for iSCSI Protocol Data
2//! Units (PDUs). It includes traits for handling data transmission, managing
3//! the Basic Header Segment (BHS), and building PDUs with data segments.
4
5// SPDX-License-Identifier: AGPL-3.0-or-later
6// Copyright (C) 2012-2025 Andrei Maltsev
7
8use anyhow::Result;
9use enum_dispatch::enum_dispatch;
10use zerocopy::{BigEndian, U32, U64};
11
12use crate::models::opcode::BhsOpcode;
13
14/// The fixed length of the Basic Header Segment (BHS) in bytes.
15pub const HEADER_LEN: usize = 48;
16
17pub type InitiatorTaskTag = U32<BigEndian>;
18pub type TargetTaskTag = U32<BigEndian>;
19pub type LogicalUnitNumber = U64<BigEndian>;
20
21/// Common helper-trait for PDUs that may be fragmented into several
22/// wire-frames (RFC 7143 ― “F”/“C” bits).
23///
24/// *Most* iSCSI PDUs are transferred in a single frame, but a few
25/// (Text, Login, SCSI Command/Data, …) allow the sender to split the
26/// **Data-Segment** into a sequence of chunks whose order is determined
27/// by the transport; the target relies only on the *Continue* and *Final*
28/// flags found in byte 1 of every Basic-Header-Segment.
29///
30/// Trait for PDU types that support data transmission control flags
31///
32/// Implementing `SendingData` lets generic helpers (e.g. the
33/// `PDUWithData` builder or the `Connection` read-loop) toggle and query
34/// those flags **without** knowing the concrete PDU type.
35/// Provides methods to manage Final (F) and Continue (C) bits used in multi-PDU
36/// sequences.
37#[enum_dispatch]
38pub trait SendingData: Sized {
39    /// Return the current state of the **Final (F)** bit.
40    fn get_final_bit(&self) -> bool;
41
42    /// Force **F = 1** (and, if your PDU has it, clear **C**).
43    fn set_final_bit(&mut self);
44
45    /// Return the current state of the **Continue (C)** bit.
46    fn get_continue_bit(&self) -> bool;
47
48    /// Force **C = 1** (and clear **F**).
49    fn set_continue_bit(&mut self);
50}
51
52/// Common functionality for any iSCSI PDU Basic Header Segment (BHS)
53///
54/// A BHS is always 48 bytes long according to RFC 7143; higher‐level PDUs then
55/// may carry additional AHS sections, a variable-length DataSegment,
56/// and optional digests. This trait encapsulates:
57/// 1. extracting lengths out of the BHS,
58/// 2. appending to the DataSegment,
59/// 3. and finally building the full wire format.
60///
61/// All iSCSI PDU types must implement this trait to provide basic header
62/// operations.
63#[enum_dispatch]
64pub trait BasicHeaderSegment: Sized + SendingData {
65    fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()>;
66
67    /// first u8 of BHS
68    fn get_opcode(&self) -> Result<BhsOpcode>;
69
70    /// Expose Initiator Task Tag of this PDU
71    fn get_initiator_task_tag(&self) -> u32;
72
73    /// Number of extra AHS bytes (always a multiple of 4).
74    fn get_ahs_length_bytes(&self) -> usize;
75
76    /// Number of extra AHS bytes (always a multiple of 4).
77    fn set_ahs_length_bytes(&mut self, len: u8);
78
79    /// Get number of actual payload bytes in the DataSegment.
80    fn get_data_length_bytes(&self) -> usize;
81
82    /// Set number of actual payload bytes in the DataSegment.
83    fn set_data_length_bytes(&mut self, len: u32);
84
85    /// Number of actual payload bytes in the DataSegment.
86    #[inline]
87    fn total_length_bytes(&self) -> usize {
88        let padding_ahs = (4 - (self.get_ahs_length_bytes() % 4)) % 4;
89        let padding_data_segment = (4 - (self.get_data_length_bytes() % 4)) % 4;
90
91        HEADER_LEN
92            + self.get_ahs_length_bytes()
93            + padding_ahs
94            + self.get_data_length_bytes()
95            + padding_data_segment
96    }
97
98    #[inline]
99    fn get_header_diggest(&self, enable_header_digest: bool) -> usize {
100        4 * enable_header_digest as usize
101    }
102
103    #[inline]
104    fn get_data_diggest(&self, enable_data_digest: bool) -> usize {
105        4 * (self.get_data_length_bytes() > 0) as usize * enable_data_digest as usize
106    }
107}
108
109// Forward SendingData to &mut T
110impl<T: SendingData> SendingData for &mut T {
111    #[inline]
112    fn get_final_bit(&self) -> bool {
113        (**self).get_final_bit()
114    }
115
116    #[inline]
117    fn set_final_bit(&mut self) {
118        (**self).set_final_bit()
119    }
120
121    #[inline]
122    fn get_continue_bit(&self) -> bool {
123        (**self).get_continue_bit()
124    }
125
126    #[inline]
127    fn set_continue_bit(&mut self) {
128        (**self).set_continue_bit()
129    }
130}
131
132// Forward BasicHeaderSegment to &mut T
133impl<T: BasicHeaderSegment> BasicHeaderSegment for &mut T {
134    #[inline]
135    fn to_bhs_bytes(&self, buf: &mut [u8]) -> anyhow::Result<()> {
136        (**self).to_bhs_bytes(buf)
137    }
138
139    #[inline]
140    fn get_opcode(&self) -> anyhow::Result<BhsOpcode> {
141        (**self).get_opcode()
142    }
143
144    #[inline]
145    fn get_initiator_task_tag(&self) -> u32 {
146        (**self).get_initiator_task_tag()
147    }
148
149    #[inline]
150    fn get_ahs_length_bytes(&self) -> usize {
151        (**self).get_ahs_length_bytes()
152    }
153
154    #[inline]
155    fn set_ahs_length_bytes(&mut self, len: u8) {
156        (**self).set_ahs_length_bytes(len)
157    }
158
159    #[inline]
160    fn get_data_length_bytes(&self) -> usize {
161        (**self).get_data_length_bytes()
162    }
163
164    #[inline]
165    fn set_data_length_bytes(&mut self, len: u32) {
166        (**self).set_data_length_bytes(len)
167    }
168
169    #[inline]
170    fn total_length_bytes(&self) -> usize {
171        (**self).total_length_bytes()
172    }
173
174    #[inline]
175    fn get_header_diggest(&self, en: bool) -> usize {
176        (**self).get_header_diggest(en)
177    }
178
179    #[inline]
180    fn get_data_diggest(&self, en: bool) -> usize {
181        (**self).get_data_diggest(en)
182    }
183}
184
185/// A helper-trait for **builder objects** that construct a complete
186/// iSCSI PDU: a 48-byte Basic-Header-Segment (BHS) plus the optional
187/// **Data-Segment** and digests.
188///
189/// The concrete type that implements `Builder` usually owns a
190/// *(header + payload)* pair and offers additional, PDU-specific setter
191/// methods (e.g. `.lun( … )`, `.read()`, …).
192///
193/// When your application is ready to send the packet you call
194/// [`Builder::build`]; the helper splits the payload into chunks that
195/// respect *MaxRecvDataSegmentLength* and automatically toggles the
196/// **F/C** bits on the header copies.
197/// Trait for building iSCSI PDUs with progressive data assembly.
198///
199/// Provides functionality to construct PDU frames by appending data segments
200/// and managing digest calculations. Implementations handle the wire format
201/// serialization with proper padding and digest generation.
202pub trait Builder: Sized {
203    /// The concrete buffer type used to return the encoded header.
204    type Header: AsRef<[u8]>;
205    type Body: AsRef<[u8]>;
206
207    /// Append raw bytes to the **Data-Segment** and update the
208    /// `DataSegmentLength` field inside the owned header.
209    fn append_data(&mut self, more: &[u8]);
210
211    /// Finish the builder and produce one or more ready-to-send
212    /// `(header_bytes, data_bytes)` frames.
213    ///
214    /// The `cfg` parameter is typically used to honour negotiated session
215    /// limits such as *MaxRecvDataSegmentLength*.
216    fn build(
217        &mut self,
218        max_recv_data_segment_length: usize,
219    ) -> Result<(Self::Header, Self::Body)>;
220}