Compare commits

..
Author SHA1 Message Date
Leency a8f285ec86 kernel/net: lock the socket in the retransmission timer
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 37s
Test PR / Build (en_US) (pull_request) Successful in 2m47s
Test PR / Build (es_ES) (pull_request) Successful in 2m4s
Test PR / Build (ru_RU) (pull_request) Successful in 1m58s
- take SOCKET.mutex while rewriting SND_NXT/cwnd/ssthresh; skip if an ACK
  stopped or re-armed the timer meanwhile
- back off from t_rxtcur: the first timeout was 3.2 s, the second 25.6 s
2026-09-30 10:14:17 +03:00
Leency 772031e6b6 kernel/net: fix TCP RTT measurement
- tcp_output: the "timing anything?" test was inverted, no segment was timed
- tcp_timer: t_rtt was never incremented
- tcp_xmit_timer: first sample keyed on t_srtt, not t_rtt; signed jg on
  srtt/rttvar updates (ja reset them to 1 whenever RTT dropped); t_rtt = 0
- timestamp RTT: 1/100 s -> 640 ms ticks (RTO came out 64x too long);
  skip it when TSecr is 0
2026-09-30 10:14:15 +03:00
Leency b1110dc03b kernel/net: start persist probes at the retransmission floor
BSD waits 5.12s before the first zero-window probe and doubles from
there. A peer that closes its window and never volunteers an update --
QEMU 0.10's slirp, or any stack applying silly-window avoidance to its
updates -- is rediscovered only by our probe, so 5, 10, 20s of silence
swallowed most of a 15s upload stage. Start at TCP_time_re_min (1.28s),
as Linux starts at the RTO; still exponential, still capped.
2026-09-30 10:14:12 +03:00
Leency 6c30c66aa3 kernel/net: implement the TCP retransmission timeout
t_rxtcur, the timeout the retransmission timer is armed with, was never
assigned anywhere: not at socket creation, not in tcp_xmit_timer. Every
socket armed the timer with 0, the 640 ms tick took it to 0xFFFFFFFF,
and it never expired. And when it did (in principle) expire, the handler
was a bare call tcp_output, which sends from SND_NXT: the unacknowledged
segment before it was never resent, nothing was backed off, the timer
was not re-armed. A segment the peer dropped -- a window that shrank
under data in flight, any loss on the uplink -- stalled the connection
until the application gave up. 23 duplicate ACKs from the peer did not
help either.

Observed as every upload through QEMU's slirp freezing the moment the
host side paused: pcap of an 8 MB POST to a sink that stops reading for
12 s showed the peer's window go 328, the guest's 1400-byte segment
beyond it dropped, then nothing but empty ACKs, and after the window
reopened the guest sent the NEXT 32 KB with the hole still there and
sat. speedtest.net's upload stage lost 3 of 4 connections this way on
the 2009 QEMU.

- tcp_init_socket: t_rxtcur = TCP_time_rtt_default (BSD TCPTV_RTOBASE)
  until the first RTT sample.
- tcp_xmit_timer: t_rxtcur = srtt + 4*rttvar (BSD TCP_REXMTVAL) within
  [re_min, re_max]; a fresh sample resets t_rxtshift.
- tcp_timer, retransmission expiry: BSD TCPT_REXMT -- drop the
  connection with ETIMEDOUT past TCP_max_rxtshift; re-arm with the
  backed-off timeout (shift capped at 6); SND_NXT = SND_UNA; no RTT
  sample from the resent segment; ssthresh = max(2, min(wnd,cwnd)/2/mss)
  segments, cwnd = one segment, dupacks = 0; then tcp_output.

