os

An operating system
git clone https://erai.gay/code/os/
Log | Files | Refs | README | LICENSE

commit b0f1cc1f141f46ee2c689e683dd0af50f6994077
parent b9aa4c3be438afc5114cb1ab3b53899d6ab5bedc
Author: erai <erai@omiltem.net>
Date:   Mon,  6 May 2024 17:32:27 -0400

tcp recv

Diffstat:
Mkernel.c | 312++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 277 insertions(+), 35 deletions(-)

diff --git a/kernel.c b/kernel.c @@ -763,6 +763,10 @@ struct arp_entry { } struct tcp_state { + state: int; + + event_func: func(tcb:*tcp_state); + // Network tuple peer_ip: int; peer_port: int; @@ -772,17 +776,9 @@ struct tcp_state { send_una: int; send_nxt: int; send_wnd: int; - send_wl1: int; - send_wl2: int; - send_iss: int; - rcv_nxt: int; - rcv_wnd: int; - rcv_up: int; - rcv_irs: int; - - send_buf: *byte; - recv_buf: *byte; + recv_nxt: int; + recv_wnd: int; } struct global { @@ -2053,11 +2049,56 @@ send_tcp(pkt: *txinfo) { pkt.ip_proto = IP_TCP; - xxd(pkt.buf, pkt.len); - send_ip(pkt); } +alloc_tcp(): *tcp_state { + var global: *global; + var flags: int; + var i: int; + var tcb: *tcp_state; + + global = g(); + + flags = rdflags(); + cli(); + + i = 0; + loop { + if i == global.tcp_count { + wrflags(flags); + return 0:*tcp_state; + } + + if !global.tcp[i] { + tcb = alloc():*tcp_state; + bzero(tcb:*byte, sizeof(*tcb)); + global.tcp[i] = tcb; + wrflags(flags); + return tcb; + } + + i = i + 1; + } +} + +tcp_listen(port: int, event_func: func(tcb: *tcp_state)) { + var global: *global; + var tcb: *tcp_state; + global = g(); + + tcb = alloc_tcp(); + + tcb.event_func = event_func; + tcb.sock_ip = global.ip; + tcb.sock_port = port; + + tcb.state = TCP_LISTEN; +} + +accept_ssh(listener: *tcp_state) { +} + send_rst(pkt: *rxinfo) { var tx: *txinfo; tx = alloc_tx(); @@ -2089,62 +2130,261 @@ send_rst(pkt: *rxinfo) { free_tx(tx); } -handle_tcp(pkt: *rxinfo) { +send_ack(tcb: *tcp_state) { + var tx: *txinfo; + tx = alloc_tx(); + + tx.ip_src = tcb.sock_ip; + tx.ip_dest = tcb.peer_ip; + tx.tcp_src = tcb.sock_port; + tx.tcp_dest = tcb.peer_port; + tx.tcp_win = tcb.recv_wnd; + tx.tcp_opt = 0:*byte; + tx.tcp_opt_len = 0; + tx.len = 0; + + tx.tcp_seq = tcb.send_nxt; + tx.tcp_ack = tcb.recv_nxt; + tx.tcp_flags = TCP_ACK; + + send_tcp(tx); + + free_tx(tx); +} + +enum { + TCP_CLOSED = 0, + TCP_LISTEN = 1, + TCP_SYN_SENT = 2, + TCP_SYN_RECV = 3, + TCP_ESTAB = 4, + TCP_FIN_WAIT_1 = 5, + TCP_FIN_WAIT_2 = 6, + TCP_CLOSE_WAIT = 7, + TCP_LAST_ACK = 8, + TCP_CLOSING = 9, + TCP_TIME_WAIT = 10, +} + +find_tcb(pkt: *rxinfo): *tcp_state { var global: *global; var tcb: *tcp_state; var i: int; global = g(); - // Find the tcb + // Find a connection i = 0; loop { if i == global.tcp_count { - tcb = 0:*tcp_state; break; } tcb = global.tcp[i]; + if tcb + && tcb.state >= TCP_SYN_SENT && tcb.peer_ip == pkt.ip_src && tcb.peer_port == pkt.tcp_src && tcb.sock_ip == pkt.ip_dest && tcb.sock_port == pkt.tcp_dest { + return tcb; + } + + i = i + 1; + } + + // Find a listener + i = 0; + loop { + if i == global.tcp_count { break; } + tcb = global.tcp[i]; + + if tcb + && tcb.state == TCP_LISTEN + && tcb.sock_ip == pkt.ip_dest + && tcb.sock_port == pkt.tcp_dest { + return tcb; + } + i = i + 1; } - // Send a reset - if !tcb { + return 0:*tcp_state; +} + +handle_syn(tcb: *tcp_state, pkt: *rxinfo) { + var c: *tcp_state; + var tx: *txinfo; + + c = alloc_tcp(); + if !c { + send_rst(pkt); + return; + } + + c.peer_ip = pkt.ip_src; + c.peer_port = pkt.tcp_src; + c.sock_ip = pkt.ip_dest; + c.sock_port = pkt.tcp_dest; + + c.send_nxt = rand(); + c.send_una = c.send_nxt; + c.send_wnd = pkt.tcp_win; + + c.recv_nxt = pkt.tcp_seq + 1; + c.recv_wnd = 512; + + c.state = TCP_SYN_RECV; + + tx = alloc_tx(); + + tx.ip_src = c.sock_ip; + tx.ip_dest = c.peer_ip; + tx.tcp_src = c.sock_port; + tx.tcp_dest = c.peer_port; + tx.tcp_win = c.recv_wnd; + tx.tcp_opt = 0:*byte; + tx.tcp_opt_len = 0; + tx.len = 0; + + tx.tcp_flags = TCP_SYN | TCP_ACK; + tx.tcp_seq = c.send_nxt; + tx.tcp_ack = c.recv_nxt; + + c.send_nxt = c.send_nxt + 1; + + send_tcp(tx); + + free_tx(tx); +} + +handle_seg(tcb: *tcp_state, pkt: *rxinfo) { + var offset: int; + var wnd: int; + + // 1. Check sequence + if pkt.tcp_seq != tcb.recv_nxt { if pkt.tcp_flags & TCP_RST { return; } - - send_rst(pkt); + kputs("bad seq\n"); + send_ack(tcb); return; } - // Got a reset + // 2. Check RST if pkt.tcp_flags & TCP_RST { - // close the connection + tcb.state = TCP_CLOSED; return; } - // TODO: slow-start add-inc-mul-dec retrans-timer fast-retrans listen maxss - // TODO: ssh random + // 4. Check SYN + if pkt.tcp_flags & TCP_SYN { + kputs("bad syn\n"); + send_ack(tcb); + return; + } - // Handle data - kputip(pkt.ip_src); - kputc(':'); - kputd(pkt.tcp_src); - kputc('>'); - kputip(pkt.ip_dest); - kputc(':'); - kputd(pkt.tcp_dest); - kputc('\n'); - xxd(pkt.tcp_opt, pkt.tcp_opt_len); - xxd(pkt.tcp_seg, pkt.tcp_seg_len); + // 5. Check ACK + if pkt.tcp_flags & TCP_ACK == 0 { + return; + } + + // Validate ACK + offset = (pkt.tcp_ack - tcb.send_una) & ((1 << 31) - 1); + wnd = (tcb.send_nxt - tcb.send_una) & ((1 << 31) - 1); + if offset > wnd { + kputs("bad ack\n"); + send_ack(tcb); + return; + } + + if tcb.state == TCP_SYN_RECV { + tcb.state = TCP_ESTAB; + } + + if tcb.state == TCP_FIN_WAIT_1 && tcb.send_una == tcb.send_nxt { + tcb.state = TCP_FIN_WAIT_2; + } + + if tcb.state == TCP_CLOSING && tcb.send_una == tcb.send_nxt { + tcb.state = TCP_TIME_WAIT; + } + + if tcb.state == TCP_LAST_ACK { + tcb.state = TCP_CLOSED; + return; + } + + tcb.send_una = pkt.tcp_ack; + + // 7. Process data + if pkt.tcp_seg_len > 0 { + tcb.recv_nxt = tcb.recv_nxt + pkt.tcp_seg_len; + xxd(pkt.tcp_seg, pkt.tcp_seg_len); + send_ack(tcb); + } + + // 8. Check FIN + if pkt.tcp_flags & TCP_FIN { + if tcb.state == TCP_SYN_RECV || tcb.state == TCP_ESTAB { + tcb.state = TCP_CLOSE_WAIT; + } + + if tcb.state == TCP_FIN_WAIT_1 { + if tcb.send_una == tcb.send_nxt { + tcb.state = TCP_TIME_WAIT; + } else { + tcb.state = TCP_CLOSING; + } + } + + if tcb.state == TCP_FIN_WAIT_2 { + tcb.state = TCP_TIME_WAIT; + } + } + + if tcb.state == TCP_TIME_WAIT { + // reset the timer + } +} + +handle_tcp(pkt: *rxinfo) { + var tcb: *tcp_state; + + if pkt.tcp_src == 0 || pkt.tcp_dest == 0 { + return; + } + + // Find state for the incoming packet + tcb = find_tcb(pkt); + if !tcb || tcb.state == TCP_CLOSED { + send_rst(pkt); + } else if tcb.state == TCP_LISTEN { + // Handle incoming connection + if pkt.tcp_flags & TCP_RST { + return; + } + + if pkt.tcp_flags & TCP_ACK { + send_rst(pkt); + return; + } + + if pkt.tcp_flags & (TCP_FIN|TCP_SYN|TCP_PSH) != TCP_SYN { + return; + } + + handle_syn(tcb, pkt); + } else if tcb.state == TCP_SYN_SENT { + // out going connections not implemented yet + send_rst(pkt); + } else { + handle_seg(tcb, pkt); + } } enum { @@ -2250,7 +2490,7 @@ rx_ip(pkt: *rxinfo) { + pkt.ip_proto + (pkt.ip_len - 20); - memo_arp(pkt.ether_src, pkt.ip_src, 0); + memo_arp(pkt.ether_src, pkt.ip_src, 1); if pkt.ip_dest != global.ip { return; @@ -2993,6 +3233,8 @@ _kstart(mb: int) { scan_pci(pci, init_realtek); scan_pci(pci, init_ahci); + tcp_listen(22, accept_ssh); + // Wait for interrupts kputs("zzz\n"); loop {