aboutsummaryrefslogtreecommitdiffstats
path: root/usr/sock.h
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-22 21:54:03 +0200
committeruser <user@clank>2026-09-22 21:54:03 +0200
commitdcd1f1320c102f9cc4cff7eee387ec538304f7d1 (patch)
treee0b48e345c8d34a5f110378e795b8690120ebd55 /usr/sock.h
parentbuild: rebuild every object when include/*.inc changes (diff)
downloadgbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.gz
gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.xz
gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.zip
net: one socket per client - four-tuple demux, spawning accept, bounded close
A server could only ever hold ONE connection, which makes an ircd pointless: listen() turned the listener into the connection, and net_find_tcp demuxed on local port alone, so two clients on 6667 were indistinguishable. - net_find_tcp now matches an established socket on the full four-tuple (local port + peer IP + peer port) and only falls back to a LISTENing socket when none matches. - A SYN at a listener no longer consumes it: tcp_spawn_conn clones a new socket (local port + owning pid, so exit/kill still reclaims it) and the listener keeps listening. accept() returns that socket; closing it leaves the listener alone. A full table drops the SYN, and the client's retransmit is taken once a slot frees. - tcp_peer_is_current preserves BC: net_find_tcp scans with the socket index there, and the compare needs BC for the rx buffer base. Without this the scan died at the first mismatching socket, so a second client's handshake ACK never reached its socket and it hung in SYNRCVD - tcpdump showed our SYN+ACK going out and the client's NICK retransmitted five times into silence. - listen() only conflicts with another LISTENER on the port; connections share it by design now. - MAX_SOCKS 4 -> 6 (listener + 3 clients + 2 spare; 537 bytes of WRAM0 still free), and SK_ACC marks a spawned connection as not yet accepted. tcp_close's settle is also time-boxed to ~250 ms of real time. It counted PUMPS (8 x 8000), which is ~30s of wall clock when the peer never answers - invisible with well-behaved peers, but a daemon refusing a client that stayed connected froze itself, and every other client with it, for half a minute. httpd keeps its listener open across requests now, so the next client's SYN is accepted immediately instead of being dropped between close and re-listen: three parallel clients go 0.1/1.1/2.1s -> 0.4/1.0/0.1s.
Diffstat (limited to 'usr/sock.h')
-rw-r--r--usr/sock.h47
1 files changed, 33 insertions, 14 deletions
diff --git a/usr/sock.h b/usr/sock.h
index efe7547..c27b0f4 100644
--- a/usr/sock.h
+++ b/usr/sock.h
@@ -21,35 +21,47 @@ static struct netreq _nr;
/* -> socket id, or 0xFF if the table is full */
static unsigned char net_socket(unsigned char type) {
- _nr.op = 0; _nr.sock = type;
+ _nr.op = 0;
+ _nr.sock = type;
return sys_net(&_nr);
}
/* set the remote address/port for send()/recv() */
static unsigned char net_connect(unsigned char s, const unsigned char *ip, unsigned int port) {
unsigned char i;
- _nr.op = 1; _nr.sock = s;
+ _nr.op = 1;
+ _nr.sock = s;
for (i = 0; i < 4; i++) _nr.ip[i] = ip[i];
- _nr.port_hi = (unsigned char)(port >> 8); _nr.port_lo = (unsigned char)port;
+ _nr.port_hi = (unsigned char)(port >> 8);
+ _nr.port_lo = (unsigned char)port;
return sys_net(&_nr);
}
/* send 'len' payload bytes to the connected peer -> 0 ok / 0xFF error */
static unsigned char net_send(unsigned char s, void *buf, unsigned char len) {
- _nr.op = 2; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = len;
+ _nr.op = 2;
+ _nr.sock = s;
+ _nr.buf = (unsigned char *)buf;
+ _nr.len = len;
return sys_net(&_nr);
}
/* receive up to 'max' payload bytes -> length, or 0xFF on timeout */
static unsigned char net_recv(unsigned char s, void *buf, unsigned char max) {
- _nr.op = 3; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = max;
+ _nr.op = 3;
+ _nr.sock = s;
+ _nr.buf = (unsigned char *)buf;
+ _nr.len = max;
return sys_net(&_nr);
}
/* non-blocking recv: one RX pump -> length, 0xFE if nothing buffered yet,
0 on TCP EOF. Loop it with msleep() to own your timeout (see ping). */
static unsigned char net_recv_nb(unsigned char s, void *buf, unsigned char max) {
- _nr.op = 8; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = max;
+ _nr.op = 8;
+ _nr.sock = s;
+ _nr.buf = (unsigned char *)buf;
+ _nr.len = max;
return sys_net(&_nr);
}
-/* passive open: take connections on a local port (TCP). The listener and the
- connection are the same socket, so serve one, close, then listen again. */
+/* passive open: take connections on a local port (TCP). The listener stays a
+ listener - accept() hands back a separate socket for each client. */
static unsigned char net_listen(unsigned char s, unsigned int port) {
_nr.op = 9;
_nr.sock = s;
@@ -57,8 +69,10 @@ static unsigned char net_listen(unsigned char s, unsigned int port) {
_nr.port_lo = (unsigned char)port;
return sys_net(&_nr);
}
-/* -> 0 once a peer has connected, 0xFE if none yet. Never blocks: one RX pump
- per call, so loop it with yield()/msleep() and poll the keyboard too. */
+/* -> the new client's socket id, or 0xFE if nobody is waiting. Never blocks:
+ one RX pump per call, so loop it with yield()/msleep(). Talk to the returned
+ socket with the ordinary send/recv/close; closing it leaves the listener
+ alone, so a server can hold several clients at once (see usr/ircd.c). */
static unsigned char net_accept(unsigned char s) {
_nr.op = 10;
_nr.sock = s;
@@ -66,16 +80,21 @@ static unsigned char net_accept(unsigned char s) {
}
/* set the local (source) port - needed so UDP replies demux back to us */
static unsigned char net_bind(unsigned char s, unsigned int port) {
- _nr.op = 5; _nr.sock = s;
- _nr.port_hi = (unsigned char)(port >> 8); _nr.port_lo = (unsigned char)port;
+ _nr.op = 5;
+ _nr.sock = s;
+ _nr.port_hi = (unsigned char)(port >> 8);
+ _nr.port_lo = (unsigned char)port;
return sys_net(&_nr);
}
static void net_close(unsigned char s) {
- _nr.op = 4; _nr.sock = s; sys_net(&_nr);
+ _nr.op = 4;
+ _nr.sock = s;
+ sys_net(&_nr);
}
/* pump the RX path once (answers inbound pings) without blocking */
static void net_poll(void) {
- _nr.op = 6; sys_net(&_nr);
+ _nr.op = 6;
+ sys_net(&_nr);
}
/* set our IPv4 address (the DHCP client calls this once it has a lease) */
static void net_setip(const unsigned char *ip) {