With the patch the same fixture completes: 8 MB, 12.36 s of which 12 s
is the sink's deliberate pause, 205 Mbps once it reads again.
2026-09-30 10:14:12 +03:00
Leency bd071d588b kernel/net: fix use-after-free on refused TCP connections (#622)
Build system / Build (en_US) (push) Successful in 3m2s
Build system / Build (es_ES) (push) Successful in 3m9s
Build system / Build (ru_RU) (push) Successful in 3m15s
Build system / Publish Images (push) Successful in 11m5s
tcp_input: on RST+ACK in SYN_SENT, unlock the socket mutex before
tcp_drop and leave through .drop_no_socket. tcp_drop frees the socket
and locks that mutex itself, so the network thread hung on it.

Assisted-by: Claude:claude-opus-5
Reviewed-on: #622
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: Kiril Lipatov <lipatov.kiril@gmail.com>
2026-09-29 19:12:30 +00:00
Leency 9ec66b0eba drivers/i8255x: get to work on a real device (#725)
Build system / Build (en_US) (push) Successful in 3m23s
Build system / Build (es_ES) (push) Successful in 3m27s
Build system / Build (ru_RU) (push) Successful in 3m31s
Build system / Publish Images (push) Successful in 11m54s
- the TX reclaim loop in int_handler multiplied sizeof.txfd by itself  (mul eax) instead of by last_tx, so it looked 1024 bytes past tx_ring and never freed a descriptor
- it also left txfd.status set, and transmit treats a non-zero status as busy, so once the 16-entry ring wrapped every send ended in "TX overrun": DHCP fell back to link-local, ping showed "Socket error", while the link still read as connected
- reclaim only descriptors the device completed (TXFD_STATUS_C) and zero their status; transmit now checks for an unreclaimed buffer (virt_addr)

Seen on an EtherExpress Pro/100 in a Pentium II. Reproduced in QEMU (-cpu pentium2, i82557b): ping stopped at the 12th reply every time; with the fix it runs indefinitely, also under a UDP flood.

Reviewed-on: #725
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 19:03:24 +00:00
Leency 95bcf78537 kernel/ahci: support CD/DVD drives (ATAPI) (#694)
Build system / Build (es_ES) (push) Successful in 3m34s
Build system / Build (en_US) (push) Successful in 3m39s
Build system / Build (ru_RU) (push) Successful in 3m46s
Build system / Publish Images (push) Successful in 10m4s
Summary: an optical drive on an AHCI port was detected and then ignored,
so CD/DVD only worked with the controller switched to IDE mode. It now
shows up as /srN and ISO9660 discs are readable, including disc changes.

Details:
- ahci_atapi_cmd sends a SCSI command through the ATA PACKET command
  (A bit in the command header, CDB in acmd, DMA when IDENTIFY PACKET
  word 49 allows it, DMADIR when word 62 requires it). Built on top of it:
  TEST UNIT READY, REQUEST SENSE, READ CAPACITY(10), READ(10), READ TOC
  (start of the last session for multisession discs) and START STOP UNIT
  for DISKFUNC.LoadTray.
- Drives are registered as sr0, sr1, ... with DISK_NO_INSERT_NOTIFICATION,
  2048-byte sectors and read-only media; iso9660 mounts them as /srN/1.
  They cannot be named cd*: fs_lfn.inc sends every path starting with "cd"
  to the old ATAPI-over-IDE code.
- Disc change: the kernel lets go of a mounted medium only when querymedia
  fails, so after a UNIT ATTENTION querymedia fails once and the kernel
  immediately mounts the new disc. A READ(10) that hits UNIT ATTENTION
  fails instead of silently reading the new disc.
- One ATAPI transfer is capped at (PRDT_MAX_ENTRIES - 1) * 4096 bytes, so
  the PRDT always covers the whole request. The PRDT builder moved out of
  ahci_rw_sectors into ahci_build_prdt and is shared by both paths.
- A command the drive refuses (an empty tray does that on every TEST UNIT
  READY) restarts the port quietly instead of running the full recovery.
- Optical ports never busy-poll a long command and never switch the whole
  controller to polling: missing interrupts are counted per port and only
  that port's PxIE is turned off.
- Diagnostic output (command statistics, interrupt wakeups, register dumps
  during init) is now behind AHCI_DBGLVL, off by default. Errors and one
  line per detected device are still printed.

Tested: QEMU, VirtualBox and VMware (detection, listings and reads checked
against host checksums; eject/insert in QEMU and VirtualBox, a direct disc
swap in VirtualBox, an empty drive in VMware), SATA disk read/write on the
same controller in QEMU, and a GA-D525TUD (NM10) with an ASUS DRW-24B3ST:
detection, reading, disc change. The debug level change was build-tested
only.

Assisted-by: Claude Opus 5
Reviewed-on: #694
Reviewed-by: Burer <burer@kolibrios.org>
Reviewed-by: Ivan B <1+dunkaist@noreply.localhost>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 15:44:06 +00:00
Leency 6f3cae4e0c kernel/network: fix window-update check in tcp_output (#711)
Build system / Build (en_US) (push) Successful in 4m8s
Build system / Build (es_ES) (push) Successful in 3m58s
Build system / Build (ru_RU) (push) Successful in 4m9s
Build system / Publish Images (push) Successful in 11m38s
`tcp_output`: the window-update check computed `min(free, max_win - advertised)` instead of BSD's `min(free, max_win) - advertised`. With unread data in the receive buffer, every `send()` produced an empty ACK and never reached the persist state: no zero-window probes, and the connection hung.

- Clamp to the free space first, then subtract the advertised window.
- Compare signed: the result is negative when the window shrank.

Tested in QEMU with pcap: ~11000 empty ACKs in 0.6 s and no probe before the fix; 12 ACKs and a 1-byte persist probe after it.

Reviewed-on: #711
Reviewed-by: Burer <burer@kolibrios.org>
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 13:38:57 +00:00
Leency b59e371c0e kernel/network: forget a network device's state when it is removed (#719)
Build system / Build (en_US) (push) Successful in 5m45s
Build system / Build (es_ES) (push) Successful in 5m55s
Build system / Build (ru_RU) (push) Successful in 6m4s
Build system / Publish Images (push) Failing after 11m50s
- `net_remove_device`: clear the slot's IPv4 settings, ARP table and counters, and drop its frames from the ethernet input queue.
- `ipv4_route`:
  - skip slots without a device;
  - an interface without an address routes broadcasts only;
  - off-link without a gateway fails instead of resolving 0.0.0.0.
- `ipv4_output_raw`: check the route before resolving it.
- `eth_output`, `arp_output_request`: refuse a null device.
- `eth_output`: count a too-large frame on the device, not on `eax`.

Reviewed-on: #719
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 13:30:00 +00:00
Leency 939505198f http.obj: close the socket when connect() fails; kernel: unlock on tcp_connect errors
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 25s
Test PR / Build (ru_RU) (pull_request) Successful in 2m2s
Test PR / Build (en_US) (pull_request) Successful in 2m11s
Test PR / Build (es_ES) (pull_request) Successful in 2m18s
Build system / Build (en_US) (push) Successful in 3m50s
Build system / Build (es_ES) (push) Successful in 3m52s
Build system / Build (ru_RU) (push) Successful in 3m55s
Build system / Publish Images (push) Successful in 11m8s
open_connection in http.obj left the socket open whenever connect()
failed. The kernel never reclaims sockets of a finished process
(socket_process_end is a stub), so every failed connect leaked a socket
with its two SOCKET_BUFFER_SIZE rings for good, and a late SYN+ACK could
still connect the orphan. WebView with a few dead image hosts ran the
kernel heap dry ("SOCKET_ring_create: Out of memory!"), after which every
application lost the network. Close the socket on the way out.

The socket() error check compared against 0, but the syscall returns -1
on failure; compare with -1.

The close() above exposed a second bug: tcp_connect took SOCKET.mutex
before creating the rings and returned from .nomem and .enoroute without
releasing it. Any later socket_free on that socket - close() from the
application, once http.obj does it - then waited for the mutex forever
and the thread became unkillable. Unlock on both error exits.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:38:01 +03:00
17 changed files with 1331 additions and 164 deletions

No files matched your search

+100 -14
View File
@@ -130,6 +130,8 @@ RU_STATUS_IDLE = 0000b shl 2
RU_STATUS_SUSPENDED = 0001b shl 2
RU_STATUS_NO_RESOURCES = 0010b shl 2
RU_STATUS_READY = 0100b shl 2
SCB_STATUS_CUS = 11000000b ; CU Status
CU_STATUS_ACTIVE = 10b shl 6
SCB_STATUS_FCP = 1 shl 8 ; Flow Control Pause
SCB_STATUS_SWI = 1 shl 10 ; Software Interrupt
SCB_STATUS_MDI = 1 shl 11 ; MDI read/write complete
@@ -183,6 +185,8 @@ struct txfd
ends
TXFD_STATUS_C = 1 shl 15
TXFD_CMD_IA = 1 shl 0
TXFD_CMD_CFG = 1 shl 1
TXFD_CMD_TX = 1 shl 2
@@ -628,6 +632,9 @@ reset:
mov ax, CU_START or INT_MASK
out dx, ax
call cmd_wait
; cmd_wait only means the SCB accepted the command. The Configure below
; reuses confcmd, so a slow chip would read it instead of our MAC.
call confcmd_wait
;-------------
; Configure CU
@@ -652,6 +659,7 @@ reset:
mov ax, CU_START ; expect Interrupts from now on
out dx, ax
call cmd_wait
call confcmd_wait
; Start media check timer
mov [ebx + device.state], ETH_LINK_DOWN
@@ -678,6 +686,11 @@ init_rx_ring:
;---------------------
; build rxfd structure
cmp [ebx + device.rx_desc], 0 ; the RFD of an earlier reset
je @f
invoke NetFree, [ebx + device.rx_desc]
mov [ebx + device.rx_desc], 0
@@:
invoke NetAlloc, 2000
test eax, eax
jz .out_of_mem
@@ -689,6 +702,7 @@ init_rx_ring:
mov [esi + sizeof.NET_BUFF + rxfd.command], RXFD_CMD_EL or RXFD_CMD_SUSPEND
mov [esi + sizeof.NET_BUFF + rxfd.link], eax
mov [esi + sizeof.NET_BUFF + rxfd.count], 0
mov [esi + sizeof.NET_BUFF + rxfd.rx_buf_addr], 0xffffffff ; simplified mode, no RBD
mov [esi + sizeof.NET_BUFF + rxfd.size], 1528
ret
@@ -709,6 +723,15 @@ init_tx_ring:
invoke GetPhysAddr
mov ecx, TX_RING_SIZE
.next_desc:
; after a reset with frames still unreclaimed: free them, or transmit sees the
; descriptor busy (virt_addr) and reports TX overrun forever
cmp [esi + txfd.virt_addr], 0
je @f
push eax ecx
invoke NetFree, [esi + txfd.virt_addr]
pop ecx eax
mov [esi + txfd.virt_addr], 0
@@:
mov [esi + txfd.status], 0
mov [esi + txfd.command], 0
lea edx, [eax + txfd.buf_addr]
@@ -763,7 +786,7 @@ proc transmit stdcall bufferptr
lea edi, [ebx + device.tx_ring + eax]
; Check if current descriptor is free or still in use
cmp [edi + txfd.status], 0
cmp [edi + txfd.virt_addr], 0 ; buffer not reclaimed yet
jne .overrun
; Fill in status and command values
@@ -786,6 +809,22 @@ proc transmit stdcall bufferptr
mov eax, edi
invoke GetPhysAddr
set_io [ebx + device.io_addr], 0
; CU_START is only valid while the CU is idle or suspended:
; wait for the previous frame to leave (1.2 ms at 10 Mbit)
push eax ecx
set_io [ebx + device.io_addr], REG_SCB_STATUS
mov ecx, 100000
.cu_busy:
in al, dx
and al, SCB_STATUS_CUS
cmp al, CU_STATUS_ACTIVE
jne .cu_free
dec ecx
jnz .cu_busy
DEBUGF 2, "CU still active, starting anyway\n"
.cu_free:
pop ecx eax
set_io [ebx + device.io_addr], REG_SCB_PTR
out dx, eax
@@ -854,7 +893,7 @@ int_handler:
DEBUGF 1,"Status: %x\n", ax
test ax, SCB_STATUS_FR ; did we receive a frame?
test ax, SCB_STATUS_FR or SCB_STATUS_RNR ; frame received or receiver stopped?
jz .no_rx
push ax
@@ -901,6 +940,7 @@ int_handler:
mov [esi + sizeof.NET_BUFF + rxfd.command], RXFD_CMD_EL or RXFD_CMD_SUSPEND
mov [esi + sizeof.NET_BUFF + rxfd.link], eax
mov [esi + sizeof.NET_BUFF + rxfd.count], 0
mov [esi + sizeof.NET_BUFF + rxfd.rx_buf_addr], 0xffffffff ; simplified mode, no RBD
mov [esi + sizeof.NET_BUFF + rxfd.size], 1528
; restart RX
@@ -916,11 +956,22 @@ int_handler:
mov ax, RX_START
out dx, ax
call cmd_wait
.out_of_mem:
; Hand the frame over to the kernel
jmp [EthInput]
.out_of_mem:
; The buffer is still our only RFD: drop the frame and reuse it
mov esi, [esp] ; the buffer
mov ecx, [esi + NET_BUFF.length]
dec [ebx + device.packets_rx] ; counted above, before the allocation
sub dword [ebx + device.bytes_rx], ecx
sbb dword [ebx + device.bytes_rx + 4], 0
inc [ebx + device.packets_rx_drop]
add esp, 12 ; buffer, .rx_loop, ebx
mov esi, [ebx + device.rx_desc]
jmp .not_ok
.not_ok:
; Reset the FD
mov [esi + sizeof.NET_BUFF + rxfd.status], 0
@@ -945,6 +996,27 @@ int_handler:
.no_rx_:
DEBUGF 1, "no more data\n"
; With a single RFD the receiver sits in No Resources/Suspended whenever a
; frame arrived before we re-armed it. Nothing restarts it but us.
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_SCB_STATUS
in al, dx
and al, SCB_STATUS_RUS
cmp al, RU_STATUS_READY
je .ru_ready
test byte[esp], 1 ; bit 0 of the saved status is reserved,
jnz .ru_ready ; we mark it: one restart per IRQ
or byte[esp], 1
movzx eax, al
DEBUGF 1, "Restarting receiver, RU status %x\n", eax:2
mov esi, [ebx + device.rx_desc]
push ebx
test [esi + sizeof.NET_BUFF + rxfd.status], RXFD_STATUS_C
jnz .rx_loop ; a frame completed meanwhile
add esp, 4
jmp .not_ok ; re-arm the empty RFD
.ru_ready:
pop ax
.no_rx:
@@ -955,13 +1027,13 @@ int_handler:
push eax
.loop_tx:
mov edi, [ebx + device.last_tx]
mov eax, sizeof.txfd
mul eax
mov eax, [ebx + device.last_tx]
mov edx, sizeof.txfd
mul edx
lea edi, [ebx + device.tx_ring + eax]
cmp [edi + txfd.status], 0
je .tx_done
test [edi + txfd.status], TXFD_STATUS_C ; sent by the device?
jz .tx_done
cmp [edi + txfd.virt_addr], 0
je .tx_done
@@ -970,6 +1042,7 @@ int_handler:
push [edi + txfd.virt_addr]
mov [edi + txfd.virt_addr], 0
mov [edi + txfd.status], 0 ; free for transmit again
invoke NetFree
inc [ebx + device.last_tx]
@@ -980,12 +1053,6 @@ int_handler:
pop eax
.no_tx:
test ax, RU_STATUS_NO_RESOURCES
jz .not_out_of_resources
DEBUGF 2, "Out of resources!\n"
.not_out_of_resources:
pop edi esi ebx
xor eax, eax
inc eax
@@ -1079,6 +1146,25 @@ proc check_media_mii stdcall dev:dword
endp
; Wait (up to 1 s) until the device has executed the command in confcmd.
align 4
confcmd_wait:
mov ecx, 1000 ; udelay = Sleep(1), 1 ms
.loop:
test [ebx + device.confcmd.status], TXFD_STATUS_C
jnz .done
call udelay
dec ecx
jnz .loop
movzx eax, [ebx + device.confcmd.command]
DEBUGF 2, "Command 0x%x not completed\n", eax:4
ret
.done:
movzx eax, [ebx + device.confcmd.command]
movzx ecx, [ebx + device.confcmd.status]
DEBUGF 1, "Command 0x%x done, status 0x%x\n", eax:4, ecx:4
ret
align 4
cmd_wait:
File diff suppressed because it is too large. Load diff
-3
View File
@@ -3,9 +3,6 @@
;; Copyright (C) KolibriOS team 2013-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Portions derived from MenuetOS RAM-disk code: ;;
;; (C) 2004 Ville Turjanmaa, License: GPL ;;
;; ;;
;; RAMDISK functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-5
View File
@@ -3,11 +3,6 @@
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License. ;;
;; ;;
;; MenuetOS process management, protected ring3 ;;
;; ;;
;; Distributed under GPL. See file COPYING for details. ;;
;; Copyright 2003 Ville Turjanmaa ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-4
View File
@@ -3,10 +3,6 @@
;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License. ;;
;; ;;
;; Portions derived from MenuetOS filesystem sources: ;;
;; (C) 2004 Ville Turjanmaa, License: GPL ;;
;; Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-13
View File
@@ -9,19 +9,6 @@
;;
;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa
;;
;; Original MenuetOS contributors:
;; Ville Mikael Turjanmaa, villemt@itu.jyu.fi - main OS coding and design
;; Jan-Michael Brummer, BUZZ2@gmx.de - mouse/display fixes and CD player
;; Felix Kaiser, info@felix-kaiser.de - AMD K6-II IRQs and APM management
;; Paolo Minazzi, paolo.minazzi@inwind.it - Sound Blaster and FAT32 write
;; quickcode@mail.ru - 320x200 palette and S3 VESA 1.2 bank switching
;; Alexey, kgaz@crosswinds.net - Voodoo-compatible graphics
;; Juan M. Caravaca, bitrider@wanadoo.es - graphics optimizations
;; kristol@nic.fi - boot fix for some Pentium models
;; Mike Hibbett, mikeh@oceanfree.net - SLIP driver and TCP/IP stack skeleton
;; Lasse Kuusijarvi, kuusijar@lut.fi - syscall jump table and modifications
;; Jarek Pelczar, jarekp3@wp.pl - AMD-compatible MTRRs
;;
;; KolibriOS is distributed in the hope that it will be useful, but WITHOUT ANY
;; WARRANTY. No author or distributor accepts responsibility to anyone for the
;; consequences of using it or for whether it serves any particular purpose or
+28
View File
@@ -83,6 +83,31 @@ macro arp_init {
}
;-----------------------------------------------------------------;
; ;
; arp_clear_device: Empty the ARP table and counters of a removed ;
; device. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro arp_clear_device {
xor eax, eax
mov [ARP_entries + edi], eax
mov [ARP_packets_tx + edi], eax
mov [ARP_packets_rx + edi], eax
mov [ARP_conflicts + edi], eax
push edi
imul edi, (ARP_TABLE_SIZE * sizeof.ARP_entry)/4
add edi, ARP_table
mov ecx, (ARP_TABLE_SIZE * sizeof.ARP_entry)/2
rep stosw
pop edi
}
;-----------------------------------------------------------------;
; ;
; arp_decrease_entry_ttls ;
@@ -313,6 +338,9 @@ arp_output_request:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\
[esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx
test ebx, ebx ; device is gone
jz .exit
mov ax, ETHER_PROTO_ARP
mov ecx, sizeof.ARP_header
mov edx, ETH_BROADCAST ; broadcast mac
+40 -1
View File
@@ -105,6 +105,29 @@ macro ipv4_init {
}
;-----------------------------------------------------------------;
; ;
; ipv4_clear_device: Reset the IPv4 settings of a removed device, ;
; so no route points to it anymore. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro ipv4_clear_device {
xor eax, eax
mov [IPv4_address + edi], eax
mov [IPv4_subnet + edi], eax
mov [IPv4_nameserver + edi], eax
mov [IPv4_gateway + edi], eax
mov [IPv4_broadcast + edi], eax
mov [IPv4_packets_tx + edi], eax
mov [IPv4_packets_rx + edi], eax
mov [IPv4_packets_dumped + edi], eax
}
;-----------------------------------------------------------------;
; ;
; Decrease TimeToLive of all fragment slots ;
@@ -757,6 +780,8 @@ ipv4_output_raw:
push esi eax
call ipv4_route
test eax, eax
jz .arp_error
call arp_ip_to_mac
test eax, 0xffff0000 ; error bits
@@ -967,6 +992,8 @@ ipv4_route:
; Check for on-link
xor edi, edi
.loop:
cmp [net_device_list + edi], 0 ; skip slots without a device
je .next
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
jz .next
@@ -983,7 +1010,10 @@ ipv4_route:
mov edi, 4 ; skip loopback device
.loop_gw:
cmp [IPv4_gateway + edi], 0
je .next_gw
cmp [net_device_list + edi], 0
jne .found_gw
.next_gw:
add edi, 4
cmp edi, 4*NET_DEVICES_MAX
jb .loop_gw
@@ -1049,14 +1079,23 @@ ipv4_route:
cmp eax, 0xffffffff
je @f
; An interface without an address (DHCP not done yet, or the lease
; dropped when the link went down) can send nothing but broadcasts: with
; address and mask both zero every destination compared as on-link, and
; the stack went asking ARP for internet addresses on behalf of 0.0.0.0.
test edx, edx
jz .fail
; Check if we should route to gateway or not
mov ebx, [IPv4_address + edi]
mov ebx, edx
and ebx, [IPv4_subnet + edi]
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ecx, ebx
je @f
mov eax, [IPv4_gateway + edi]
test eax, eax
jz .fail ; off-link and no gateway
@@:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
ret
+44 -1
View File
@@ -61,6 +61,41 @@ macro eth_init {
}
;-----------------------------------------------------------------;
; ;
; eth_clear_device: Drop the frames of a removed device that are ;
; still waiting in the input queue, their ;
; NET_BUFF.device will not be valid anymore. ;
; ;
; IN: ebx = device ptr ;
; ;
;-----------------------------------------------------------------;
macro eth_clear_device {
local .loop, .done
spin_lock_irqsave
mov esi, [ETH_frame_head]
.loop:
cmp esi, ETH_frame_head
je .done
mov eax, esi
mov esi, [esi + NET_BUFF.NextPtr]
cmp [eax + NET_BUFF.device], ebx
jne .loop
; unlink it, ETH_frame_head/tail double as the NextPtr/PrevPtr of the list head
mov ecx, [eax + NET_BUFF.PrevPtr]
mov [ecx + NET_BUFF.NextPtr], esi
mov [esi + NET_BUFF.PrevPtr], ecx
dec [ETH_frame_queued]
stdcall net_buff_free, eax
jmp .loop
.done:
spin_unlock_irqrestore
}
align 4
; This function is called by ethernet drivers.
; Push the received ethernet packet onto the ethernet input queue.
@@ -245,6 +280,9 @@ eth_output:
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: size=%u device=%x\n", ecx, ebx
test ebx, ebx
jz .no_device
cmp ecx, [ebx + ETH_DEVICE.mtu]
ja .too_large
@@ -295,11 +333,16 @@ eth_output:
ret
.too_large:
inc [eax + NET_DEVICE.packets_tx_err]
inc [ebx + NET_DEVICE.packets_tx_err]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Packet too large!\n"
xor eax, eax
ret
.no_device:
DEBUGF DEBUG_NETWORK_ERROR, "ETH_output: no device!\n"
xor eax, eax
ret
;-----------------------------------------------------------------;
+10
View File
@@ -597,6 +597,16 @@ net_remove_device:
mov dword [edi-4], eax
dec [net_device_count]
;-----------------------------------------------------------------
; Forget everything the protocols still know about this interface.
; Otherwise its IP address keeps matching in ipv4_route, and output
; ends up calling through the now empty net_device_list slot.
sub edi, net_device_list + 4 ; device number * 4
ipv4_clear_device
arp_clear_device
eth_clear_device
call net_send_event
xor eax, eax
+1 -1
View File
@@ -63,7 +63,7 @@ TCP_OPT_TIMESTAMP = 8
TCP_time_MSL = 47 ; max segment lifetime (30s)
TCP_time_re_min = 2 ; min retransmission (1,28s)
TCP_time_re_max = 100 ; max retransmission (64s)
TCP_time_pers_min = 8 ; min persist (5,12s)
TCP_time_pers_min = 2 ; min persist (1,28s)
TCP_time_pers_max = 94 ; max persist (60,16s)
TCP_time_keep_init = 118 ; connection establishment (75,52s)
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h)
+12 -2
View File
@@ -525,8 +525,11 @@ endl
test [temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp_rtt
cmp [ebx + TCP_SOCKET.ts_ecr], 0 ; nothing echoed
je .no_timestamp_rtt
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
shr eax, 6 ; 1/100 s -> 640 ms ticks
inc eax
call tcp_xmit_timer
jmp .rtt_done
@@ -1114,8 +1117,11 @@ endl
test [temp_bits], TCP_BIT_TIMESTAMP
jz .timestamp_not_present
cmp [ebx + TCP_SOCKET.ts_ecr], 0 ; nothing echoed
je .timestamp_not_present
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
shr eax, 6 ; 1/100 s -> 640 ms ticks
inc eax
call tcp_xmit_timer
jmp .rtt_done_
@@ -1422,10 +1428,14 @@ endl
test [edx + TCP_header.Flags], TH_ACK
jz .drop
mov eax, ebx
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax
mov ebx, ECONNREFUSED
call tcp_drop
jmp .drop
jmp .drop_no_socket
@@:
;-----------------------------------------------------------------------------------
+9 -7
View File
@@ -234,23 +234,25 @@ endl
mov ebx, TCP_max_win
shl ebx, cl
pop ecx
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
; Clamp to available window first, then subtract what the peer already knows (as in BSD)
cmp ebx, ecx
jl @f
jbe @f
mov ebx, ecx
@@:
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: we can increase window by %d bytes\n", ebx
mov edi, [eax + TCP_SOCKET.t_maxseg]
shl edi, 1
cmp ebx, edi
jae .send
cmp ebx, edi ; signed: may be negative if window shrank
jge .send
cmp ebx, SOCKET_BUFFER_SIZE/2
jae .send
jge .send
.no_window:
@@ -624,7 +626,7 @@ endl
mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything?
je @f
jne @f
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer
mov [eax + TCP_SOCKET.t_rtseq], edi
inc [TCPS_segstimed]
+15 -5
View File
@@ -96,7 +96,7 @@ macro tcp_init_socket socket {
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min
;;; TODO: TCP_time_rangeset
mov [socket + TCP_SOCKET.t_rxtcur], TCP_time_rtt_default
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift
@@ -518,7 +518,7 @@ tcp_xmit_timer:
inc [TCPS_rttupdated]
cmp [ebx + TCP_SOCKET.t_rtt], 0
cmp [ebx + TCP_SOCKET.t_srtt], 0 ; first sample?
je .no_rtt_yet
; srtt is stored as a fixed point with 3 bits after the binary point.
@@ -534,7 +534,7 @@ tcp_xmit_timer:
pop ecx
add [ebx + TCP_SOCKET.t_srtt], eax
ja @f
jg @f ; signed: delta may be negative
mov [ebx + TCP_SOCKET.t_srtt], 1
@@:
@@ -556,10 +556,10 @@ tcp_xmit_timer:
pop edx
add [ebx + TCP_SOCKET.t_rttvar], eax
ja @f
jg @f
mov [ebx + TCP_SOCKET.t_rttvar], 1
@@:
ret
jmp .rto
.no_rtt_yet:
@@ -572,6 +572,16 @@ tcp_xmit_timer:
mov [ebx + TCP_SOCKET.t_rttvar], eax
pop ecx
.rto:
; Retransmit timeout = srtt + 4*rttvar, reset the backoff
push ecx
mov ecx, [ebx + TCP_SOCKET.t_srtt]
shr ecx, TCP_RTT_SHIFT
add ecx, [ebx + TCP_SOCKET.t_rttvar]
tcpt_rangeset [ebx + TCP_SOCKET.t_rxtcur], ecx, TCP_time_re_min, TCP_time_re_max
pop ecx
mov [ebx + TCP_SOCKET.t_rxtshift], 0
mov [ebx + TCP_SOCKET.t_rtt], 0 ; the timed segment is done
ret
+93
View File
@@ -91,6 +91,10 @@ proc tcp_timer_640ms
jne .loop
inc [eax + TCP_SOCKET.t_idle]
cmp [eax + TCP_SOCKET.t_rtt], 0 ; timing a segment?
je @f
inc [eax + TCP_SOCKET.t_rtt]
@@:
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .check_more2
@@ -99,6 +103,95 @@ proc tcp_timer_640ms
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: Retransmission timer expired\n", eax
; Lock socket, an ACK may have stopped or restarted the timer meanwhile
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .rexmt_cancelled
cmp [eax + TCP_SOCKET.timer_retransmission], 0
jne .rexmt_cancelled
; Too many retransmissions? Drop the connection
inc [eax + TCP_SOCKET.t_rxtshift]
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift
jbe .rexmt
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: too many retransmissions, dropping\n", eax
push [eax + SOCKET.NextPtr]
mov ebx, ETIMEDOUT
call tcp_drop
pop eax
jmp .check_only
.rexmt_cancelled:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
jmp .check_more2
.rexmt:
push ebx ecx edx
; Restart timer with backoff: t_rxtcur << min(t_rxtshift, 6)
mov ebx, [eax + TCP_SOCKET.t_rxtcur]
mov cl, [eax + TCP_SOCKET.t_rxtshift]
cmp cl, 6
jbe @f
mov cl, 6
@@:
shl ebx, cl
cmp ebx, TCP_time_re_min
jae @f
mov ebx, TCP_time_re_min
@@:
cmp ebx, TCP_time_re_max
jbe @f
mov ebx, TCP_time_re_max
@@:
mov [eax + TCP_SOCKET.timer_retransmission], ebx
; Resend from the last acknowledged byte, don't time it
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
mov [eax + TCP_SOCKET.t_rtt], 0
mov [eax + TCP_SOCKET.t_dupacks], 0
; Slow start: ssthresh = max(2, min(wnd, cwnd) / 2 / mss) * mss, cwnd = mss
mov ecx, [eax + TCP_SOCKET.t_maxseg]
mov edx, [eax + TCP_SOCKET.SND_WND]
cmp edx, [eax + TCP_SOCKET.SND_CWND]
jbe @f
mov edx, [eax + TCP_SOCKET.SND_CWND]
@@:
mov ebx, eax ; socket ptr
mov eax, edx
shr eax, 1
xor edx, edx
div ecx
cmp eax, 2
jae @f
mov eax, 2
@@:
mul ecx
mov [ebx + TCP_SOCKET.SND_SSTHRESH], eax
mov [ebx + TCP_SOCKET.SND_CWND], ecx
mov eax, ebx
pop edx ecx ebx
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
push eax
call tcp_output
pop eax
+4
View File
@@ -180,6 +180,8 @@ tcp_connect:
.nomem:
pop edx eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
xor eax, eax
dec eax
mov ebx, ENOMEM
@@ -194,6 +196,8 @@ tcp_connect:
.enoroute:
pop eax
popa
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
xor eax, eax
dec eax
mov ebx, EADDRNOTAVAIL
+5 -3
View File
@@ -1618,21 +1618,23 @@ endl
; Open a new TCP socket
mcall socket, AF_INET4, SOCK_STREAM, 0
test eax, eax
jz .error3
cmp eax, -1
je .error3
mov [socketnum], eax
DEBUGF 1, "Socket: 0x%x\n", eax
; Connect to the server
mcall connect, [socketnum], [sockaddr], 18
test eax, eax
jnz .error3
jnz .error4
DEBUGF 1, "Socket is now connected.\n"
invoke freeaddrinfo ; Free allocated memory
mov eax, [socketnum]
ret
.error4:
mcall close, [socketnum]
.error3:
DEBUGF 2, "Could not connect to the remote server\n"
invoke freeaddrinfo ; Free allocated memory