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