Skip to main content

atmos/kernel/net/
reassembly.rs

1//! TCP 受信側の順序外セグメント再構成バッファ。
2//!
3//! 仕様・実測データ・優先順位は `spec/tcp_receive.md` を参照。
4//! 典拠: RFC 9293 §3.10.7.4(Segment Receive)。
5//!
6//! ## なぜ必要か
7//! RFC 9293 は順序外セグメントの破棄を**許容**している(キューイングは実装の裁量)。
8//! したがって現行の「破棄 + RCV.NXT を載せた ACK」は RFC 違反ではない。
9//! しかし性能上は致命的で、実測では 660 セグメント中 33 個(5%)を破棄し、
10//! ソケットが ESTABLISHED のまま `rx_buf=0` で停止していた。
11//! 1 個の欠落で以降の到着分をすべて捨て、相手の再送を待つためである。
12//!
13//! ここに保持しておけば、欠けた 1 個が再送で埋まった瞬間に
14//! まとめて上位へ渡せる。
15//!
16//! グローバル状態にもハードウェアにも依存しない純粋ロジックのみで構成する。
17
18extern crate alloc;
19
20use alloc::vec::Vec;
21
22/// 保持できる順序外セグメントの最大数。超えた分は破棄する
23/// (破棄しても RFC 上は正しく、相手の再送で回復する)。
24pub const MAX_SEGMENTS: usize = 16;
25
26/// 保持できる合計バイト数の上限。メモリ枯渇を防ぐ。
27pub const MAX_BYTES: usize = 64 * 1024;
28
29/// 挿入結果。
30#[derive(Debug, PartialEq, Eq, Clone, Copy)]
31pub enum InsertOutcome {
32    /// 保持した。
33    Stored,
34    /// 既に同じ seq を保持しているので無視した(重複到着)。
35    Duplicate,
36    /// 容量超過のため破棄した。相手の再送に委ねる。
37    Dropped,
38}
39
40/// 引数が不正だった場合のエラー。
41#[derive(Debug, PartialEq, Eq, Clone, Copy)]
42pub enum ReassemblyError {
43    /// 空ペイロードを渡された。順序外バッファに入れる意味が無い。
44    EmptyPayload,
45}
46
47/// 順序外セグメントを seq 昇順で保持する。
48#[derive(Debug, Default)]
49pub struct Reassembler {
50    /// (seq, data) を seq 昇順で保持する。
51    segs: Vec<(u32, Vec<u8>)>,
52    bytes: usize,
53}
54
55impl Reassembler {
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// `static` 配列の初期化用(`const` 文脈で使える)。
61    pub const fn new_const() -> Self {
62        Self {
63            segs: Vec::new(),
64            bytes: 0,
65        }
66    }
67
68    /// 保持中のセグメント数。
69    pub fn len(&self) -> usize {
70        self.segs.len()
71    }
72
73    pub fn is_empty(&self) -> bool {
74        self.segs.is_empty()
75    }
76
77    /// 保持中の合計バイト数。
78    pub fn bytes(&self) -> usize {
79        self.bytes
80    }
81
82    /// 順序外セグメントを保持する。
83    ///
84    /// 空ペイロードは黙って戻らず、エラーログを出して `Err` を返す
85    /// (呼び出し側のバグを握り潰さないため)。
86    pub fn insert(&mut self, seq: u32, data: &[u8]) -> Result<InsertOutcome, ReassemblyError> {
87        if data.is_empty() {
88            crate::error!("[TCP][REASM] 空ペイロードは保持できません seq={}", seq);
89            return Err(ReassemblyError::EmptyPayload);
90        }
91        if self.segs.iter().any(|(s, _)| *s == seq) {
92            return Ok(InsertOutcome::Duplicate);
93        }
94        if self.segs.len() >= MAX_SEGMENTS || self.bytes + data.len() > MAX_BYTES {
95            return Ok(InsertOutcome::Dropped);
96        }
97        // seq 昇順を保って挿入する(後続の連結取り出しを単純にするため)。
98        let pos = self
99            .segs
100            .iter()
101            .position(|(s, _)| *s > seq)
102            .unwrap_or(self.segs.len());
103        self.segs.insert(pos, (seq, data.to_vec()));
104        self.bytes += data.len();
105        Ok(InsertOutcome::Stored)
106    }
107
108    /// `rcv_nxt` から連続する分を取り出し、新しい `rcv_nxt` とデータを返す。
109    ///
110    /// 欠落が埋まった瞬間に、そこから繋がる全セグメントをまとめて返す。
111    /// 連続していなければ空を返し `rcv_nxt` は据え置く。
112    pub fn pop_contiguous(&mut self, rcv_nxt: u32) -> (u32, Vec<u8>) {
113        let mut next = rcv_nxt;
114        let mut out: Vec<u8> = Vec::new();
115        loop {
116            let Some(idx) = self.segs.iter().position(|(s, _)| *s == next) else {
117                break;
118            };
119            let (_, data) = self.segs.remove(idx);
120            next = next.wrapping_add(data.len() as u32);
121            self.bytes -= data.len();
122            out.extend_from_slice(&data);
123        }
124        (next, out)
125    }
126
127    /// `rcv_nxt` より前(既に受信済み)のセグメントを捨て、捨てた数を返す。
128    /// 再送で重複到着したものが溜まり続けるのを防ぐ。
129    pub fn drop_before(&mut self, rcv_nxt: u32) -> usize {
130        let before = self.segs.len();
131        let mut freed = 0usize;
132        self.segs.retain(|(s, d)| {
133            // 折り返しを考慮し、rcv_nxt から見て「過去」なら捨てる。
134            let is_past = rcv_nxt.wrapping_sub(*s) < 0x8000_0000
135                && rcv_nxt.wrapping_sub(*s) >= d.len() as u32;
136            if is_past {
137                freed += d.len();
138            }
139            !is_past
140        });
141        self.bytes -= freed;
142        before - self.segs.len()
143    }
144
145    /// 全破棄(接続終了時など)。
146    pub fn clear(&mut self) {
147        self.segs.clear();
148        self.bytes = 0;
149    }
150}