From d102366e6808f74ef98044374d744e453efdbefa Mon Sep 17 00:00:00 2001 From: user Date: Fri, 17 Jul 2026 12:15:09 +0200 Subject: net: ping - GB-originated ICMP echo (a Game Boy pings the real internet) New `ping [A.B.C.D]` program (PROG_PING=27, bank 29): builds and sends ICMP echo requests from 10.0.0.2, then reads replies off the link port. It keeps reading SLIP frames until it finds *our* echo reply, skipping the IGMP/mDNS/ SSDP multicast noise that shares 10.0.0.0/24. Reply wait uses a generous srecv_nb spin budget since the emulator runs uncapped (no timer syscall yet). Verified over the tunbridge (with NAT): /# ping 10.0.0.1 -> 4/4 received, ttl=64 (the SLIP peer/host) /# ping 1.1.1.1 -> 4/4 received, ttl=56 (Cloudflare, real net!) ttl=56 is a real internet round trip (64 minus the hops). Combined with the host being able to ping the GB, the Game Boy is now a full two-way ICMP host. --- c/ping.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 c/ping.c (limited to 'c/ping.c') diff --git a/c/ping.c b/c/ping.c new file mode 100644 index 0000000..12dabd9 --- /dev/null +++ b/c/ping.c @@ -0,0 +1,100 @@ +#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] */ + +static unsigned char pkt[128]; +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 parse_ip(char *s, unsigned char *out) { + unsigned char i, v; + for (i = 0; i < 4; i++) { + if (*s < '0' || *s > '9') return 0; + v = 0; + while (*s >= '0' && *s <= '9') { v = (unsigned char)(v * 10 + (*s - '0')); s++; } + out[i] = v; + if (i < 3) { if (*s != '.') return 0; s++; } + } + 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; + 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); } + + puts("PING "); put_ip(dst); nl(); + for (seq = 1; seq <= 4; seq++) { + send_echo(seq); + if (ping_wait(seq)) { + got++; + puts(" reply from "); put_ip(pkt + 12); + puts(" seq="); putu(seq); + puts(" ttl="); putu(pkt[8]); nl(); + } else { + puts(" seq="); putu(seq); puts(" timeout"); nl(); + } + } + puts("-- "); putu(got); puts("/4 received"); nl(); +} -- cgit v1.3.1-sl0p