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
411/// 【2026-09-26 診断】EOF と判断した理由を 1 行で返す(途中切れの切り分け用)。
412/// 状態・FIN 受信・受信バッファ残量・rcv_nxt と、再構成バッファに残る保持数。
413pub fn tcp_close_diag(local_port: u16) -> alloc::string::String {
414    let _lock = super::NET_LOCK.lock();
415    unsafe {
416        match tcp_find_socket_by_local(local_port) {
417            Some(idx) => {
418                let s = &super::TCP_SOCKETS[idx];
419                alloc::format!(
420                    "state={} fin={} rx_left={} rcv_nxt={} reasm_segs={}",
421                    tcp_state_name(s.state),
422                    s.fin_received,
423                    s.rx_buffer.len(),
424                    s.rcv_nxt,
425                    REASSEMBLERS[idx].len()
426                )
427            }
428            None => alloc::string::String::from("socket-missing"),
429        }
430    }
431}
432
433pub fn tcp_is_socket_closed(local_port: u16) -> bool {
434    let _lock = super::NET_LOCK.lock();
435    unsafe {
436        if let Some(idx) = tcp_find_socket_by_local(local_port) {
437            let s = &super::TCP_SOCKETS[idx];
438            s.state == super::TcpState::CloseWait
439                || s.state == super::TcpState::Closed
440                || s.state == super::TcpState::TimeWait
441                || s.fin_received
442        } else {
443            true
444        }
445    }
446}
447
448/// ソケットごとの順序外再構成バッファ(`spec/tcp_receive.md`)。
449/// `TCP_SOCKETS` と添字を共有する。
450static mut REASSEMBLERS: [super::reassembly::Reassembler; super::TCP_SOCKET_TABLE_SIZE] =
451    [const { super::reassembly::Reassembler::new_const() }; super::TCP_SOCKET_TABLE_SIZE];
452
453/// 【2026-07-29診断】受信セグメントの受理/棄却カウンタ。
454pub static SEG_ACCEPTED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
455pub static SEG_DROPPED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
456
457/// `tcp_recv_real` がイーサネット受信をポーリングした回数(診断用)。
458///
459/// 【2026-08-05】受信は割り込み駆動ではなくここのポーリングで進む。
460/// 「待っているのか、処理が重いのか」はこの回数で切り分く。
461pub static RECV_POLLS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
462
463// 【2026-08-05】TCP 受信処理の内訳計装は目的を果たしたので撤去した(L-4)。
464// 得られた事実(3.7MB 取得中、13862 セグメント):
465//   tcp_lock_us =  0.08 秒  ← NET_LOCK の取得待ちは律速ではない
466//   tcp_proc_us =  3.22 秒  ← ロック保持中の処理が支配的(1 セグメント 232µs)
467//   tcp_tx_us   =  0.39 秒  ← ACK のキュー投入は軽い
468// ここから `TCP_SOCKETS[idx].clone()` を 2 箇所(受信処理と ACK 組み立て)
469// 見つけて除去した。計装自体も `get_system_time_us` の除算を
470// 1 パケット 6 回呼ぶため熱経路のコストになるので残さない。
471/// 【2026-08-05 検証済み・入れていない】ロック無しの「読むべきものがある」印。
472///
473/// `tcp_recv_real` は毎回 `NET_LOCK` を取って受信バッファが空かを見ており、
474/// 3.7MB の取得中に 12000 回以上通る。受信処理も同じロックを取るため
475/// 競合していると考え、ソケットごとの `AtomicBool` で先に判定する案を試した。
476///
477/// 結果、`sec_ingest_us` は 5.02 → 5.17 秒で**変わらなかった**。
478/// NET_LOCK の競合は律速ではない。新しい同期機構は入れない。
479pub fn tcp_close_real(local_port: u16) -> Result<(), &'static str> {
480    let idx = {
481        let _lock = super::NET_LOCK.lock();
482        tcp_find_socket_by_local(local_port).ok_or("socket not found")?
483    };
484
485    let (remote_ip, remote_port, seq, ack) = {
486        let _lock = super::NET_LOCK.lock();
487        unsafe {
488            let s = &super::TCP_SOCKETS[idx];
489            if s.state != super::TcpState::Established && s.state != super::TcpState::CloseWait {
490                // 【2026-07-24重大バグ発見・修正】以前はここで即座に`Err`を返して
491                // いたため、相手が先にFINを送ってきた(`FinWait1`/`Closed`等へ
492                // 既に遷移済みの)ソケットに対して呼び出し元が`tcp_close_real`を
493                // 呼ぶと、下方の`TCP_SOCKETS[idx] = TcpSocket::empty()`に一切
494                // 到達できず、**そのソケットスロットが二度と再利用されないまま
495                // 永久にリークする**重大なバグだった(`TCP_SOCKET_TABLE_SIZE`は
496                // 8のみのため、失敗した接続が数回続くだけで新規接続が丸ごと
497                // 「tcp socket table full」で失敗するようになる)。相手がFINを
498                // 送信済み等、こちら側から丁寧にクローズシーケンスを行う意味が
499                // 既に無い状態では、グレースフルクローズをスキップして即座に
500                // スロットを解放する。
501                super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
502            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
503            // スロットを使う別接続へ他人のデータが混入する。
504            REASSEMBLERS[idx].clear();
505                return Ok(());
506            }
507            (s.remote_ip, s.remote_port, s.snd_nxt, s.rcv_nxt)
508        }
509    };
510
511    {
512        let _lock = super::NET_LOCK.lock();
513        unsafe {
514            super::TCP_SOCKETS[idx].state = super::TcpState::FinWait1;
515            super::TCP_SOCKETS[idx].snd_nxt = super::TCP_SOCKETS[idx].snd_nxt.wrapping_add(1);
516        }
517    }
518
519    let mut fin_pkt = [0u8; 128];
520    let len = build_tcp_ipv4(
521        super::status().ip,
522        local_port,
523        remote_ip,
524        remote_port,
525        seq,
526        ack,
527        0x11, // FIN-ACK
528        &[],
529        &mut fin_pkt,
530    )?;
531    // FIN が送れなくても後続のスロット解放処理は必要なので即 return はしないが、
532    // 「切断を通知できていない」事実は必ず残す(相手側にはハーフオープンな
533    // 接続が残り、次の接続で不可解な RST を受ける原因になる)。
534    if let Err(e) = super::arp::transmit_nic_ipv4_packet(&fin_pkt[..len]) {
535        crate::warn!(
536            "[NET] TCP CLOSE: FIN transmit failed on local_port={}: {}",
537            local_port,
538            e
539        );
540    }
541
542    {
543        let _lock = super::NET_LOCK.lock();
544        unsafe {
545            super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
546        }
547    }
548
549    let start_time = crate::kernel::timer::get_system_time_ms();
550    while crate::kernel::timer::get_system_time_ms() - start_time < 3000 {
551        crate::kernel::usb::poll_ethernet_data_plane_only();
552
553        let state = {
554            let _lock = super::NET_LOCK.lock();
555            unsafe { super::TCP_SOCKETS[idx].state }
556        };
557
558        if state == super::TcpState::TimeWait || state == super::TcpState::Closed {
559            break;
560        }
561        // フリーズ対策: スリープで完全に待つのではなく yield_now() で
562        // パケット回収を高頻度で継続しつつ、他タスクへ CPU を譲る。
563        crate::kernel::scheduler::yield_now();
564    }
565
566    {
567        let _lock = super::NET_LOCK.lock();
568        unsafe {
569            super::TCP_SOCKETS[idx] = super::TcpSocket::empty();
570            // スロット解放時は順序外バッファも必ず捨てる。残すと次に同じ
571            // スロットを使う別接続へ他人のデータが混入する。
572            REASSEMBLERS[idx].clear();
573        }
574    }
575
576    Ok(())
577}
578
579pub(crate) fn parse_tcp_ipv4_packet(packet: &[u8]) -> Result<(), &'static str> {
580    // 【2026-07-25】長さ検証より先に `packet[0]` を読んでいたため、空スライスで
581    // 呼ぶとインデックス範囲外でパニック(=カーネルクラッシュ)していた。
582    // 現在の唯一の呼び出し元 `parse_ipv4_packet` は事前に 20 バイト以上を保証
583    // しているので実害は出ていないが、境界検証を関数自身の責務として持たせる。
584    if packet.len() < 20 {
585        return Err("tcp packet too short");
586    }
587    let ihl = ((packet[0] & 0x0F) as usize) * 4;
588    if ihl < 20 || packet.len() < ihl + 20 {
589        return Err("tcp packet too short");
590    }
591
592    let total_len = (((packet[2] as u16) << 8) | packet[3] as u16) as usize;
593    if total_len < ihl + 20 || total_len > packet.len() {
594        return Err("invalid ipv4 total length");
595    }
596
597    let tcp = &packet[ihl..total_len];
598    let src_port = ((tcp[0] as u16) << 8) | tcp[1] as u16;
599    let dst_port = ((tcp[2] as u16) << 8) | tcp[3] as u16;
600    let seq =
601        ((tcp[4] as u32) << 24) | ((tcp[5] as u32) << 16) | ((tcp[6] as u32) << 8) | tcp[7] as u32;
602    let ack = ((tcp[8] as u32) << 24)
603        | ((tcp[9] as u32) << 16)
604        | ((tcp[10] as u32) << 8)
605        | tcp[11] as u32;
606    let data_offset = ((tcp[12] >> 4) as usize) * 4;
607    if data_offset < 20 || data_offset > tcp.len() {
608        return Err("invalid tcp data offset");
609    }
610
611    let flags = tcp[13];
612    let payload_len = tcp.len() - data_offset;
613    let src_ip = [packet[12], packet[13], packet[14], packet[15]];
614
615    if dst_port > 1024 {
616        crate::debug!(
617            "TCP RX: src_port={} dst_port={} flags=0x{:02x} payload_len={}",
618            src_port,
619            dst_port,
620            flags,
621            payload_len
622        );
623    }
624
625    // ACK は「ロック内で組み立て、ロック解放後に送信」する。送信経路は ARP
626    // キャッシュミス時に最大 2 秒ブロックし、その間 `poll_ethernet_data_plane_only()`
627    // 経由で本関数が再入されうるため、`NET_LOCK` を保持したまま送信しては
628    // ならない(詳細は `build_tcp_ack_packet` のコメント参照)。
629    let mut ack_pkt = [0u8; 128];
630    let mut ack_len = 0usize;
631
632    {
633        let _lock = super::NET_LOCK.lock();
634        let idx = tcp_find_socket(dst_port, src_ip, src_port).ok_or("tcp socket not found")?;
635
636        unsafe {
637            // 【2026-08-05】ここは `TCP_SOCKETS[idx].clone()` して、
638            // 書き換えてから丸ごと代入で書き戻していた。
639            //
640            // `TcpSocket` は `rx_buffer: Vec<u8>` を内包するので、
641            // **セグメントが 1 個届くたびに受信バッファ全体を複製**していた。
642            // バッファは読み手が引き取るまで育つため、
643            // 「溜まっているほど 1 セグメントが高くつく」という
644            // 二次的な悪化になっていた。
645            //
646            // 実測: `ingest_nic_ethernet_frame` に 1 フレームあたり 789µs
647            // かかっており、フレーム処理時間の 95% を占めていた
648            // (3.7MB の取得で合計 10.7 秒)。
649            //
650            // その場で書き換えれば複製は要らない。
651            // 計算量は O(受信バッファ長) から O(1) へ。
652            let s = &mut super::TCP_SOCKETS[idx];
653
654            // 状態遷移の判断そのものは純粋モジュール `tcp_state` へ委譲する
655            // (ホスト側ユニットテストで異常系まで検証済み。`tests/src/test_tcp_state.rs`)。
656            // ここはグローバル状態・rx_buffer という副作用のみを担当する。
657            let mut rs = super::tcp_state::RecvState {
658                state: s.state,
659                snd_nxt: s.snd_nxt,
660                rcv_nxt: s.rcv_nxt,
661                fin_received: s.fin_received,
662            };
663            let seg = super::tcp_state::Segment {
664                seq,
665                ack,
666                flags,
667                payload_len: payload_len as u32,
668            };
669            let prev_fin = rs.fin_received;
670            let outcome = super::tcp_state::on_segment(&mut rs, &seg);
671            // 【2026-07-29診断】受信が途中で止まる件。届いたセグメントのうち
672            // ペイロードを受理した数/順序外等で捨てた数を数える。
673            if payload_len > 0 {
674                if outcome.accept_payload {
675                    SEG_ACCEPTED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
676                } else {
677                    SEG_DROPPED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
678                }
679            }
680
681            if outcome.accept_payload {
682                // 重なった再送は既受信の先頭を飛ばす(`payload_skip`。2026-09-26)。
683                let start = data_offset
684                    .saturating_add(outcome.payload_skip as usize)
685                    .min(tcp.len());
686                s.rx_buffer.extend_from_slice(&tcp[start..]);
687                // 【2026-07-29】欠落が埋まったので、保持していた順序外セグメントの
688                // うち連続する分をまとめて取り込む(`spec/tcp_receive.md` 優先度1)。
689                let reasm = &mut REASSEMBLERS[idx];
690                reasm.drop_before(rs.rcv_nxt);
691                let (new_nxt, more) = reasm.pop_contiguous(rs.rcv_nxt);
692                if !more.is_empty() {
693                    s.rx_buffer.extend_from_slice(&more);
694                    rs.rcv_nxt = new_nxt;
695                }
696            } else if payload_len > 0
697                && (flags & super::tcp_state::FLAG_SYN) == 0
698                && rs.state == super::TcpState::Established
699            {
700                // 順序外セグメント。RFC 9293 は破棄を許容するが、捨てると
701                // 1 個の欠落で以降を全部落として受信が停止する(実測で確認)。
702                // 保持しておき、欠落が再送で埋まった瞬間に繋げる。
703                let _ = REASSEMBLERS[idx].insert(seq, &tcp[data_offset..]);
704            }
705            // 【2026-09-26 診断】RST で接続が切られた瞬間を記録する(途中切れの切り分け)。
706            if (flags & super::tcp_state::FLAG_RST) != 0 {
707                crate::warn!(
708                    "[NET][DIAG] RST seg on local_port={} prev_state={} seq={} rcv_nxt={} rx_buffer_len={}",
709                    s.local_port,
710                    tcp_state_name(s.state),
711                    seq,
712                    s.rcv_nxt,
713                    s.rx_buffer.len()
714                );
715            }
716            if (flags & super::tcp_state::FLAG_FIN) != 0 && !prev_fin {
717                crate::warn!(
718                    "[NET][DIAG] FIN seg on local_port={} state={} accepted={} rx_buffer_len={}",
719                    s.local_port,
720                    tcp_state_name(s.state),
721                    rs.fin_received,
722                    s.rx_buffer.len()
723                );
724            }
725
726            s.state = rs.state;
727            s.snd_nxt = rs.snd_nxt;
728            s.rcv_nxt = rs.rcv_nxt;
729            s.fin_received = rs.fin_received;
730
731            if outcome.send_ack {
732                if let Ok(n) = build_tcp_ack_packet(idx, &mut ack_pkt) {
733                    ack_len = n;
734                }
735            }
736
737            super::NET_STATE.rx_packets = super::NET_STATE.rx_packets.saturating_add(1);
738        }
739    } // ← ここで NET_LOCK を解放してから送信する
740
741    if ack_len > 0 {
742        // ACK が送れないと相手は再送を続け、こちらからは「応答が来ない」ように
743        // 見える。受信処理からは呼び出し側へ返す先が無いため、ここでは必ず
744        // ログへ残す(黙って捨てない)。
745        if let Err(e) = super::arp::transmit_nic_ipv4_packet(&ack_pkt[..ack_len]) {
746            crate::warn!("[NET] TCP: ACK transmit failed: {}", e);
747        }
748    }
749
750    Ok(())
751}
752
753/// 指定ソケットへの ACK パケットを **組み立てるだけ** で、送信はしない。
754///
755/// 【2026-07-26 重要】以前はこの関数が `transmit_nic_ipv4_packet()` まで
756/// 行っており、しかも呼び出し元 `parse_tcp_ipv4_packet` が `NET_LOCK` を
757/// 保持したまま呼んでいた。送信経路は ARP キャッシュミス時に
758/// `build_usb_ethernet_ipv4_frame` 内で**最大 2 秒ブロッキング**する
759/// ARP 解決ループへ入り、そのループは内部で
760/// `poll_ethernet_data_plane_only()` を回す。ポーリングは受信パケットを
761/// `parse_tcp_ipv4_packet` / `parse_udp_ipv4_packet` へ流し、これらは
762/// いずれも `NET_LOCK` を取得しようとするため、再入不可の `spin::Mutex`
763/// に対する**自己デッドロック**になりうる。デッドロックに至らなくとも、
764/// 2 秒間 `NET_LOCK` を占有すると DNS 応答待ちループ
765/// (`udp_recv_matching` も同じロックを取る)が自分のキューを確認できず、
766/// `dns response timeout` が誘発される。
767/// そのため組み立てと送信を分離し、送信はロック解放後に行う。
768fn build_tcp_ack_packet(idx: usize, out: &mut [u8]) -> Result<usize, &'static str> {
769    unsafe {
770        // 【2026-08-05】ここも `clone()` していた。
771        //
772        // `parse_tcp_ipv4_packet` と同じ形の問題で、`TcpSocket` は
773        // `rx_buffer: Vec<u8>` を内包するため、**ACK を 1 本組むたびに
774        // 受信バッファ全体を複製**していた。ACK は受信セグメントごとに
775        // 送るので、3.7MB の取得では 13000 回以上通る。
776        //
777        // 読みたいのはポート・IP・シーケンス番号の 5 つだけ。参照で足りる。
778        let s = &super::TCP_SOCKETS[idx];
779        build_tcp_ipv4(
780            super::status().ip,
781            s.local_port,
782            s.remote_ip,
783            s.remote_port,
784            s.snd_nxt,
785            s.rcv_nxt,
786            0x10, // ACK
787            &[],
788            out,
789        )
790    }
791}
792
793fn tcp_alloc_socket(
794    local_port: u16,
795    remote_ip: [u8; 4],
796    remote_port: u16,
797) -> Result<usize, &'static str> {
798    unsafe {
799        // 空きスロットの選定は純粋モジュールへ委譲(満杯時の挙動は
800        // `tests/src/test_tcp_state.rs` で決定論的に検証済み)。
801        let idx = super::tcp_state::find_free_slot(super::TCP_SOCKET_TABLE_SIZE, |i| {
802            super::TCP_SOCKETS[i].valid
803        })?;
804        // 新規割り当て時にも順序外バッファを初期化する(前の接続の残骸混入防止)。
805        REASSEMBLERS[idx].clear();
806        super::TCP_SOCKETS[idx] = super::TcpSocket {
807            valid: true,
808            local_port,
809            remote_ip,
810            remote_port,
811            state: super::TcpState::Closed,
812            snd_nxt: 0,
813            rcv_nxt: 0,
814            rx_buffer: alloc::vec::Vec::new(),
815            fin_received: false,
816        };
817        Ok(idx)
818    }
819}
820
821/// 【試験用】空いているスロットをすべて占有し、占有した数を返す。
822///
823/// 「テーブルが満杯なら `tcp_connect_real` が `Err("tcp socket table full")`
824/// を返す」ことを**決定論的に**確かめるために使う。
825///
826/// これが無かった頃、試験は 40 本の OS スレッドを一斉に起動して
827/// 「SYN タイムアウト(6 秒)で先行分が抜ける前に全員が揃う」ことに賭けていた。
828/// ホスト負荷が高いと起動が遅れて満杯を観測できず、偽陰性で落ちていた。
829/// 確かめたい性質は満杯時の戻り値であって、スレッドが競争に勝てるかではない。
830#[cfg(test)]
831pub fn tcp_fill_socket_table_for_test() -> usize {
832    let _lock = super::NET_LOCK.lock();
833    let mut occupied = 0usize;
834    unsafe {
835        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
836            if super::TCP_SOCKETS[i].valid {
837                continue;
838            }
839            super::TCP_SOCKETS[i] = super::TcpSocket {
840                valid: true,
841                // 実在しない宛先。パケットは送らないので応答も来ない。
842                local_port: 40000 + i as u16,
843                remote_ip: [203, 0, 113, 1],
844                remote_port: 9,
845                state: super::TcpState::Closed,
846                snd_nxt: 0,
847                rcv_nxt: 0,
848                rx_buffer: alloc::vec::Vec::new(),
849                fin_received: false,
850            };
851            occupied += 1;
852        }
853    }
854    occupied
855}
856
857/// 【試験用】ソケットテーブルを丸ごと空に戻す。
858///
859/// `tcp_fill_socket_table_for_test` と対で使い、
860/// グローバル状態を後続の試験へ持ち越さない。
861#[cfg(test)]
862pub fn tcp_clear_socket_table_for_test() {
863    let _lock = super::NET_LOCK.lock();
864    unsafe {
865        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
866            super::TCP_SOCKETS[i] = super::TcpSocket::empty();
867        }
868    }
869}
870
871fn tcp_find_socket(local_port: u16, remote_ip: [u8; 4], remote_port: u16) -> Option<usize> {
872    unsafe {
873        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
874            let s = &super::TCP_SOCKETS[i];
875            if !s.valid {
876                continue;
877            }
878            if s.local_port == local_port
879                && s.remote_port == remote_port
880                && s.remote_ip == remote_ip
881            {
882                return Some(i);
883            }
884        }
885    }
886    None
887}
888
889pub(crate) fn tcp_find_socket_by_local(local_port: u16) -> Option<usize> {
890    unsafe {
891        for i in 0..super::TCP_SOCKET_TABLE_SIZE {
892            if super::TCP_SOCKETS[i].valid && super::TCP_SOCKETS[i].local_port == local_port {
893                return Some(i);
894            }
895        }
896    }
897    None
898}
899
900fn build_tcp_ipv4(
901    src_ip: [u8; 4],
902    src_port: u16,
903    dst_ip: [u8; 4],
904    dst_port: u16,
905    seq: u32,
906    ack: u32,
907    flags: u8,
908    payload: &[u8],
909    out: &mut [u8],
910) -> Result<usize, &'static str> {
911    let mut opt_len = 0;
912    if (flags & 0x02) != 0 {
913        opt_len = 4;
914    }
915    let tcp_hdr_len = 20usize + opt_len;
916    let tcp_len = tcp_hdr_len + payload.len();
917    let total_len = 20usize + tcp_len;
918    if total_len > out.len() {
919        return Err("buffer too small");
920    }
921
922    out[0] = 0x45;
923    out[1] = 0;
924    out[2] = ((total_len >> 8) & 0xFF) as u8;
925    out[3] = (total_len & 0xFF) as u8;
926    out[4] = 0x61;
927    out[5] = 0x22;
928    out[6] = 0;
929    out[7] = 0;
930    out[8] = 64;
931    out[9] = 6;
932    out[10] = 0;
933    out[11] = 0;
934    out[12..16].copy_from_slice(&src_ip);
935    out[16..20].copy_from_slice(&dst_ip);
936
937    let ip_checksum = super::ipv4_header_checksum(&out[0..20]);
938    out[10] = (ip_checksum >> 8) as u8;
939    out[11] = ip_checksum as u8;
940
941    out[20] = (src_port >> 8) as u8;
942    out[21] = src_port as u8;
943    out[22] = (dst_port >> 8) as u8;
944    out[23] = dst_port as u8;
945    out[24] = (seq >> 24) as u8;
946    out[25] = (seq >> 16) as u8;
947    out[26] = (seq >> 8) as u8;
948    out[27] = seq as u8;
949    out[28] = (ack >> 24) as u8;
950    out[29] = (ack >> 16) as u8;
951    out[30] = (ack >> 8) as u8;
952    out[31] = ack as u8;
953    out[32] = ((tcp_hdr_len / 4) << 4) as u8;
954    out[33] = flags;
955    // 【2026-07-24】受信ウィンドウサイズを`0xFFFF`(65535バイト)へ拡大する
956    // 変更を試みたが、それ以降メインHTML取得自体の成功率が悪化したように
957    // 見えたため(一度に大量のデータが届くことでこの簡易TCPスタックの
958    // 受信処理が追いつかなくなる可能性)、元の値(2880)へ差し戻した。
959    // 「より慎重な検証(中間的なサイズでの比較等)が必要」と残されていた。
960    //
961    // 【2026-07-29】測定基盤(`tls.rs` の `stats`)ができ、成功率を
962    // 段階別に数値化できるようになったので中間値で再挑戦する。
963    // 2880(≒2 MSS)は小さすぎ、49KB の応答に 17 往復以上を要して
964    // 損失機会を増やしていた。順序外再構成バッファ(`net/reassembly.rs`)も
965    // 入ったので、一度に多く届いても取りこぼしにくくなっている。
966    // 8192 バイト(≒5.6 MSS)から試す。効果は STATS の
967    // truncated 件数で比較すること(単発の観測で判断しない)。
968    //
969    // 【2026-08-05】画像取得の内訳を測ったところ、3.7MB の JPEG で
970    // 取得 3746 tick に対しデコードは 950 tick だった(`[IMGPERF]`)。
971    // **律速は取得側**で、デコードを速くしても意味が無い。
972    //
973    // 実効 100KB/s 程度。ウィンドウ 8192 バイトだと「飛ばせる量」が
974    // 帯域遅延積で頭打ちになる(RTT 80ms なら 8192/0.08 ≒ 100KB/s で、
975    // 実測とちょうど一致する)。ウィンドウが律速そのもの。
976    //
977    // 【2026-08-05 検証済み・変更せず】32768(0x8000)へ拡大して測ったが、
978    // 3.7MB の取得は 3746 → 3906 tick で**改善しなかった**(誤差範囲)。
979    // 成功率は 16/16・truncated=0 で悪化もしていない。
980    //
981    // つまりウィンドウは律速ではない。飛ばせる量ではなく、
982    // **こちらの受信処理速度**が上限を決めている。
983    // 根拠の無い変更は入れないので 8192 のまま残す。
984    out[34] = 0x20;
985    out[35] = 0x00;
986    out[36] = 0x00;
987    out[37] = 0x00;
988    out[38] = 0x00;
989    out[39] = 0x00;
990
991    let mut payload_start = 40;
992    if opt_len > 0 {
993        out[40] = 2;
994        out[41] = 4;
995        out[42] = 0x05;
996        out[43] = 0xB4;
997        payload_start += 4;
998    }
999    let payload_end = payload_start + payload.len();
1000    out[payload_start..payload_end].copy_from_slice(payload);
1001
1002    let mut tcp_csum_buf = alloc::vec![0u8; 12 + tcp_len];
1003    tcp_csum_buf[0..4].copy_from_slice(&src_ip);
1004    tcp_csum_buf[4..8].copy_from_slice(&dst_ip);
1005    tcp_csum_buf[8] = 0;
1006    tcp_csum_buf[9] = 6;
1007    tcp_csum_buf[10] = ((tcp_len >> 8) & 0xFF) as u8;
1008    tcp_csum_buf[11] = (tcp_len & 0xFF) as u8;
1009    tcp_csum_buf[12..12 + tcp_len].copy_from_slice(&out[20..total_len]);
1010
1011    let csum = super::ipv4_header_checksum(&tcp_csum_buf);
1012    out[36] = (csum >> 8) as u8;
1013    out[37] = csum as u8;
1014
1015    let sent = super::arp::transmit_nic_ipv4_packet(&out[..total_len])?;
1016    Ok(sent)
1017}
1018
1019fn tcp_state_name(state: super::TcpState) -> &'static str {
1020    super::tcp_state::tcp_state_name(state)
1021}