diff options
| -rw-r--r-- | c/ping.c | 15 | ||||
| -rw-r--r-- | c/sock.h | 6 | ||||
| -rw-r--r-- | include/gbos.inc | 3 | ||||
| -rw-r--r-- | src/socket.asm | 57 |
4 files changed, 77 insertions, 4 deletions
@@ -14,6 +14,19 @@ static void put_ip(unsigned char *ip) { for (i = 0; i < 4; i++) { putu(ip[i]); if (i < 3) putc('.'); } } +/* wait up to ~2s for an echo reply without blocking in the kernel: a lost + reply (some hosts rate-limit ICMP) must cost one "timeout" line, not + minutes wedged inside net_recv's pump-counted wait. */ +static unsigned char wait_reply(unsigned char s) { + unsigned char t, r; + for (t = 0; t < 40; t++) { + r = net_recv_nb(s, rbuf, (unsigned char)sizeof(rbuf)); + if (r != 0xFE) return r; /* reply (or error) */ + msleep(50); + } + return 0xFF; /* ~2s with no reply */ +} + void main(void) { char *arg; unsigned char s, seq, got = 0, i; @@ -35,7 +48,7 @@ void main(void) { puts("PING "); put_ip(dst); nl(); for (seq = 1; seq <= 4; seq++) { net_send(s, payload, 4); - if (net_recv(s, rbuf, (unsigned char)sizeof(rbuf)) != 0xFF) { + if (wait_reply(s) != 0xFF) { got++; puts(" reply from "); put_ip(dst); puts(" seq="); putu(seq); nl(); } else { @@ -42,6 +42,12 @@ 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; 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; + return sys_net(&_nr); +} /* 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; diff --git a/include/gbos.inc b/include/gbos.inc index 937d083..e9f8d7d 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -153,6 +153,9 @@ DEF NET_CLOSE EQU 4 ; NR_SOCK DEF NET_BIND EQU 5 ; NR_SOCK, NR_PORT -> bind local port (UDP) DEF NET_POLL EQU 6 ; pump RX once (answer pings) -> A=0 DEF NET_SETIP EQU 7 ; set our IPv4 address from NR_IP (DHCP) +DEF NET_RECVNB EQU 8 ; like NET_RECV but never blocks: pump once, then + ; A=len if a datagram was buffered, $FE if none yet, + ; 0 on TCP EOF. Lets programs own their timeouts (ping). ; socket types DEF SOCK_ICMP EQU 1 DEF SOCK_UDP EQU 2 diff --git a/src/socket.asm b/src/socket.asm index fa6b245..20ed982 100644 --- a/src/socket.asm +++ b/src/socket.asm @@ -48,6 +48,11 @@ wNetReq:: DS NR_SIZE wNetSockPtr:: DS 2 wNetIhl:: DS 1 wNetSeq:: DS 1 +wNetEchoId:: DS 2 ; ICMP echo identifier: unique per boot + per host. + ; A fixed id (the old $1234) made every GB's ping + ; flow identical across boots/sessions, colliding in + ; upstream NAT conntrack (keyed on icmp id) - replies + ; were silently dropped until the stale entry expired. wNetTx:: DS 320 ; tx build buffer wNetRxBuf:: DS 320 ; rx frame assembly / parse wNetRxLen:: DS 2 ; 16-bit: frames can exceed 255 bytes (DHCP/BOOTP) @@ -90,6 +95,10 @@ net_init:: ld [wConHead], a ld [wConTail], a ld [wNetSeq], a + ldh a, [rDIV] ; per-boot echo id seed (refined at DHCP lease) + ld [wNetEchoId], a + cpl + ld [wNetEchoId+1], a ret ; con_push(A) - queue a console-input byte (dropped if the ring is full) @@ -147,6 +156,9 @@ net_op_setip: ld [wNetOurIP+2], a ld a, [wNetReq+NR_IP+3] ld [wNetOurIP+3], a + ld [wNetEchoId], a ; echo id hi = our host octet (distinct per GB) + ldh a, [rDIV] ; lo = timer noise at lease time (distinct per + ld [wNetEchoId+1], a ; boot - DHCP timing varies with the network) xor a ret @@ -300,6 +312,8 @@ sys_net:: jp z, net_op_poll cp NET_SETIP jp z, net_op_setip + cp NET_RECVNB + jp z, net_op_recvnb ld a, $FF ret @@ -418,6 +432,43 @@ net_op_send: ld a, $FF ret +; ---- recvnb(sock, buf, maxlen) -> A = len / $FE (nothing yet) / 0 (TCP EOF) -- +; One pump, no blocking: programs that need their own (wall-clock) timeout - +; ping, most of all - loop recvnb+msleep instead of sitting in net_op_recv's +; pump-counted wait, which at native speed can block for minutes if a reply +; is lost (e.g. a host that rate-limits ICMP). +net_op_recvnb: + ld a, [wNetReq + NR_SOCK] + call net_sock_ptr + ld a, l + ld [wNetSockPtr], a + ld a, h + ld [wNetSockPtr+1], a + call net_pump + ld a, [wNetReq+NR_SOCK] ; tcp_in may have moved wNetSockPtr; restore + call net_sock_ptr + ld a, l + ld [wNetSockPtr], a + ld a, h + ld [wNetSockPtr+1], a + call sock_has_rx + jp nz, net_op_recv.got ; deliver exactly like a blocking recv + call sock_ptr_hl ; TCP peer closed + nothing buffered -> EOF + ld a, l + add SK_STATE + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [hl] + cp TCP_DONE + jr nz, .none + xor a ; EOF -> 0, same as net_op_recv + ret +.none + ld a, $FE ; nothing buffered yet + ret + ; ---- recv(sock, buf, maxlen) -> A = len or $FF (timeout) --------------------- net_op_recv: ld a, [wNetReq + NR_SOCK] @@ -595,9 +646,9 @@ icmp_send: ld [wNetTx+21], a ; code ld [wNetTx+22], a ld [wNetTx+23], a ; cksum - ld a, $12 - ld [wNetTx+24], a ; id - ld a, $34 + ld a, [wNetEchoId] + ld [wNetTx+24], a ; id (unique per boot + host, set by net_setip) + ld a, [wNetEchoId+1] ld [wNetTx+25], a xor a ld [wNetTx+26], a ; seq hi |
