Skip to main content

atmos/kernel/net/
tcp.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3
4pub fn tcp_socket_lines() -> Vec<String> {
5    let mut lines = Vec::new();
6    unsafe {
7        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
8            let s = &super::TCP_SOCKETS[i];
9            if !s.valid {
10                continue;
11            }
12            lines.push(alloc::format!(
13                "lport={} -> {}:{} state={} snd_nxt={} rcv_nxt={} rx_buf={}",
14                s.local_port,
15                super::format_ipv4(s.remote_ip),
16                s.remote_port,
17                tcp_state_name(s.state),
18                s.snd_nxt,
19                s.rcv_nxt,
20                s.rx_buffer.len()
21            ));
22        }
23    }
24    lines
25}
26
27pub fn tcp_connect_real(remote_ip: [u8; 4], remote_port: u16) -> Result<u16, &'static str> {
28    let _p = crate::os_lib::web_engine::perf::start();
29    let r = tcp_connect_real_measured(remote_ip, remote_port);
30    crate::os_lib::web_engine::perf::add(
31        crate::os_lib::web_engine::perf::Slot::TcpConnect,
32        _p,
33    );
34    r
35}
36
37fn tcp_connect_real_measured(remote_ip: [u8; 4], remote_port: u16) -> Result<u16, &'static str> {
38    if remote_port == 0 {
39        return Err("invalid remote port");
40    }
41
42    let local_port = {
43        let _lock = super::NET_LOCK.lock();
44        unsafe {
45            // 【2026-08-31 バグ修正】候補ポートが既存の生きたソケットと
46            // 衝突していないかを確認してから払い出す(`spec/TODO.md` の
47            // 「エフェメラルポート未検証の設計ギャップ」参照)。
48            let picked = super::tcp_state::next_ephemeral_port(
49                super::TCP_EPHEMERAL_PORT,
50                |port| {
51                    (0..super::TCP_SOCKET_TABLE_SIZE)
52                        .any(|i| super::TCP_SOCKETS[i].valid && super::TCP_SOCKETS[i].local_port == port)
53                },
54            );
55            // `None` はテーブルが空いているのに全ポートが使用中という
56            // 呼び出し側の不整合を意味する。実際には起こらないはずだが、
57            // 取りこぼすより「衝突未確認のまま払い出す」従来動作へ
58            // フォールバックする(接続自体は試みる。ソケット確保の段で
59            // テーブル満杯なら別途エラーになる)。
60            let p = picked.unwrap_or(super::TCP_EPHEMERAL_PORT);
61            super::TCP_EPHEMERAL_PORT = p.wrapping_add(1);
62            if super::TCP_EPHEMERAL_PORT < 40000 {
63                super::TCP_EPHEMERAL_PORT = 40000;
64            }
65            p
66        }
67    };
68
69    let idx = {
70        let _lock = super::NET_LOCK.lock();
71        tcp_alloc_socket(local_port, remote_ip, remote_port)?
72    };
73
74    let mut syn_pkt = [0u8; 128];
75    let syn_len = {
76        let _lock = super::NET_LOCK.lock();
77        unsafe {
78            super::TCP_SOCKETS[idx].state = super::TcpState::SynSent;
79            super::TCP_SOCKETS[idx].snd_nxt = 1000;
80        }
81        build_tcp_ipv4(
82            super::status().ip,
83            local_port,
84            remote_ip,
85            remote_port,
86            1000,
87            0,
88            0x02, // SYN
89            &[],
90            &mut syn_pkt,
91        )?
92    };
93    let my_ip = super::status().ip;
94    crate::info!(
95        "TCP CONNECT: {}.{}.{}.{} -> {}.{}.{}.{}:{} (SYN, src_ip valid={})",
96        my_ip[0],
97        my_ip[1],
98        my_ip[2],
99        my_ip[3],
100        remote_ip[0],
101        remote_ip[1],
102        remote_ip[2],
103        remote_ip[3],
104        remote_port,
105        my_ip != [0, 0, 0, 0]
106    );
107    // 【2026-07-28】SYN の送出失敗を握り潰すと、1 バイトも送れていないのに
108    // 6 秒待ってから "tcp connect timeout" になり、本当の原因(送信キュー満杯・
109    // ARP 未解決・パケット長超過)が呼び出し側に一切伝わらなかった。
110    // 初回 SYN が送れないなら接続は成立しえないので、スロットを解放して
111    // ただちにエラーを返す。
112    if let Err(e) = super::arp::transmit_nic_ipv4_packet(&syn_pkt[..syn_len]) {
113        crate::warn!("[NET] TCP CONNECT: SYN transmit failed: {}", e);
114        let _lock = super::NET_LOCK.lock();
115        unsafe {
116            super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
117            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
118            // スロットを使う別接続へ他人のデータが混入する。
119            REASSEMBLERS[idx].clear();
120        }
121        return Err(e);
122    }
123
124    // 【2026-07-25】QEMU/SLIRP経由で実インターネット上のホスト
125    // (fonts.gstatic.com/cdnjs.cloudflare.com等)へ接続する場合、
126    // 3秒では正当なコネクションでも確立前にタイムアウトすることが
127    // 実機ログで頻発していた("tcp connect timeout")。SYN再送は
128    // 引き続き500ms間隔で行うため、単に猶予を延ばすだけの低リスクな
129    // 変更として6秒に緩和する。
130    let start_time = crate::kernel::timer::get_system_time_ms();
131    let mut last_retx = start_time;
132    while crate::kernel::timer::get_system_time_ms() - start_time < 6000 {
133        let state = {
134            let _lock = super::NET_LOCK.lock();
135            unsafe { super::TCP_SOCKETS[idx].state }
136        };
137        if state == super::TcpState::Established {
138            crate::info!("[NET] TCP CONNECT: established on local_port={}", local_port);
139            return Ok(local_port);
140        }
141        if state == super::TcpState::Closed {
142            // 【2026-07-25 バグ修正】この分岐は直下のタイムアウト分岐と異なり
143            // `TCP_SOCKETS[idx] = TcpSocket::empty()`を呼ばずに即returnしていた
144            // ため、RST受信(接続拒否)のたびにソケットスロットが恒久的に
145            // リークしていた(`tcp_alloc_socket`は`valid`フラグでしか空き
146            // スロットを判定しないため)。`TCP_SOCKET_TABLE_SIZE`は32のみで、
147            // 同一ブート内で接続を重ねるほど失敗しやすくなる既知の現象
148            // (spec/TODO.md記載)の一因である可能性が高い。
149            let _lock = super::NET_LOCK.lock();
150            unsafe {
151                super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
152            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
153            // スロットを使う別接続へ他人のデータが混入する。
154            REASSEMBLERS[idx].clear();
155            }
156            return Err("tcp connection refused (RST received)");
157        }
158        let now = crate::kernel::timer::get_system_time_ms();
159        if now - last_retx >= 500 {
160            // 再送の失敗は致命ではない(次の再送で回復しうる)が、黙って
161            // 捨てると「再送しているのに届かない」のか「そもそも送れて
162            // いない」のか区別できなくなるため必ず記録する。
163            if let Err(e) = super::arp::transmit_nic_ipv4_packet(&syn_pkt[..syn_len]) {
164                crate::warn!("[NET] TCP CONNECT: SYN retransmit failed: {}", e);
165            }
166            last_retx = now;
167        }
168        // フリーズ対策: スリープで完全に待つのではなく sleep(1) で
169        // パケット回収を待ちつつ、他タスクへ CPU を完全に譲る。
170        crate::kernel::scheduler::sleep(1);
171    }
172
173    {
174        let _lock = super::NET_LOCK.lock();
175        unsafe {
176            super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
177            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
178            // スロットを使う別接続へ他人のデータが混入する。
179            REASSEMBLERS[idx].clear();
180        }
181    }
182    // 【2026-07-29診断】実サイトで「HTML は取れたのに直後の CSS 取得だけ
183    // SYN 無応答」になる件の切り分け。SYN 再送回数・宛先・使用ポート・
184    // ARP 解決状況を実測する(推測で直さないため)。
185    crate::warn!(
186        "[NET][SYNDIAG] connect timeout dst={}.{}.{}.{}:{} local_port={}",
187        remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3],
188        remote_port,
189        local_port
190    );
191    Err("tcp connect timeout")
192}
193
194pub fn tcp_send_real(local_port: u16, data: &[u8]) -> Result<(), &'static str> {
195    let _lock = super::NET_LOCK.lock();
196    let idx = tcp_find_socket_by_local(local_port).ok_or("socket not found")?;
197    let (remote_ip, remote_port, seq, ack) = unsafe {
198        let s = &super::TCP_SOCKETS[idx];
199        if s.state != super::TcpState::Established {
200            return Err("socket not established");
201        }
202        (s.remote_ip, s.remote_port, s.snd_nxt, s.rcv_nxt)
203    };
204
205    let mut pkt = alloc::vec![0u8; 128 + data.len()];
206    let pkt_len = build_tcp_ipv4(
207        super::status().ip,
208        local_port,
209        remote_ip,
210        remote_port,
211        seq,
212        ack,
213        0x18,
214        data,
215        &mut pkt,
216    )?;
217    // 【2026-07-28 重大】ここで送信結果を捨てていたため、送信に失敗しても
218    // `snd_nxt` だけが進み、**相手が受け取っていないバイト列を送信済みとして
219    // 扱う**状態になっていた。以後こちらのシーケンス番号は永久にずれ、
220    // 相手からは無応答に見える(TLS 復号エラー・接続タイムアウトの一因)。
221    // 送れなかったならシーケンス番号を進めず、そのままエラーを返す。
222    if let Err(e) = super::arp::transmit_nic_ipv4_packet(&pkt[..pkt_len]) {
223        crate::warn!(
224            "[NET] TCP SEND failed on local_port={} len={}: {}",
225            local_port,
226            data.len(),
227            e
228        );
229        return Err(e);
230    }
231    unsafe {
232        super::TCP_SOCKETS[idx].snd_nxt = super::TCP_SOCKETS[idx]
233            .snd_nxt
234            .wrapping_add(data.len() as u32);
235    }
236    Ok(())
237}
238
239/// 【2026-07-24追加】`tcp_send_real`が呼ばれる度に無条件で`snd_nxt`を進めて
240/// しまうため、同一データを"再送"する目的で`tcp_send_real`を再度呼ぶと、
241/// 実際には既に進んだ(誤った)シーケンス番号で送信してしまい、相手側の
242/// TCP/TLSストリームを破壊するバグを発見した(`kernel/tls.rs`の
243/// `TcpIoWrapper`書き込み再送機構で使用)。この関数は`snd_nxt`を一切
244/// 変更せず、呼び出し元が明示的に指定した`seq`のみで再送パケットを
245/// 組み立てて送出する(正しいTCP再送のセマンティクス)。
246pub fn tcp_retransmit_real(local_port: u16, data: &[u8], seq: u32) -> Result<(), &'static str> {
247    let _lock = super::NET_LOCK.lock();
248    let idx = tcp_find_socket_by_local(local_port).ok_or("socket not found")?;
249    let (remote_ip, remote_port, ack) = unsafe {
250        let s = &super::TCP_SOCKETS[idx];
251        if s.state != super::TcpState::Established {
252            return Err("socket not established");
253        }
254        (s.remote_ip, s.remote_port, s.rcv_nxt)
255    };
256
257    let mut pkt = alloc::vec![0u8; 128 + data.len()];
258    let pkt_len = build_tcp_ipv4(
259        super::status().ip,
260        local_port,
261        remote_ip,
262        remote_port,
263        seq,
264        ack,
265        0x18,
266        data,
267        &mut pkt,
268    )?;
269    // 再送も送出できなければ「再送した」と言ってはいけない。
270    if let Err(e) = super::arp::transmit_nic_ipv4_packet(&pkt[..pkt_len]) {
271        crate::warn!(
272            "[NET] TCP RETRANSMIT failed on local_port={} seq={}: {}",
273            local_port,
274            seq,
275            e
276        );
277        return Err(e);
278    }
279    Ok(())
280}
281
282/// 現在の`snd_nxt`(次に使われる送信シーケンス番号)を取得する。再送時に
283/// 「このデータを送った時点でのシーケンス番号」を正しく再利用するため、
284/// 呼び出し側が`tcp_send_real`の直前に取得しておく用途。
285pub fn tcp_current_snd_seq(local_port: u16) -> Option<u32> {
286    let _lock = super::NET_LOCK.lock();
287    unsafe {
288        tcp_find_socket_by_local(local_port).map(|idx| super::TCP_SOCKETS[idx].snd_nxt)
289    }
290}
291
292pub fn tcp_recv_real(
293    local_port: u16,
294    timeout_iters: usize,
295) -> Result<alloc::vec::Vec<u8>, &'static str> {
296    let idx = tcp_find_socket_by_local(local_port).ok_or("socket not found")?;
297
298    let max_timeout_ms = if timeout_iters <= 1 {
299        0
300    } else {
301        (timeout_iters as u32 * 5).min(5000)
302    };
303
304    let start_time = crate::kernel::timer::get_system_time_ms();
305    loop {
306        // 【2026-08-05 計測】受信は割り込みではなくここのポーリングで駆動される。
307        // 回数を数えれば「待っている」のか「処理が重い」のかが決まる。
308        // 目的: 取得が律速である理由の特定。破棄条件: 特定でき次第削除。
309        RECV_POLLS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
310        crate::kernel::usb::poll_ethernet_data_plane_only();
311
312        let buf_opt = {
313            let _lock = super::NET_LOCK.lock();
314            unsafe {
315                if !super::TCP_SOCKETS[idx].rx_buffer.is_empty()
316                    || super::TCP_SOCKETS[idx].state == super::TcpState::CloseWait
317                    || super::TCP_SOCKETS[idx].fin_received
318                {
319                    // 【2026-08-05】`clone()` してから `clear()` していた。
320                    // 中身は**そのまま呼び出し元へ渡すだけ**なので、
321                    // 複製する必要がない。受信バッファは大きく育ちうる
322                    // (3.7MB の取得中は数十 KB になる)ため、
323                    // 読むたびに全体を memcpy していたことになる。
324                    // `take` なら所有権を移すだけで O(1)。
325                    // 【2026-08-05 検証済み】容量を引き継ぐ案は**10 倍悪化した**。
326                    //
327                    // 「`take` は容量まで持ち去るので、次の周期で 0 から
328                    // 再び育ち再確保が起きる」と考えて
329                    // `mem::replace(.., Vec::with_capacity(前回の容量))` を試したが、
330                    //
331                    // ```text
332                    // take:              tcp_proc_us =  3.66 秒
333                    // with_capacity 版:  tcp_proc_us = 36.22 秒
334                    // ```
335                    //
336                    // 読むたびに最大 64KB を先行確保するため、ヒープが荒れて
337                    // 確保そのものが重くなる。**先行確保は空振りが多いと逆効果**。
338                    // 育つに任せる方が速い。
339                    Some(core::mem::take(&mut super::TCP_SOCKETS[idx].rx_buffer))
340                } else {
341                    None
342                }
343            }
344        };
345
346        if let Some(buf) = buf_opt {
347            return Ok(buf);
348        }
349
350        if max_timeout_ms == 0
351            || crate::kernel::timer::get_system_time_ms() - start_time >= max_timeout_ms as u64
352        {
353            break;
354        }
355        // フリーズ対策: スリープで完全に待つのではなく sleep(1) で
356        // パケット回収を待ちつつ、他タスクへ CPU を完全に譲る。
357        crate::kernel::scheduler::sleep(1);
358    }
359
360    Ok(alloc::vec::Vec::new())
361}
362
363/// 受信バッファに何か(1バイトでも)届いているかを、消費せずに確認する。
364/// 送信直後にごく短時間だけ様子を見て「応答が全く来ていない=パケット
365/// ロストの疑い」を判定するための軽量ヘルパー(2026-07-24)。
366/// 応答の最初の 1 バイトが届くまで待つ(上限つき)。
367///
368/// 【2026-08-05】各所に「送信したのでサーバ処理を待つ」という
369/// `sleep(50)`(500ms 固定)が 6 箇所あった。だが
370///
371/// - `sleep(n)` は「ちょうど n」ではなく「最低でも n」で、
372///   実測では 3.7 倍かかる(`spec/resource_loading.md` R-14)。
373///   500ms のつもりが約 1.85 秒
374/// - 直後の受信ループがどのみち待つので、この待ちは**純粋な上乗せ**
375///
376/// 上限は同じままに、**届いたら即座に返る**形にする。
377/// 待ちを実経過で数えるのが要点(指定値の足し算では上限が効かない)。
378///
379/// 計算量: **O(待った tick 数)**。
380pub fn wait_for_first_byte(local_port: u16, max_ticks: usize) {
381    let _p = crate::os_lib::web_engine::perf::start();
382    wait_for_first_byte_inner(local_port, max_ticks);
383    crate::os_lib::web_engine::perf::add(
384        crate::os_lib::web_engine::perf::Slot::FirstByte,
385        _p,
386    );
387}
388
389fn wait_for_first_byte_inner(local_port: u16, max_ticks: usize) {
390    let start = crate::kernel::timer::get_ticks();
391    while crate::kernel::timer::get_ticks().wrapping_sub(start) < max_ticks {
392        crate::kernel::usb::poll_ethernet_data_plane_only();
393        if tcp_recv_peek_nonempty(local_port) || tcp_is_socket_closed(local_port) {
394            return;
395        }
396        crate::kernel::scheduler::sleep(1);
397    }
398}
399
400pub fn tcp_recv_peek_nonempty(local_port: u16) -> bool {
401    let _lock = super::NET_LOCK.lock();
402    unsafe {
403        if let Some(idx) = tcp_find_socket_by_local(local_port) {
404            !super::TCP_SOCKETS[idx].rx_buffer.is_empty()
405        } else {
406            false
407        }
408    }
409}
410
411pub fn tcp_is_socket_closed(local_port: u16) -> bool {
412    let _lock = super::NET_LOCK.lock();
413    unsafe {
414        if let Some(idx) = tcp_find_socket_by_local(local_port) {
415            let s = &super::TCP_SOCKETS[idx];
416            s.state == super::TcpState::CloseWait
417                || s.state == super::TcpState::Closed
418                || s.state == super::TcpState::TimeWait
419                || s.fin_received
420        } else {
421            true
422        }
423    }
424}
425
426/// ソケットごとの順序外再構成バッファ(`spec/tcp_receive.md`)。
427/// `TCP_SOCKETS` と添字を共有する。
428static mut REASSEMBLERS: [super::reassembly::Reassembler; super::TCP_SOCKET_TABLE_SIZE] =
429    [const { super::reassembly::Reassembler::new_const() }; super::TCP_SOCKET_TABLE_SIZE];
430
431/// 【2026-07-29診断】受信セグメントの受理/棄却カウンタ。
432pub static SEG_ACCEPTED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
433pub static SEG_DROPPED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
434
435/// `tcp_recv_real` がイーサネット受信をポーリングした回数(診断用)。
436///
437/// 【2026-08-05】受信は割り込み駆動ではなくここのポーリングで進む。
438/// 「待っているのか、処理が重いのか」はこの回数で切り分く。
439pub static RECV_POLLS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
440
441// 【2026-08-05】TCP 受信処理の内訳計装は目的を果たしたので撤去した(L-4)。
442// 得られた事実(3.7MB 取得中、13862 セグメント):
443//   tcp_lock_us =  0.08 秒  ← NET_LOCK の取得待ちは律速ではない
444//   tcp_proc_us =  3.22 秒  ← ロック保持中の処理が支配的(1 セグメント 232µs)
445//   tcp_tx_us   =  0.39 秒  ← ACK のキュー投入は軽い
446// ここから `TCP_SOCKETS[idx].clone()` を 2 箇所(受信処理と ACK 組み立て)
447// 見つけて除去した。計装自体も `get_system_time_us` の除算を
448// 1 パケット 6 回呼ぶため熱経路のコストになるので残さない。
449/// 【2026-08-05 検証済み・入れていない】ロック無しの「読むべきものがある」印。
450///
451/// `tcp_recv_real` は毎回 `NET_LOCK` を取って受信バッファが空かを見ており、
452/// 3.7MB の取得中に 12000 回以上通る。受信処理も同じロックを取るため
453/// 競合していると考え、ソケットごとの `AtomicBool` で先に判定する案を試した。
454///
455/// 結果、`sec_ingest_us` は 5.02 → 5.17 秒で**変わらなかった**。
456/// NET_LOCK の競合は律速ではない。新しい同期機構は入れない。
457pub fn tcp_close_real(local_port: u16) -> Result<(), &'static str> {
458    let idx = {
459        let _lock = super::NET_LOCK.lock();
460        tcp_find_socket_by_local(local_port).ok_or("socket not found")?
461    };
462
463    let (remote_ip, remote_port, seq, ack) = {
464        let _lock = super::NET_LOCK.lock();
465        unsafe {
466            let s = &super::TCP_SOCKETS[idx];
467            if s.state != super::TcpState::Established && s.state != super::TcpState::CloseWait {
468                // 【2026-07-24重大バグ発見・修正】以前はここで即座に`Err`を返して
469                // いたため、相手が先にFINを送ってきた(`FinWait1`/`Closed`等へ
470                // 既に遷移済みの)ソケットに対して呼び出し元が`tcp_close_real`を
471                // 呼ぶと、下方の`TCP_SOCKETS[idx] = TcpSocket::empty()`に一切
472                // 到達できず、**そのソケットスロットが二度と再利用されないまま
473                // 永久にリークする**重大なバグだった(`TCP_SOCKET_TABLE_SIZE`は
474                // 8のみのため、失敗した接続が数回続くだけで新規接続が丸ごと
475                // 「tcp socket table full」で失敗するようになる)。相手がFINを
476                // 送信済み等、こちら側から丁寧にクローズシーケンスを行う意味が
477                // 既に無い状態では、グレースフルクローズをスキップして即座に
478                // スロットを解放する。
479                super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
480            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
481            // スロットを使う別接続へ他人のデータが混入する。
482            REASSEMBLERS[idx].clear();
483                return Ok(());
484            }
485            (s.remote_ip, s.remote_port, s.snd_nxt, s.rcv_nxt)
486        }
487    };
488
489    {
490        let _lock = super::NET_LOCK.lock();
491        unsafe {
492            super::TCP_SOCKETS[idx].state = super::TcpState::FinWait1;
493            super::TCP_SOCKETS[idx].snd_nxt = super::TCP_SOCKETS[idx].snd_nxt.wrapping_add(1);
494        }
495    }
496
497    let mut fin_pkt = [0u8; 128];
498    let len = build_tcp_ipv4(
499        super::status().ip,
500        local_port,
501        remote_ip,
502        remote_port,
503        seq,
504        ack,
505        0x11, // FIN-ACK
506        &[],
507        &mut fin_pkt,
508    )?;
509    // FIN が送れなくても後続のスロット解放処理は必要なので即 return はしないが、
510    // 「切断を通知できていない」事実は必ず残す(相手側にはハーフオープンな
511    // 接続が残り、次の接続で不可解な RST を受ける原因になる)。
512    if let Err(e) = super::arp::transmit_nic_ipv4_packet(&fin_pkt[..len]) {
513        crate::warn!(
514            "[NET] TCP CLOSE: FIN transmit failed on local_port={}: {}",
515            local_port,
516            e
517        );
518    }
519
520    {
521        let _lock = super::NET_LOCK.lock();
522        unsafe {
523            super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
524        }
525    }
526
527    let start_time = crate::kernel::timer::get_system_time_ms();
528    while crate::kernel::timer::get_system_time_ms() - start_time < 3000 {
529        crate::kernel::usb::poll_ethernet_data_plane_only();
530
531        let state = {
532            let _lock = super::NET_LOCK.lock();
533            unsafe { super::TCP_SOCKETS[idx].state }
534        };
535
536        if state == super::TcpState::TimeWait || state == super::TcpState::Closed {
537            break;
538        }
539        // フリーズ対策: スリープで完全に待つのではなく yield_now() で
540        // パケット回収を高頻度で継続しつつ、他タスクへ CPU を譲る。
541        crate::kernel::scheduler::yield_now();
542    }
543
544    {
545        let _lock = super::NET_LOCK.lock();
546        unsafe {
547            super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
548            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
549            // スロットを使う別接続へ他人のデータが混入する。
550            REASSEMBLERS[idx].clear();
551        }
552    }
553
554    Ok(())
555}
556
557pub(crate) fn parse_tcp_ipv4_packet(packet: &[u8]) -> Result<(), &'static str> {
558    // 【2026-07-25】長さ検証より先に `packet[0]` を読んでいたため、空スライスで
559    // 呼ぶとインデックス範囲外でパニック(=カーネルクラッシュ)していた。
560    // 現在の唯一の呼び出し元 `parse_ipv4_packet` は事前に 20 バイト以上を保証
561    // しているので実害は出ていないが、境界検証を関数自身の責務として持たせる。
562    if packet.len() < 20 {
563        return Err("tcp packet too short");
564    }
565    let ihl = ((packet[0] & 0x0F) as usize) * 4;
566    if ihl < 20 || packet.len() < ihl + 20 {
567        return Err("tcp packet too short");
568    }
569
570    let total_len = (((packet[2] as u16) << 8) | packet[3] as u16) as usize;
571    if total_len < ihl + 20 || total_len > packet.len() {
572        return Err("invalid ipv4 total length");
573    }
574
575    let tcp = &packet[ihl..total_len];
576    let src_port = ((tcp[0] as u16) << 8) | tcp[1] as u16;
577    let dst_port = ((tcp[2] as u16) << 8) | tcp[3] as u16;
578    let seq =
579        ((tcp[4] as u32) << 24) | ((tcp[5] as u32) << 16) | ((tcp[6] as u32) << 8) | tcp[7] as u32;
580    let ack = ((tcp[8] as u32) << 24)
581        | ((tcp[9] as u32) << 16)
582        | ((tcp[10] as u32) << 8)
583        | tcp[11] as u32;
584    let data_offset = ((tcp[12] >> 4) as usize) * 4;
585    if data_offset < 20 || data_offset > tcp.len() {
586        return Err("invalid tcp data offset");
587    }
588
589    let flags = tcp[13];
590    let payload_len = tcp.len() - data_offset;
591    let src_ip = [packet[12], packet[13], packet[14], packet[15]];
592
593    if dst_port > 1024 {
594        crate::debug!(
595            "TCP RX: src_port={} dst_port={} flags=0x{:02x} payload_len={}",
596            src_port,
597            dst_port,
598            flags,
599            payload_len
600        );
601    }
602
603    // ACK は「ロック内で組み立て、ロック解放後に送信」する。送信経路は ARP
604    // キャッシュミス時に最大 2 秒ブロックし、その間 `poll_ethernet_data_plane_only()`
605    // 経由で本関数が再入されうるため、`NET_LOCK` を保持したまま送信しては
606    // ならない(詳細は `build_tcp_ack_packet` のコメント参照)。
607    let mut ack_pkt = [0u8; 128];
608    let mut ack_len = 0usize;
609
610    {
611        let _lock = super::NET_LOCK.lock();
612        let idx = tcp_find_socket(dst_port, src_ip, src_port).ok_or("tcp socket not found")?;
613
614        unsafe {
615            // 【2026-08-05】ここは `TCP_SOCKETS[idx].clone()` して、
616            // 書き換えてから丸ごと代入で書き戻していた。
617            //
618            // `TcpSocket` は `rx_buffer: Vec<u8>` を内包するので、
619            // **セグメントが 1 個届くたびに受信バッファ全体を複製**していた。
620            // バッファは読み手が引き取るまで育つため、
621            // 「溜まっているほど 1 セグメントが高くつく」という
622            // 二次的な悪化になっていた。
623            //
624            // 実測: `ingest_nic_ethernet_frame` に 1 フレームあたり 789µs
625            // かかっており、フレーム処理時間の 95% を占めていた
626            // (3.7MB の取得で合計 10.7 秒)。
627            //
628            // その場で書き換えれば複製は要らない。
629            // 計算量は O(受信バッファ長) から O(1) へ。
630            let s = &mut super::TCP_SOCKETS[idx];
631
632            // 状態遷移の判断そのものは純粋モジュール `tcp_state` へ委譲する
633            // (ホスト側ユニットテストで異常系まで検証済み。`tests/src/test_tcp_state.rs`)。
634            // ここはグローバル状態・rx_buffer という副作用のみを担当する。
635            let mut rs = super::tcp_state::RecvState {
636                state: s.state,
637                snd_nxt: s.snd_nxt,
638                rcv_nxt: s.rcv_nxt,
639                fin_received: s.fin_received,
640            };
641            let seg = super::tcp_state::Segment {
642                seq,
643                ack,
644                flags,
645                payload_len: payload_len as u32,
646            };
647            let prev_fin = rs.fin_received;
648            let outcome = super::tcp_state::on_segment(&mut rs, &seg);
649            // 【2026-07-29診断】受信が途中で止まる件。届いたセグメントのうち
650            // ペイロードを受理した数/順序外等で捨てた数を数える。
651            if payload_len > 0 {
652                if outcome.accept_payload {
653                    SEG_ACCEPTED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
654                } else {
655                    SEG_DROPPED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
656                }
657            }
658
659            if outcome.accept_payload {
660                s.rx_buffer.extend_from_slice(&tcp[data_offset..]);
661                // 【2026-07-29】欠落が埋まったので、保持していた順序外セグメントの
662                // うち連続する分をまとめて取り込む(`spec/tcp_receive.md` 優先度1)。
663                let reasm = &mut REASSEMBLERS[idx];
664                reasm.drop_before(rs.rcv_nxt);
665                let (new_nxt, more) = reasm.pop_contiguous(rs.rcv_nxt);
666                if !more.is_empty() {
667                    s.rx_buffer.extend_from_slice(&more);
668                    rs.rcv_nxt = new_nxt;
669                }
670            } else if payload_len > 0
671                && (flags & super::tcp_state::FLAG_SYN) == 0
672                && rs.state == super::TcpState::Established
673            {
674                // 順序外セグメント。RFC 9293 は破棄を許容するが、捨てると
675                // 1 個の欠落で以降を全部落として受信が停止する(実測で確認)。
676                // 保持しておき、欠落が再送で埋まった瞬間に繋げる。
677                let _ = REASSEMBLERS[idx].insert(seq, &tcp[data_offset..]);
678            }
679            if (flags & super::tcp_state::FLAG_FIN) != 0 && !prev_fin {
680                crate::warn!(
681                    "[NET][DIAG] FIN seg on local_port={} state={} accepted={} rx_buffer_len={}",
682                    s.local_port,
683                    tcp_state_name(s.state),
684                    rs.fin_received,
685                    s.rx_buffer.len()
686                );
687            }
688
689            s.state = rs.state;
690            s.snd_nxt = rs.snd_nxt;
691            s.rcv_nxt = rs.rcv_nxt;
692            s.fin_received = rs.fin_received;
693
694            if outcome.send_ack {
695                if let Ok(n) = build_tcp_ack_packet(idx, &mut ack_pkt) {
696                    ack_len = n;
697                }
698            }
699
700            super::NET_STATE.rx_packets = super::NET_STATE.rx_packets.saturating_add(1);
701        }
702    } // ← ここで NET_LOCK を解放してから送信する
703
704    if ack_len > 0 {
705        // ACK が送れないと相手は再送を続け、こちらからは「応答が来ない」ように
706        // 見える。受信処理からは呼び出し側へ返す先が無いため、ここでは必ず
707        // ログへ残す(黙って捨てない)。
708        if let Err(e) = super::arp::transmit_nic_ipv4_packet(&ack_pkt[..ack_len]) {
709            crate::warn!("[NET] TCP: ACK transmit failed: {}", e);
710        }
711    }
712
713    Ok(())
714}
715
716/// 指定ソケットへの ACK パケットを **組み立てるだけ** で、送信はしない。
717///
718/// 【2026-07-26 重要】以前はこの関数が `transmit_nic_ipv4_packet()` まで
719/// 行っており、しかも呼び出し元 `parse_tcp_ipv4_packet` が `NET_LOCK` を
720/// 保持したまま呼んでいた。送信経路は ARP キャッシュミス時に
721/// `build_usb_ethernet_ipv4_frame` 内で**最大 2 秒ブロッキング**する
722/// ARP 解決ループへ入り、そのループは内部で
723/// `poll_ethernet_data_plane_only()` を回す。ポーリングは受信パケットを
724/// `parse_tcp_ipv4_packet` / `parse_udp_ipv4_packet` へ流し、これらは
725/// いずれも `NET_LOCK` を取得しようとするため、再入不可の `spin::Mutex`
726/// に対する**自己デッドロック**になりうる。デッドロックに至らなくとも、
727/// 2 秒間 `NET_LOCK` を占有すると DNS 応答待ちループ
728/// (`udp_recv_matching` も同じロックを取る)が自分のキューを確認できず、
729/// `dns response timeout` が誘発される。
730/// そのため組み立てと送信を分離し、送信はロック解放後に行う。
731fn build_tcp_ack_packet(idx: usize, out: &mut [u8]) -> Result<usize, &'static str> {
732    unsafe {
733        // 【2026-08-05】ここも `clone()` していた。
734        //
735        // `parse_tcp_ipv4_packet` と同じ形の問題で、`TcpSocket` は
736        // `rx_buffer: Vec<u8>` を内包するため、**ACK を 1 本組むたびに
737        // 受信バッファ全体を複製**していた。ACK は受信セグメントごとに
738        // 送るので、3.7MB の取得では 13000 回以上通る。
739        //
740        // 読みたいのはポート・IP・シーケンス番号の 5 つだけ。参照で足りる。
741        let s = &super::TCP_SOCKETS[idx];
742        build_tcp_ipv4(
743            super::status().ip,
744            s.local_port,
745            s.remote_ip,
746            s.remote_port,
747            s.snd_nxt,
748            s.rcv_nxt,
749            0x10, // ACK
750            &[],
751            out,
752        )
753    }
754}
755
756fn tcp_alloc_socket(
757    local_port: u16,
758    remote_ip: [u8; 4],
759    remote_port: u16,
760) -> Result<usize, &'static str> {
761    unsafe {
762        // 空きスロットの選定は純粋モジュールへ委譲(満杯時の挙動は
763        // `tests/src/test_tcp_state.rs` で決定論的に検証済み)。
764        let idx = super::tcp_state::find_free_slot(super::TCP_SOCKET_TABLE_SIZE, |i| {
765            super::TCP_SOCKETS[i].valid
766        })?;
767        // 新規割り当て時にも順序外バッファを初期化する(前の接続の残骸混入防止)。
768        REASSEMBLERS[idx].clear();
769        super::TCP_SOCKETS[idx] = super::TcpSocket {
770            valid: true,
771            local_port,
772            remote_ip,
773            remote_port,
774            state: super::TcpState::Closed,
775            snd_nxt: 0,
776            rcv_nxt: 0,
777            rx_buffer: alloc::vec::Vec::new(),
778            fin_received: false,
779        };
780        Ok(idx)
781    }
782}
783
784/// 【試験用】空いているスロットをすべて占有し、占有した数を返す。
785///
786/// 「テーブルが満杯なら `tcp_connect_real` が `Err("tcp socket table full")`
787/// を返す」ことを**決定論的に**確かめるために使う。
788///
789/// これが無かった頃、試験は 40 本の OS スレッドを一斉に起動して
790/// 「SYN タイムアウト(6 秒)で先行分が抜ける前に全員が揃う」ことに賭けていた。
791/// ホスト負荷が高いと起動が遅れて満杯を観測できず、偽陰性で落ちていた。
792/// 確かめたい性質は満杯時の戻り値であって、スレッドが競争に勝てるかではない。
793#[cfg(test)]
794pub fn tcp_fill_socket_table_for_test() -> usize {
795    let _lock = super::NET_LOCK.lock();
796    let mut occupied = 0usize;
797    unsafe {
798        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
799            if super::TCP_SOCKETS[i].valid {
800                continue;
801            }
802            super::TCP_SOCKETS[i] = super::TcpSocket {
803                valid: true,
804                // 実在しない宛先。パケットは送らないので応答も来ない。
805                local_port: 40000 + i as u16,
806                remote_ip: [203, 0, 113, 1],
807                remote_port: 9,
808                state: super::TcpState::Closed,
809                snd_nxt: 0,
810                rcv_nxt: 0,
811                rx_buffer: alloc::vec::Vec::new(),
812                fin_received: false,
813            };
814            occupied += 1;
815        }
816    }
817    occupied
818}
819
820/// 【試験用】ソケットテーブルを丸ごと空に戻す。
821///
822/// `tcp_fill_socket_table_for_test` と対で使い、
823/// グローバル状態を後続の試験へ持ち越さない。
824#[cfg(test)]
825pub fn tcp_clear_socket_table_for_test() {
826    let _lock = super::NET_LOCK.lock();
827    unsafe {
828        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
829            super::TCP_SOCKETS[i] = super::TcpSocket::empty();
830        }
831    }
832}
833
834fn tcp_find_socket(local_port: u16, remote_ip: [u8; 4], remote_port: u16) -> Option<usize> {
835    unsafe {
836        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
837            let s = &super::TCP_SOCKETS[i];
838            if !s.valid {
839                continue;
840            }
841            if s.local_port == local_port
842                && s.remote_port == remote_port
843                && s.remote_ip == remote_ip
844            {
845                return Some(i);
846            }
847        }
848    }
849    None
850}
851
852pub(crate) fn tcp_find_socket_by_local(local_port: u16) -> Option<usize> {
853    unsafe {
854        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
855            if super::TCP_SOCKETS[i].valid && super::TCP_SOCKETS[i].local_port == local_port {
856                return Some(i);
857            }
858        }
859    }
860    None
861}
862
863fn build_tcp_ipv4(
864    src_ip: [u8; 4],
865    src_port: u16,
866    dst_ip: [u8; 4],
867    dst_port: u16,
868    seq: u32,
869    ack: u32,
870    flags: u8,
871    payload: &[u8],
872    out: &mut [u8],
873) -> Result<usize, &'static str> {
874    let mut opt_len = 0;
875    if (flags & 0x02) != 0 {
876        opt_len = 4;
877    }
878    let tcp_hdr_len = 20usize + opt_len;
879    let tcp_len = tcp_hdr_len + payload.len();
880    let total_len = 20usize + tcp_len;
881    if total_len > out.len() {
882        return Err("buffer too small");
883    }
884
885    out[0] = 0x45;
886    out[1] = 0;
887    out[2] = ((total_len >> 8) & 0xFF) as u8;
888    out[3] = (total_len & 0xFF) as u8;
889    out[4] = 0x61;
890    out[5] = 0x22;
891    out[6] = 0;
892    out[7] = 0;
893    out[8] = 64;
894    out[9] = 6;
895    out[10] = 0;
896    out[11] = 0;
897    out[12..16].copy_from_slice(&src_ip);
898    out[16..20].copy_from_slice(&dst_ip);
899
900    let ip_checksum = super::ipv4_header_checksum(&out[0..20]);
901    out[10] = (ip_checksum >> 8) as u8;
902    out[11] = ip_checksum as u8;
903
904    out[20] = (src_port >> 8) as u8;
905    out[21] = src_port as u8;
906    out[22] = (dst_port >> 8) as u8;
907    out[23] = dst_port as u8;
908    out[24] = (seq >> 24) as u8;
909    out[25] = (seq >> 16) as u8;
910    out[26] = (seq >> 8) as u8;
911    out[27] = seq as u8;
912    out[28] = (ack >> 24) as u8;
913    out[29] = (ack >> 16) as u8;
914    out[30] = (ack >> 8) as u8;
915    out[31] = ack as u8;
916    out[32] = ((tcp_hdr_len / 4) << 4) as u8;
917    out[33] = flags;
918    // 【2026-07-24】受信ウィンドウサイズを`0xFFFF`(65535バイト)へ拡大する
919    // 変更を試みたが、それ以降メインHTML取得自体の成功率が悪化したように
920    // 見えたため(一度に大量のデータが届くことでこの簡易TCPスタックの
921    // 受信処理が追いつかなくなる可能性)、元の値(2880)へ差し戻した。
922    // 「より慎重な検証(中間的なサイズでの比較等)が必要」と残されていた。
923    //
924    // 【2026-07-29】測定基盤(`tls.rs` の `stats`)ができ、成功率を
925    // 段階別に数値化できるようになったので中間値で再挑戦する。
926    // 2880(≒2 MSS)は小さすぎ、49KB の応答に 17 往復以上を要して
927    // 損失機会を増やしていた。順序外再構成バッファ(`net/reassembly.rs`)も
928    // 入ったので、一度に多く届いても取りこぼしにくくなっている。
929    // 8192 バイト(≒5.6 MSS)から試す。効果は STATS の
930    // truncated 件数で比較すること(単発の観測で判断しない)。
931    //
932    // 【2026-08-05】画像取得の内訳を測ったところ、3.7MB の JPEG で
933    // 取得 3746 tick に対しデコードは 950 tick だった(`[IMGPERF]`)。
934    // **律速は取得側**で、デコードを速くしても意味が無い。
935    //
936    // 実効 100KB/s 程度。ウィンドウ 8192 バイトだと「飛ばせる量」が
937    // 帯域遅延積で頭打ちになる(RTT 80ms なら 8192/0.08 ≒ 100KB/s で、
938    // 実測とちょうど一致する)。ウィンドウが律速そのもの。
939    //
940    // 【2026-08-05 検証済み・変更せず】32768(0x8000)へ拡大して測ったが、
941    // 3.7MB の取得は 3746 → 3906 tick で**改善しなかった**(誤差範囲)。
942    // 成功率は 16/16・truncated=0 で悪化もしていない。
943    //
944    // つまりウィンドウは律速ではない。飛ばせる量ではなく、
945    // **こちらの受信処理速度**が上限を決めている。
946    // 根拠の無い変更は入れないので 8192 のまま残す。
947    out[34] = 0x20;
948    out[35] = 0x00;
949    out[36] = 0x00;
950    out[37] = 0x00;
951    out[38] = 0x00;
952    out[39] = 0x00;
953
954    let mut payload_start = 40;
955    if opt_len > 0 {
956        out[40] = 2;
957        out[41] = 4;
958        out[42] = 0x05;
959        out[43] = 0xB4;
960        payload_start += 4;
961    }
962    let payload_end = payload_start + payload.len();
963    out[payload_start..payload_end].copy_from_slice(payload);
964
965    let mut tcp_csum_buf = alloc::vec![0u8; 12 + tcp_len];
966    tcp_csum_buf[0..4].copy_from_slice(&src_ip);
967    tcp_csum_buf[4..8].copy_from_slice(&dst_ip);
968    tcp_csum_buf[8] = 0;
969    tcp_csum_buf[9] = 6;
970    tcp_csum_buf[10] = ((tcp_len >> 8) & 0xFF) as u8;
971    tcp_csum_buf[11] = (tcp_len & 0xFF) as u8;
972    tcp_csum_buf[12..12 + tcp_len].copy_from_slice(&out[20..total_len]);
973
974    let csum = super::ipv4_header_checksum(&tcp_csum_buf);
975    out[36] = (csum >> 8) as u8;
976    out[37] = csum as u8;
977
978    let sent = super::arp::transmit_nic_ipv4_packet(&out[..total_len])?;
979    Ok(sent)
980}
981
982fn tcp_state_name(state: super::TcpState) -> &'static str {
983    super::tcp_state::tcp_state_name(state)
984}