Skip to main content

iscsi_client_rs/models/login/
response.rs

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