diff options
| author | user <user@clank> | 2026-07-17 12:54:43 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 12:54:43 +0200 |
| commit | 26a2ccb8368a9968b6ec16068ad68dd0edb40641 (patch) | |
| tree | 1396180dc2a8e635c58e10b994ae15fe7705e542 /src | |
| parent | net: proper socket API in the kernel (SYS_NET) - no more SLIP in userland (diff) | |
| download | gbos-26a2ccb8368a9968b6ec16068ad68dd0edb40641.tar.gz gbos-26a2ccb8368a9968b6ec16068ad68dd0edb40641.tar.xz gbos-26a2ccb8368a9968b6ec16068ad68dd0edb40641.zip | |
net: UDP sockets in the kernel + a DNS resolver (real hostname lookups)
Adds UDP to the kernel socket layer on top of the ICMP core:
- net_sum() (raw folded sum) split out of net_cksum() so a UDP pseudo-header
(src/dst IP + proto + length) can seed the segment checksum.
- udp_send: builds IP+UDP with the pseudo-header checksum; NET_BIND sets the
local/source port; net_connect sets the peer.
- udp_in: demuxes inbound UDP by destination port to the bound socket
(net_find_udp), delivers the payload + source addr.
Also fixes a real recv bug: net_pump clobbers BC/DE/HL, so the old recv timeout
counted in registers and was effectively random. recv now counts in WRAM.
New `nslookup HOST` (PROG_NSLOOKUP=28, bank 30): builds a DNS A query and parses
the answer (with 0xC0 name-compression) entirely in userland over a UDP socket -
the kernel never sees DNS, just UDP. Verified through the bridge NAT:
/# nslookup example.com -> example.com -> 172.66.147.243
This gives us name resolution for the TCP/HTTP demo next.
Diffstat (limited to '')
| -rw-r--r-- | src/programs.asm | 7 | ||||
| -rw-r--r-- | src/socket.asm | 342 |
2 files changed, 328 insertions, 21 deletions
diff --git a/src/programs.asm b/src/programs.asm index 854b2d2..9ae7f58 100644 --- a/src/programs.asm +++ b/src/programs.asm @@ -124,6 +124,9 @@ ProgNetd: SECTION "prog_ping", ROMX[$4000], BANK[29] ProgPing: INCBIN "c/ping.bin" +SECTION "prog_nslookup", ROMX[$4000], BANK[30] +ProgNslookup: + INCBIN "c/nslookup.bin" ; ----------------------------------------------------------------------------- ; PROG_SH (bank 3) - the shell, now written in C (c/sh.c): parses >/</| and @@ -222,6 +225,8 @@ ProgramTable:: dw ProgNetd db LOW(BANK(ProgPing)), HIGH(BANK(ProgPing)) dw ProgPing + db LOW(BANK(ProgNslookup)), HIGH(BANK(ProgNslookup)) + dw ProgNslookup ; ----------------------------------------------------------------------------- ; NameTable (ROM0) - command name -> program id, searched by sys_lookup. @@ -284,4 +289,6 @@ NameTable:: db PROG_NETD db "ping", 0 db PROG_PING + db "nslookup", 0 + db PROG_NSLOOKUP db 0 diff --git a/src/socket.asm b/src/socket.asm index f0d8444..ef9d4b9 100644 --- a/src/socket.asm +++ b/src/socket.asm @@ -35,6 +35,8 @@ wNetTx:: DS 256 ; tx build buffer wNetRxBuf:: DS 256 ; rx frame assembly / parse wNetRxLen:: DS 1 wNetRxEsc:: DS 1 +wNetUdpPort:: DS 2 ; scratch: UDP dst port being demuxed +wNetTO:: DS 3 ; recv timeout counter (net_pump clobbers registers) SECTION "socket", ROM0 @@ -123,9 +125,10 @@ net_slip_send: ret ; ----------------------------------------------------------------------------- -; net_cksum(HL = buf, B = len, DE = seed) -> HL = inverted 16-bit checksum. -; One's-complement sum (RFC 1071), carry-folded each add. len <= 255. -net_cksum: +; net_sum(HL = buf, B = len, DE = seed) -> DE = folded 16-bit one's-complement +; sum (RFC 1071), carry-folded each add. len <= 255. Used to chain a pseudo- +; header into a segment checksum. +net_sum: .loop ld a, b cp 2 @@ -147,15 +150,19 @@ net_cksum: .tail ld a, b or a - jr z, .fold + ret z ld a, [hl] ; last odd byte -> high half add d ld d, a - jr nc, .fold + ret nc inc e - jr nz, .fold + ret nz inc d -.fold + ret + +; net_cksum(HL, B, DE=seed) -> HL = inverted 16-bit checksum. +net_cksum: + call net_sum ld a, d cpl ld h, a @@ -201,11 +208,30 @@ sys_net:: jp z, net_op_recv cp NET_CLOSE jp z, net_op_close + cp NET_BIND + jp z, net_op_bind cp NET_POLL jp z, net_op_poll ld a, $FF ret +; ---- bind(sock, port) - set local port ----------------------------------- +net_op_bind: + ld a, [wNetReq + NR_SOCK] + call net_sock_ptr + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [wNetReq + NR_PORT] + ld [hl+], a + ld a, [wNetReq + NR_PORT + 1] + ld [hl], a + xor a + ret + ; ---- socket(type) -> A = sockid or $FF --------------------------------------- net_op_socket: ld c, 0 @@ -281,6 +307,8 @@ net_op_send: ld a, [hl] ; SK_TYPE cp SOCK_ICMP jp z, icmp_send + cp SOCK_UDP + jp z, udp_send ld a, $FF ret @@ -292,21 +320,27 @@ net_op_recv: ld [wNetSockPtr], a ld a, h ld [wNetSockPtr+1], a - ld b, 200 ; outer timeout -.outer - ld de, 0 -.inner + xor a ; 24-bit timeout counter in WRAM (net_pump + ld [wNetTO], a ; clobbers BC/DE/HL, so we can't count in regs) + ld [wNetTO+1], a + ld [wNetTO+2], a +.wait call net_pump - call sock_has_rx ; Z clear (nz) if HASRX set + call sock_has_rx ; nz if a datagram is buffered jr nz, .got - dec de - ld a, d - or e - jr nz, .inner - call SchedYield - dec b - jr nz, .outer - ld a, $FF ; timeout + ld hl, wNetTO + inc [hl] + jr nz, .wait ; low byte didn't wrap -> spin (no yield) + call SchedYield ; every 256 pumps, yield to the scheduler + inc hl + inc [hl] + jr nz, .wait + inc hl + inc [hl] + ld a, [hl] + cp 250 ; timeout after ~250*65536 pumps + jr c, .wait + ld a, $FF ret .got ; copy sock.RXBUF -> process NR_BUF, len = sock.RXLEN, NR_IP = sock.SRCIP @@ -493,6 +527,153 @@ icmp_send: ret ; ============================================================================= +; udp_send - build + transmit a UDP datagram from the current socket. +; ============================================================================= +udp_send: + ld a, $45 + ld [wNetTx+0], a + xor a + ld [wNetTx+1], a + ld [wNetTx+2], a + ld a, [wNetReq+NR_LEN] + add 28 ; 20 IP + 8 UDP + payload + ld [wNetTx+3], a + xor a + ld [wNetTx+4], a + ld [wNetTx+5], a + ld [wNetTx+6], a + ld [wNetTx+7], a + ld a, 64 + ld [wNetTx+8], a + ld a, 17 + ld [wNetTx+9], a ; proto UDP + xor a + ld [wNetTx+10], a + ld [wNetTx+11], a + ld a, [wNetOurIP+0] + ld [wNetTx+12], a + ld a, [wNetOurIP+1] + ld [wNetTx+13], a + ld a, [wNetOurIP+2] + ld [wNetTx+14], a + ld a, [wNetOurIP+3] + ld [wNetTx+15], a + ; dst IP = sock.RIP + call sock_ptr_hl + ld a, l + add SK_RIP + ld l, a + ld a, h + adc 0 + ld h, a + ld de, wNetTx+16 + ld b, 4 +.dip + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .dip + ; UDP header at wNetTx+20: src port = SK_LPORT + call sock_ptr_hl + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [hl+] + ld [wNetTx+20], a + ld a, [hl] + ld [wNetTx+21], a + ; dst port = SK_RPORT + call sock_ptr_hl + ld a, l + add SK_RPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [hl+] + ld [wNetTx+22], a + ld a, [hl] + ld [wNetTx+23], a + ; length = 8 + payload + xor a + ld [wNetTx+24], a + ld a, [wNetReq+NR_LEN] + add 8 + ld [wNetTx+25], a + xor a + ld [wNetTx+26], a ; cksum = 0 + ld [wNetTx+27], a + ; payload from process buf + ld a, [wNetReq+NR_BUF] + ld l, a + ld a, [wNetReq+NR_BUF+1] + ld h, a + ld de, wNetTx+28 + ld a, [wNetReq+NR_LEN] + ld b, a + or a + jr z, .nopl +.cppl + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .cppl +.nopl + ; UDP checksum with pseudo-header: sum(src+dst IP) + proto + udplen, then seg + ld hl, wNetTx+12 + ld b, 8 + ld de, 0 + call net_sum ; DE = IP-address sum + ld a, e + add 17 ; + protocol + ld e, a + ld a, d + adc 0 + ld d, a + ld a, [wNetReq+NR_LEN] + add 8 ; + udp length + add e + ld e, a + ld a, d + adc 0 + ld d, a ; DE = pseudo-header seed + ld a, [wNetReq+NR_LEN] + add 8 + ld b, a + ld hl, wNetTx+20 + call net_cksum ; HL = inverted checksum + ld a, h + or l + jr nz, .cknz + ld hl, $FFFF ; 0 means "no checksum" -> send all-ones +.cknz + ld a, h + ld [wNetTx+26], a + ld a, l + ld [wNetTx+27], a + ; IP checksum + ld hl, wNetTx + ld b, 20 + ld de, 0 + call net_cksum + ld a, h + ld [wNetTx+10], a + ld a, l + ld [wNetTx+11], a + ld a, [wNetReq+NR_LEN] + add 28 + ld b, a + ld hl, wNetTx + call net_slip_send + xor a + ret + +; ============================================================================= ; net_pump - read available serial bytes, assemble SLIP frames, process each. ; Non-blocking: returns when the link has no more bytes ready. ; ============================================================================= @@ -576,7 +757,9 @@ process_frame: ld [wNetIhl], a ; ihl in bytes ld a, [wNetRxBuf+9] ; protocol cp 1 - jr z, icmp_in + jp z, icmp_in + cp 17 + jp z, udp_in ret ; ----------------------------------------------------------------------------- @@ -736,6 +919,123 @@ icmp_reply: jr nz, .cp ret +; ----------------------------------------------------------------------------- +; udp_in - deliver an inbound UDP datagram to the socket bound to its dst port. +udp_in: + ld a, [wNetIhl] + add 2 + ld e, a + ld d, 0 + ld hl, wNetRxBuf + add hl, de ; HL = &udp.dstport + ld a, [hl+] + ld [wNetUdpPort], a + ld a, [hl] + ld [wNetUdpPort+1], a + call net_find_udp ; HL = &sock, CF if none + ret c + ; deliver: HASRX=1, SRCIP, RXLEN + RXBUF (payload after ihl+8) + push hl + ld a, l + add SK_HASRX + ld l, a + ld a, h + adc 0 + ld h, a + ld a, 1 + ld [hl], a + pop hl + push hl + ld a, l + add SK_SRCIP + ld l, a + ld a, h + adc 0 + ld h, a + ld de, wNetRxBuf+12 + ld b, 4 +.sip + ld a, [de] + ld [hl+], a + inc de + dec b + jr nz, .sip + pop hl + ; payload offset = ihl + 8, length = rxlen - ihl - 8 + ld a, [wNetIhl] + add 8 + ld e, a + ld d, 0 + ld a, [wNetRxLen] + sub e + ld c, a ; C = payload length + push hl + ld a, l + add SK_RXLEN + ld l, a + ld a, h + adc 0 + ld h, a + ld a, c + ld [hl], a + pop hl + ld a, l + add SK_RXBUF + ld l, a + ld a, h + adc 0 + ld h, a ; HL = &sock.RXBUF (dest) + push hl + ld hl, wNetRxBuf + add hl, de ; HL = &payload (src) + pop de ; DE = dest + ld a, c + or a + ret z +.cp + ld a, [hl+] + ld [de], a + inc de + dec c + jr nz, .cp + ret + +; net_find_udp -> HL = &UDP socket bound to wNetUdpPort, CF set if none +net_find_udp: + ld c, 0 +.l + ld a, c + call net_sock_ptr ; HL = &sock (clobbers B, DE) + ld a, [hl] + cp SOCK_UDP + jr nz, .nx + push hl + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [wNetUdpPort] + cp [hl] + jr nz, .nomatch + inc hl + ld a, [wNetUdpPort+1] + cp [hl] + jr nz, .nomatch + pop hl ; match + or a ; CF = 0 + ret +.nomatch + pop hl +.nx + inc c + ld a, c + cp MAX_SOCKS + jr c, .l + scf + ret + ; net_swap_ip - swap the src/dst IP fields of the frame in wNetRxBuf net_swap_ip: ld hl, wNetRxBuf+12 |
