aboutsummaryrefslogtreecommitdiffstats
path: root/c/ping.c
blob: 12dabd9db102a65b33a2e20c9e64d5ec3748e707 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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();
}