aboutsummaryrefslogtreecommitdiffstats
path: root/c/tcp.c
blob: 07eb2b178bb5c2977de74002a4dd5bff78d042df (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
101
102
103
104
105
106
107
#include "netlib.h"
/* tcp - a minimal TCP client over SLIP (milestone C). Connects to 10.0.0.1:7,
   sends a line, prints the reply, closes. No retransmission: the GB<->gateway
   link is lossless, and the gateway's real socket handles the lossy side.
   Us = 10.0.0.2, peer = 10.0.0.1. */

static unsigned char pkt[256];
static unsigned long snd_nxt, rcv_nxt;
#define LPORT 40000u
#define RPORT 7u

/* SYN 0x02, ACK 0x10, FIN 0x01, PSH 0x08, RST 0x04 */

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 void put32(unsigned char *p, unsigned long v) {
    p[0] = (unsigned char)(v >> 24); p[1] = (unsigned char)(v >> 16);
    p[2] = (unsigned char)(v >> 8);  p[3] = (unsigned char)v;
}
static unsigned long get32(unsigned char *p) {
    return ((unsigned long)p[0] << 24) | ((unsigned long)p[1] << 16)
         | ((unsigned long)p[2] << 8) | p[3];
}

/* build+send IP+TCP with 'flags' and 'dlen' payload bytes already at pkt[40..] */
static void tcp_send(unsigned char flags, unsigned char dlen) {
    unsigned int c, psum, tcplen = 20 + dlen, total = 20 + tcplen;
    pkt[0] = 0x45; pkt[1] = 0; pkt[2] = (unsigned char)(total >> 8); pkt[3] = (unsigned char)total;
    pkt[4] = 0; pkt[5] = 1; pkt[6] = 0x40; pkt[7] = 0; pkt[8] = 64; pkt[9] = 6;
    pkt[10] = 0; pkt[11] = 0;
    pkt[12] = 10; pkt[13] = 0; pkt[14] = 0; pkt[15] = 2;
    pkt[16] = 10; pkt[17] = 0; pkt[18] = 0; pkt[19] = 1;
    pkt[20] = LPORT >> 8; pkt[21] = (unsigned char)LPORT;
    pkt[22] = RPORT >> 8; pkt[23] = (unsigned char)RPORT;
    put32(pkt + 24, snd_nxt);
    put32(pkt + 28, rcv_nxt);
    pkt[32] = 0x50; pkt[33] = flags; pkt[34] = 2; pkt[35] = 0;   /* window 512 */
    pkt[36] = 0; pkt[37] = 0; pkt[38] = 0; pkt[39] = 0;
    psum = sum16(pkt + 12, 8, 0);
    c = 6;      psum += c; if (psum < c) psum++;
    c = tcplen; psum += c; if (psum < c) psum++;
    c = ~sum16(pkt + 20, tcplen, psum);
    pkt[36] = (unsigned char)(c >> 8); pkt[37] = (unsigned char)c;
    c = ~sum16(pkt, 20, 0);
    pkt[10] = (unsigned char)(c >> 8); pkt[11] = (unsigned char)c;
    slip_send((char *)pkt, (unsigned char)total);
}

/* receive the next TCP segment for our connection; returns TCP header offset,
   sets *flags,*seq,*dlen,*doff. */
static unsigned char tcp_recv(unsigned char *flags, unsigned long *seq,
                              unsigned char *dlen, unsigned char *doff) {
    unsigned char len, ihl, hl;
    for (;;) {
        len = slip_recv((char *)pkt, 255);
        if (len < 40 || (pkt[0] >> 4) != 4 || pkt[9] != 6) continue;
        ihl = (pkt[0] & 0x0f) << 2;
        if (((unsigned int)pkt[22] << 8 | pkt[23]) != LPORT) continue;   /* not our port */
        hl = (pkt[ihl + 12] >> 4) << 2;
        *flags = pkt[ihl + 13];
        *seq = get32(pkt + ihl + 4);
        *doff = (unsigned char)(ihl + hl);
        *dlen = (unsigned char)(len - ihl - hl);
        return ihl;
    }
}

void main(void) {
    unsigned char flags, dlen, doff, i;
    unsigned long seq;
    puts("tcp: SYN -> 10.0.0.1:7"); nl();
    snd_nxt = 1000; rcv_nxt = 0;
    tcp_send(0x02, 0);                       /* SYN */
    snd_nxt = 1001;

    tcp_recv(&flags, &seq, &dlen, &doff);    /* expect SYN|ACK */
    if (!(flags & 0x02)) { puts("tcp: no SYN-ACK"); nl(); return; }
    rcv_nxt = seq + 1;
    tcp_send(0x10, 0);                        /* ACK -> ESTABLISHED */

    pkt[40] = 'h'; pkt[41] = 'i'; pkt[42] = ' '; pkt[43] = 'g'; pkt[44] = 'b';
    tcp_send(0x18, 5);                        /* PSH|ACK "hi gb" */
    snd_nxt += 5;

    for (;;) {
        tcp_recv(&flags, &seq, &dlen, &doff);
        if (dlen) {
            puts("tcp: recv '");
            for (i = 0; i < dlen; i++) putc(pkt[doff + i]);
            puts("'"); nl();
            rcv_nxt = seq + dlen;
            tcp_send(0x10, 0);               /* ACK the data */
            tcp_send(0x11, 0);               /* FIN|ACK - start closing */
            snd_nxt += 1;
        }
        if (flags & 0x01) {                  /* their FIN */
            rcv_nxt = seq + dlen + 1;
            tcp_send(0x10, 0);               /* ACK it */
            puts("tcp: closed"); nl();
            return;
        }
    }
}