Merge from Etherboot 5.4
[people/xl0/gpxe.git] / src / proto / tcp.c
1 #include "etherboot.h"
2 #include "ip.h"
3 #include "tcp.h"
4 #include "nic.h"
5
6 void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
7                   unsigned int destsock, long send_seq, long recv_seq,
8                   int window, int flags, int ttl, int len, const void *buf)
9 {
10        struct iphdr *ip;
11        struct tcphdr *tcp;
12        ip = (struct iphdr *)buf;
13        build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
14        tcp = (struct tcphdr *)(ip + 1);
15        tcp->src = htons(srcsock);
16        tcp->dst = htons(destsock);
17        tcp->seq = htonl(send_seq);
18        tcp->ack = htonl(recv_seq);
19        tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
20        tcp->window = htons(window);
21        tcp->chksum = 0;
22        if ((tcp->chksum = tcpudpchksum(ip)) == 0)
23                tcp->chksum = 0xffff;
24 }
25
26 /**************************************************************************
27 TCP_TRANSMIT - Send a TCP packet
28 **************************************************************************/
29 int tcp_transmit(unsigned long destip, unsigned int srcsock,
30                 unsigned int destsock, long send_seq, long recv_seq,
31                 int window, int flags, int len, const void *buf)
32 {
33        build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
34                      window, flags, 60, len, buf);
35        return ip_transmit(len, buf);
36 }
37
38 int tcp_reset(struct iphdr *ip) {
39        struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
40        char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
41
42        if (!(tcp->ctrl & htons(RST))) {
43               long seq = ntohl(tcp->seq) + ntohs(ip->len) -
44                          sizeof(struct iphdr) -
45                          ((ntohs(tcp->ctrl) >> 10) & 0x3C);
46               if (tcp->ctrl & htons(SYN|FIN))
47                       seq++;
48               return tcp_transmit(ntohl(ip->src.s_addr),
49                                   ntohs(tcp->dst), ntohs(tcp->src),
50                                   tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
51                                   seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
52        }
53        return (1);
54 }
55
56 /**************************************************************************
57 TCP - Simple-minded TCP stack. Can only send data once and then
58       receive the response. The algorithm for computing window
59       sizes and delaying ack's is currently broken, and thus
60       disabled. Performance would probably improve a little, if
61       this gets fixed. FIXME
62 **************************************************************************/
63 static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
64                     struct iphdr *ip, struct udphdr *udp __unused,
65                     struct tcphdr *tcp)
66 {
67        if (!tcp) {
68                return 0;
69        }
70        if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
71                return 0;
72        if (ntohs(tcp->dst) != ival) {
73                tcp_reset(ip);
74                return 0;
75        }
76        *(void **)ptr = tcp;
77        return 1;
78 }
79
80 int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
81                    int (*send)(int len, void *buf, void *ptr),
82                    int (*recv)(int len, const void *buf, void *ptr)) {
83        static uint16_t srcsock = 0;
84        int             rc = 1;
85        long            send_seq = currticks();
86        long            recv_seq = 0;
87        int             can_send = 0;
88        int             sent_all = 0;
89        struct iphdr   *ip;
90        struct tcphdr  *tcp;
91        int             ctrl = SYN;
92        char            buf[128]; /* Small outgoing buffer */
93        long            payload;
94        int             header_size;
95        int             window = 3*TCP_MIN_WINDOW;
96        long            last_ack = 0;
97        long            last_sent = 0;
98        long            rtt = 0;
99        long            srtt = 0;
100        long            rto = TCP_INITIAL_TIMEOUT;
101        int             retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
102        enum { CLOSED, SYN_RCVD, ESTABLISHED,
103               FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
104
105        if (!srcsock) {
106                srcsock = currticks();
107        }
108        if (++srcsock < 1024)
109                srcsock += 1024;
110
111        rx_qdrain();
112
113  send_data:
114        if (ctrl & ACK)
115                last_ack = recv_seq;
116        if (!tcp_transmit(destip, srcsock, destsock, send_seq,
117                          recv_seq, window, ctrl,
118                          sizeof(struct iphdr) + sizeof(struct tcphdr)+
119                          can_send, buf)) {
120                return (0);
121        }
122        last_sent = currticks();
123
124  recv_data:
125        if (!await_reply(await_tcp, srcsock, &tcp,
126                         (state == ESTABLISHED && !can_send)
127                         ? TCP_MAX_TIMEOUT : rto)) {
128                if (state == ESTABLISHED) {
129  close:
130                        ctrl = FIN|ACK;
131                        state = FIN_WAIT_1;
132                        rc = 0;
133                        goto send_data;
134                }
135
136                if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
137                        return (rc);
138
139                if (--retry <= 0) {
140                        /* time out */
141                        if (state == SYN_RCVD) {
142                                tcp_transmit(destip, srcsock, destsock,
143                                             send_seq, 0, window, RST,
144                                             sizeof(struct iphdr) +
145                                             sizeof(struct tcphdr), buf);
146                        }
147                        return (0);
148                }
149                /* retransmit */
150                goto send_data;
151        }
152        /* got_data: */
153        retry = TCP_MAX_RETRY;
154
155        if (tcp->ctrl & htons(ACK) ) {
156                char *data;
157                int syn_ack, consumed;
158
159                if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
160                        state = FIN_WAIT_2;
161                        ctrl = ACK;
162                        goto consume_data;
163                }
164                syn_ack = state == CLOSED || state == SYN_RCVD;
165                consumed = ntohl(tcp->ack) - send_seq - syn_ack;
166                if (consumed < 0 || consumed > can_send) {
167                        tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
168                        goto recv_data;
169                }
170
171                rtt = currticks() - last_sent;
172                srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
173                rto = srtt + srtt/2;
174                if (rto < TCP_MIN_TIMEOUT)
175                        rto = TCP_MIN_TIMEOUT;
176                else if (rto > TCP_MAX_TIMEOUT)
177                        rto = TCP_MAX_TIMEOUT;
178
179                can_send -= consumed;
180                send_seq += consumed + syn_ack;
181                data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
182                if (can_send) {
183                        memmove(data, data + consumed, can_send);
184                }
185                if (!sent_all) {
186                        int more_data;
187                        data += can_send;
188                        more_data = buf + sizeof(buf) - data;
189                        if (more_data > 0) {
190                                more_data = send(more_data, data, ptr);
191                                can_send += more_data;
192                        }
193                        sent_all = !more_data;
194                }
195                if (state == SYN_RCVD) {
196                        state = ESTABLISHED;
197                        ctrl = PSH|ACK;
198                        goto consume_data;
199                }
200                if (tcp->ctrl & htons(RST))
201                        return (0);
202        } else if (tcp->ctrl & htons(RST)) {
203                if (state == CLOSED)
204                        goto recv_data;
205                return (0);
206        }
207
208  consume_data:
209        ip  = (struct iphdr *)&nic.packet[ETH_HLEN];
210        header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
211        payload = ntohs(ip->len) - header_size;
212        if (payload > 0 && state == ESTABLISHED) {
213                int old_bytes = recv_seq - (long)ntohl(tcp->seq);
214                if (old_bytes >= 0 && payload - old_bytes > 0) {
215                        recv_seq += payload - old_bytes;
216                        if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
217                            !recv(payload - old_bytes,
218                                  &nic.packet[ETH_HLEN+header_size+old_bytes],
219                                  ptr)) {
220                                goto close;
221                        }
222                        if ((state == ESTABLISHED || state == SYN_RCVD) &&
223                            !(tcp->ctrl & htons(FIN))) {
224                                int in_window = window - 2*TCP_MIN_WINDOW >
225                                                 recv_seq - last_ack;
226                                ctrl = can_send ? PSH|ACK : ACK;
227                                if (!can_send && in_window) {
228 /* Window scaling is broken right now, just fall back to acknowledging every */
229 /* packet immediately and unconditionally. FIXME                       */ /***/
230 /*                                     if (await_reply(await_tcp, srcsock,
231                                                        &tcp, rto))
232                                                goto got_data;
233                                        else */
234                                                goto send_data;
235                                }
236                                if (!in_window) {
237                                        window += TCP_MIN_WINDOW;
238                                        if (window > TCP_MAX_WINDOW)
239                                                window = TCP_MAX_WINDOW;
240                                }
241                                goto send_data;
242                        }
243                } else {
244                        /* saw old data again, must have lost packets */
245                        window /= 2;
246                        if (window < 2*TCP_MIN_WINDOW)
247                                window = 2*TCP_MIN_WINDOW;
248                }
249        }
250
251        if (tcp->ctrl & htons(FIN)) {
252                if (state == ESTABLISHED) {
253                        ctrl = FIN|ACK;
254                } else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
255                        ctrl = ACK;
256                } else {
257                        ctrl = RST;
258                }
259                return (tcp_transmit(destip, srcsock, destsock,
260                                     send_seq, recv_seq + 1, window, ctrl,
261                                     sizeof(struct iphdr) +
262                                     sizeof(struct tcphdr), buf) &&
263                        (state == ESTABLISHED ||
264                         state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
265                        !can_send);
266        }
267
268        if (state == CLOSED) {
269                if (tcp->ctrl & htons(SYN)) {
270                        recv_seq = ntohl(tcp->seq) + 1;
271                        if (!(tcp->ctrl & htons(ACK))) {
272                                state = SYN_RCVD;
273                                ctrl = SYN|ACK|PSH;
274                                goto send_data;
275                        } else {
276                                state = ESTABLISHED;
277                                ctrl = PSH|ACK;
278                        }
279                }
280        }
281
282        if (can_send || payload) {
283                goto send_data;
284        }
285        goto recv_data;
286 }