Skip to main content

iscsi_client_rs/models/nop/
response.rs

1//! This module defines the structures for iSCSI NOP-In PDUs.
2//! It includes the `NopInResponse` header and related methods for handling ping
3//! responses.
4
5// SPDX-License-Identifier: AGPL-3.0-or-later
6// Copyright (C) 2012-2025 Andrei Maltsev
7
8use anyhow::{Result, anyhow, bail};
9use tracing::warn;
10use zerocopy::{
11    BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32,
12};
13
14use crate::{
15    client::pdu_connection::FromBytes,
16    models::{
17        common::{
18            BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, LogicalUnitNumber,
19            SendingData, TargetTaskTag,
20        },
21        data_fromat::ZeroCopyType,
22        opcode::{BhsOpcode, Opcode, RawBhsOpcode},
23    },
24};
25
26/// Represents the Basic Header Segment (BHS) for a NOP-In PDU.
27#[repr(C)]
28#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
29pub struct NopInResponse {
30    pub opcode: RawBhsOpcode,                 // 0
31    reserved1: [u8; 3],                       // 1..4
32    pub total_ahs_length: u8,                 // 4
33    pub data_segment_length: [u8; 3],         // 5..8
34    pub lun: LogicalUnitNumber,               // 8..16
35    pub initiator_task_tag: InitiatorTaskTag, // 16..20
36    pub target_task_tag: TargetTaskTag,       // 20..24
37    pub stat_sn: U32<BigEndian>,              // 24..28
38    pub exp_cmd_sn: U32<BigEndian>,           // 28..32
39    pub max_cmd_sn: U32<BigEndian>,           // 32..36
40    reserved2: [u8; 12],                      // 36..48
41}
42
43impl NopInResponse {
44    /// Serializes the BHS into a byte buffer.
45    pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
46        if buf.len() != HEADER_LEN {
47            bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
48        }
49        buf.copy_from_slice(self.as_bytes());
50        Ok(())
51    }
52
53    /// Deserializes the BHS from a byte buffer.
54    pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
55        let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
56            .map_err(|e| anyhow!("failed convert buffer NopInResponse: {e}"))?;
57        if hdr.opcode.opcode_known() != Some(Opcode::NopIn) {
58            bail!(
59                "NopInResponse: invalid opcode 0x{:02x}",
60                hdr.opcode.opcode_raw()
61            );
62        }
63        Ok(hdr)
64    }
65}
66
67impl SendingData for NopInResponse {
68    fn get_final_bit(&self) -> bool {
69        true
70    }
71
72    fn set_final_bit(&mut self) {
73        warn!("NopIn Response cannot be marked as Final");
74    }
75
76    fn get_continue_bit(&self) -> bool {
77        false
78    }
79
80    fn set_continue_bit(&mut self) {
81        warn!("NopIn Response cannot be marked as Contine");
82    }
83}
84
85impl FromBytes for NopInResponse {
86    fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
87        NopInResponse::from_bhs_bytes(bytes)
88    }
89}
90
91impl BasicHeaderSegment for NopInResponse {
92    #[inline]
93    fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
94        self.to_bhs_bytes(buf)
95    }
96
97    #[inline]
98    fn get_opcode(&self) -> Result<BhsOpcode> {
99        BhsOpcode::try_from(self.opcode.raw())
100    }
101
102    #[inline]
103    fn get_initiator_task_tag(&self) -> u32 {
104        self.initiator_task_tag.get()
105    }
106
107    #[inline]
108    fn get_ahs_length_bytes(&self) -> usize {
109        (self.total_ahs_length as usize) * 4
110    }
111
112    #[inline]
113    fn set_ahs_length_bytes(&mut self, len: u8) {
114        self.total_ahs_length = len >> 2;
115    }
116
117    #[inline]
118    fn get_data_length_bytes(&self) -> usize {
119        u32::from_be_bytes([
120            0,
121            self.data_segment_length[0],
122            self.data_segment_length[1],
123            self.data_segment_length[2],
124        ]) as usize
125    }
126
127    #[inline]
128    fn set_data_length_bytes(&mut self, len: u32) {
129        let be = len.to_be_bytes();
130        self.data_segment_length = [be[1], be[2], be[3]];
131    }
132}
133
134impl ZeroCopyType for NopInResponse {}