diff options
| author | user <user@clank> | 2026-07-17 12:39:01 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 12:39:01 +0200 |
| commit | 09ae37313a8b6d567a05a3ae886de78e88cd1afe (patch) | |
| tree | 8d4707fbd2c6c7fec56a1c9c31abbd9dc66cf7bb /c/ping.c | |
| parent | net: ping - GB-originated ICMP echo (a Game Boy pings the real internet) (diff) | |
| download | gbos-09ae37313a8b6d567a05a3ae886de78e88cd1afe.tar.gz gbos-09ae37313a8b6d567a05a3ae886de78e88cd1afe.tar.xz gbos-09ae37313a8b6d567a05a3ae886de78e88cd1afe.zip | |
net: proper socket API in the kernel (SYS_NET) - no more SLIP in userland
The network stack moves into the kernel. src/socket.asm owns SLIP framing,
IPv4, RFC1071 checksums, and ICMP; programs now speak a socket API through one
syscall (SYS_NET, DE=&netreq dispatched by op): net_socket/connect/send/recv/
close/poll (c/sock.h). No program touches SLIP, IP headers, or checksums.
- Socket table (4 sockets) + tx/rx buffers in WRAM0; our IP = 10.0.0.2.
- net_pump: drains the link, reassembles SLIP frames, demuxes IPv4. Inbound
ICMP echo requests are auto-answered in-kernel, so the GB replies to pings
whenever any process pumps RX.
- ICMP sockets: send() emits an echo request to the connected peer; recv()
returns the matching reply (with a spin/yield timeout).
ping.c is now a ~15-line socket client; netd.c is just `for(;;){net_poll();
yield();}`. Verified over tunbridge:
/# ping 1.1.1.1 -> replies from the real internet (kernel builds it all)
host# ping 10.0.0.2 -> 4/4, 0% loss (kernel auto-answers)
Gotchas recorded: gbos.inc isn't a make dep (touch asm after editing); this
crt0 doesn't copy initializers (fill arrays at runtime); and the arg string at
0xA000 overlaps _DATA, so parse targets must sit past it (big buffer first).
UDP and TCP sockets build on this same core next.
Diffstat (limited to '')
| -rw-r--r-- | c/ping.c | 85 |
1 files changed, 19 insertions, 66 deletions
@@ -1,17 +1,14 @@ -#include "netlib.h" -/* ping - originate ICMP echo requests from the Game Boy (10.0.0.2) and print - the replies. Target defaults to 10.0.0.1 (the SLIP peer / host); with NAT on - the bridge it can reach real internet hosts too. Usage: ping [A.B.C.D] */ +#include "sock.h" +/* ping - originate ICMP echo requests via the kernel socket layer. No SLIP, + no IP, no checksums here: the kernel owns all of that. Usage: ping [A.B.C.D] + (default 10.0.0.1, the SLIP peer/host; reaches real hosts through NAT). */ -static unsigned char pkt[128]; +/* rbuf is declared first (and large) so the parse target dst[] lands well past + the argument string the shell leaves at 0xA000 - writing dst must not clobber + the arg while we're still reading it. */ +static unsigned char rbuf[96]; static unsigned char dst[4]; - -static unsigned int sum16(unsigned char *d, unsigned int len, unsigned int sum) { - unsigned int w, i = 0; - while (i + 1 < len) { w = ((unsigned int)d[i] << 8) | d[i + 1]; sum += w; if (sum < w) sum++; i += 2; } - if (len & 1) { w = (unsigned int)d[len - 1] << 8; sum += w; if (sum < w) sum++; } - return sum; -} +static unsigned char payload[4]; static unsigned char parse_ip(char *s, unsigned char *out) { unsigned char i, v; @@ -24,77 +21,33 @@ static unsigned char parse_ip(char *s, unsigned char *out) { } return 1; } - static void put_ip(unsigned char *ip) { unsigned char i; for (i = 0; i < 4; i++) { putu(ip[i]); if (i < 3) putc('.'); } } -static void send_echo(unsigned char seq) { - unsigned int c; - pkt[0] = 0x45; pkt[1] = 0; pkt[2] = 0; pkt[3] = 28; /* 20 IP + 8 ICMP */ - pkt[4] = 0; pkt[5] = 0; pkt[6] = 0; pkt[7] = 0; - pkt[8] = 64; pkt[9] = 1; pkt[10] = 0; pkt[11] = 0; /* TTL, proto ICMP */ - pkt[12] = 10; pkt[13] = 0; pkt[14] = 0; pkt[15] = 2; /* src 10.0.0.2 */ - pkt[16] = dst[0]; pkt[17] = dst[1]; pkt[18] = dst[2]; pkt[19] = dst[3]; - c = ~sum16(pkt, 20, 0); pkt[10] = (unsigned char)(c >> 8); pkt[11] = (unsigned char)c; - pkt[20] = 8; pkt[21] = 0; pkt[22] = 0; pkt[23] = 0; /* echo request */ - pkt[24] = 0x12; pkt[25] = 0x34; pkt[26] = 0; pkt[27] = seq; - c = ~sum16(pkt + 20, 8, 0); pkt[22] = (unsigned char)(c >> 8); pkt[23] = (unsigned char)c; - slip_send((char *)pkt, 28); -} - -/* wait for the ICMP echo reply for 'seq', skipping unrelated frames (the shared - 10.0.0.0/24 carries IGMP/mDNS/SSDP noise). Returns 1 on match, 0 on timeout. - The emulator runs uncapped, so the spin budget is generous to span the - wall-clock round trip through the host bridge. */ -static unsigned char ping_wait(unsigned char seq) { - unsigned int o, i; - unsigned char len = 0, c, ihl; - int b; - for (o = 0; o < 20; o++) { - for (i = 0; i < 60000; i++) { - b = srecv_nb(); - if (b < 0) continue; - c = (unsigned char)b; - if (c == SLIP_END) { - if (len) { - ihl = (unsigned char)((pkt[0] & 0x0f) << 2); - if (len >= (unsigned char)(ihl + 8) && pkt[9] == 1 - && pkt[ihl] == 0 && pkt[ihl + 7] == seq) return 1; - len = 0; /* not ours - keep waiting */ - } - continue; - } - if (c == SLIP_ESC) { - c = srecv(); - if (c == SLIP_ESC_END) c = SLIP_END; - else if (c == SLIP_ESC_ESC) c = SLIP_ESC; - } - if (len < 128) pkt[len++] = c; else len = 0; - } - } - return 0; -} - void main(void) { char *arg; - unsigned char seq, got = 0; + unsigned char s, seq, got = 0; arg = getargs(); if (!arg || !*arg) { dst[0] = 10; dst[1] = 0; dst[2] = 0; dst[3] = 1; } else if (!parse_ip(arg, dst)) { puts("ping: bad address"); nl(); sexit(1); } + payload[0] = 'g'; payload[1] = 'b'; payload[2] = 'o'; payload[3] = 's'; + s = net_socket(SOCK_ICMP); + if (s == 0xFF) { puts("ping: no socket"); nl(); sexit(1); } + net_connect(s, dst, 0); + puts("PING "); put_ip(dst); nl(); for (seq = 1; seq <= 4; seq++) { - send_echo(seq); - if (ping_wait(seq)) { + net_send(s, payload, 4); + if (net_recv(s, rbuf, sizeof(rbuf)) != 0xFF) { got++; - puts(" reply from "); put_ip(pkt + 12); - puts(" seq="); putu(seq); - puts(" ttl="); putu(pkt[8]); nl(); + puts(" reply from "); put_ip(dst); puts(" seq="); putu(seq); nl(); } else { puts(" seq="); putu(seq); puts(" timeout"); nl(); } } puts("-- "); putu(got); puts("/4 received"); nl(); + net_close(s); } |
