2
0
mirror of https://github.com/xcat2/xNBA.git synced 2025-01-05 11:05:07 +00:00

Split TCP code out into proto/tcp.c

This commit is contained in:
Michael Brown 2005-05-02 12:07:47 +00:00
parent cc980d1c94
commit a918abc036
4 changed files with 295 additions and 301 deletions

View File

@ -219,7 +219,6 @@ static int rarp(void);
#else
static int bootp(void);
#endif
static unsigned short tcpudpchksum(struct iphdr *ip);
/*
@ -320,9 +319,6 @@ static int nic_load ( struct type_dev *type_dev,
char *kernel;
/* Now use TFTP to load file */
#ifdef DOWNLOAD_PROTO_NFS
rpc_init();
#endif
kernel = KERNEL_BUF[0] == '\0' ?
#ifdef DEFAULT_BOOTFILE
DEFAULT_BOOTFILE
@ -529,29 +525,6 @@ void build_udp_hdr(unsigned long destip,
udp->chksum = 0xffff;
}
#ifdef DOWNLOAD_PROTO_HTTP
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
int window, int flags, int ttl, int len, const void *buf)
{
struct iphdr *ip;
struct tcphdr *tcp;
ip = (struct iphdr *)buf;
build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
tcp = (struct tcphdr *)(ip + 1);
tcp->src = htons(srcsock);
tcp->dst = htons(destsock);
tcp->seq = htonl(send_seq);
tcp->ack = htonl(recv_seq);
tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
tcp->window = htons(window);
tcp->chksum = 0;
if ((tcp->chksum = tcpudpchksum(ip)) == 0)
tcp->chksum = 0xffff;
}
#endif
/**************************************************************************
UDP_TRANSMIT - Send an UDP datagram
**************************************************************************/
@ -562,37 +535,6 @@ int udp_transmit(unsigned long destip, unsigned int srcsock,
return ip_transmit(len, buf);
}
/**************************************************************************
TCP_TRANSMIT - Send a TCP packet
**************************************************************************/
#ifdef DOWNLOAD_PROTO_HTTP
int tcp_transmit(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
int window, int flags, int len, const void *buf)
{
build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
window, flags, 60, len, buf);
return ip_transmit(len, buf);
}
int tcp_reset(struct iphdr *ip) {
struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
if (!(tcp->ctrl & htons(RST))) {
long seq = ntohl(tcp->seq) + ntohs(ip->len) -
sizeof(struct iphdr) -
((ntohs(tcp->ctrl) >> 10) & 0x3C);
if (tcp->ctrl & htons(SYN|FIN))
seq++;
return tcp_transmit(ntohl(ip->src.s_addr),
ntohs(tcp->dst), ntohs(tcp->src),
tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
}
return (1);
}
#endif
/**************************************************************************
QDRAIN - clear the nic's receive queue
@ -616,9 +558,6 @@ void rx_qdrain(void)
await_reply(await_qdrain, 0, NULL, 0);
}
#ifdef DOWNLOAD_PROTO_TFTP
#endif /* DOWNLOAD_PROTO_TFTP */
#ifdef RARP_NOT_BOOTP
/**************************************************************************
RARP - Get my IP address and load information
@ -876,7 +815,7 @@ static int bootp(void)
}
#endif /* RARP_NOT_BOOTP */
static uint16_t tcpudpchksum(struct iphdr *ip)
uint16_t tcpudpchksum(struct iphdr *ip)
{
struct udp_pseudo_hdr pseudo;
uint16_t checksum;
@ -1034,239 +973,6 @@ void join_group(int slot, unsigned long group)
#include "proto_eth_slow.c"
/**************************************************************************
TCP - Simple-minded TCP stack. Can only send data once and then
receive the response. The algorithm for computing window
sizes and delaying ack's is currently broken, and thus
disabled. Performance would probably improve a little, if
this gets fixed. FIXME
**************************************************************************/
#ifdef DOWNLOAD_PROTO_HTTP
static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
struct iphdr *ip, struct udphdr *udp __unused,
struct tcphdr *tcp)
{
if (!tcp) {
return 0;
}
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
return 0;
if (ntohs(tcp->dst) != ival) {
tcp_reset(ip);
return 0;
}
*(void **)ptr = tcp;
return 1;
}
int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
int (*send)(int len, void *buf, void *ptr),
int (*recv)(int len, const void *buf, void *ptr)) {
static uint16_t srcsock = 0;
int rc = 1;
long send_seq = currticks();
long recv_seq = 0;
int can_send = 0;
int sent_all = 0;
struct iphdr *ip;
struct tcphdr *tcp;
int ctrl = SYN;
char buf[128]; /* Small outgoing buffer */
long payload;
int header_size;
int window = 3*TCP_MIN_WINDOW;
long last_ack = 0;
long last_sent = 0;
long rtt = 0;
long srtt = 0;
long rto = TCP_INITIAL_TIMEOUT;
int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
enum { CLOSED, SYN_RCVD, ESTABLISHED,
FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
if (!srcsock) {
srcsock = currticks();
}
if (++srcsock < 1024)
srcsock += 1024;
await_reply(await_qdrain, 0, NULL, 0);
send_data:
if (ctrl & ACK)
last_ack = recv_seq;
if (!tcp_transmit(destip, srcsock, destsock, send_seq,
recv_seq, window, ctrl,
sizeof(struct iphdr) + sizeof(struct tcphdr)+
can_send, buf)) {
return (0);
}
last_sent = currticks();
recv_data:
if (!await_reply(await_tcp, srcsock, &tcp,
(state == ESTABLISHED && !can_send)
? TCP_MAX_TIMEOUT : rto)) {
if (state == ESTABLISHED) {
close:
ctrl = FIN|ACK;
state = FIN_WAIT_1;
rc = 0;
goto send_data;
}
if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
return (rc);
if (--retry <= 0) {
/* time out */
if (state == SYN_RCVD) {
tcp_transmit(destip, srcsock, destsock,
send_seq, 0, window, RST,
sizeof(struct iphdr) +
sizeof(struct tcphdr), buf);
}
return (0);
}
/* retransmit */
goto send_data;
}
got_data:
retry = TCP_MAX_RETRY;
if (tcp->ctrl & htons(ACK) ) {
char *data;
int syn_ack, consumed;
if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
state = FIN_WAIT_2;
ctrl = ACK;
goto consume_data;
}
syn_ack = state == CLOSED || state == SYN_RCVD;
consumed = ntohl(tcp->ack) - send_seq - syn_ack;
if (consumed < 0 || consumed > can_send) {
tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
goto recv_data;
}
rtt = currticks() - last_sent;
srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
rto = srtt + srtt/2;
if (rto < TCP_MIN_TIMEOUT)
rto = TCP_MIN_TIMEOUT;
else if (rto > TCP_MAX_TIMEOUT)
rto = TCP_MAX_TIMEOUT;
can_send -= consumed;
send_seq += consumed + syn_ack;
data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
if (can_send) {
memmove(data, data + consumed, can_send);
}
if (!sent_all) {
int more_data;
data += can_send;
more_data = buf + sizeof(buf) - data;
if (more_data > 0) {
more_data = send(more_data, data, ptr);
can_send += more_data;
}
sent_all = !more_data;
}
if (state == SYN_RCVD) {
state = ESTABLISHED;
ctrl = PSH|ACK;
goto consume_data;
}
if (tcp->ctrl & htons(RST))
return (0);
} else if (tcp->ctrl & htons(RST)) {
if (state == CLOSED)
goto recv_data;
return (0);
}
consume_data:
ip = (struct iphdr *)&nic.packet[ETH_HLEN];
header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
payload = ntohs(ip->len) - header_size;
if (payload > 0 && state == ESTABLISHED) {
int old_bytes = recv_seq - (long)ntohl(tcp->seq);
if (old_bytes >= 0 && payload - old_bytes > 0) {
recv_seq += payload - old_bytes;
if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
!recv(payload - old_bytes,
&nic.packet[ETH_HLEN+header_size+old_bytes],
ptr)) {
goto close;
}
if ((state == ESTABLISHED || state == SYN_RCVD) &&
!(tcp->ctrl & htons(FIN))) {
int in_window = window - 2*TCP_MIN_WINDOW >
recv_seq - last_ack;
ctrl = can_send ? PSH|ACK : ACK;
if (!can_send && in_window) {
/* Window scaling is broken right now, just fall back to acknowledging every */
/* packet immediately and unconditionally. FIXME */ /***/
/* if (await_reply(await_tcp, srcsock,
&tcp, rto))
goto got_data;
else */
goto send_data;
}
if (!in_window) {
window += TCP_MIN_WINDOW;
if (window > TCP_MAX_WINDOW)
window = TCP_MAX_WINDOW;
}
goto send_data;
}
} else {
/* saw old data again, must have lost packets */
window /= 2;
if (window < 2*TCP_MIN_WINDOW)
window = 2*TCP_MIN_WINDOW;
}
}
if (tcp->ctrl & htons(FIN)) {
if (state == ESTABLISHED) {
ctrl = FIN|ACK;
} else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
ctrl = ACK;
} else {
ctrl = RST;
}
return (tcp_transmit(destip, srcsock, destsock,
send_seq, recv_seq + 1, window, ctrl,
sizeof(struct iphdr) +
sizeof(struct tcphdr), buf) &&
(state == ESTABLISHED ||
state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
!can_send);
}
if (state == CLOSED) {
if (tcp->ctrl & htons(SYN)) {
recv_seq = ntohl(tcp->seq) + 1;
if (!(tcp->ctrl & htons(ACK))) {
state = SYN_RCVD;
ctrl = SYN|ACK|PSH;
goto send_data;
} else {
state = ESTABLISHED;
ctrl = PSH|ACK;
}
}
}
if (can_send || payload) {
goto send_data;
}
goto recv_data;
}
#endif
/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
@ -1361,7 +1067,6 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
}
}
tcp = 0;
#ifdef DOWNLOAD_PROTO_HTTP
if (ip && (ip->protocol == IP_TCP) &&
(nic.packetlen >=
ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){
@ -1377,7 +1082,6 @@ int await_reply(reply_t reply, int ival, void *ptr, long timeout)
}
}
#endif
result = reply(ival, ptr, ptype, ip, udp, tcp);
if (result > 0) {
return result;

View File

@ -18,4 +18,6 @@ struct iphdr {
struct in_addr dest;
} PACKED;
extern uint16_t tcpudpchksum(struct iphdr *ip);
#endif /* _IP_H */

View File

@ -9,10 +9,6 @@
#define TCP_MIN_WINDOW (1500-TCP_MAX_HEADER)
#define TCP_MAX_WINDOW (65535-TCP_MAX_HEADER)
#define MAX_URL 80
#define FIN 1
#define SYN 2
#define RST 4
@ -32,4 +28,10 @@ struct tcphdr {
uint16_t urgent;
};
extern int tcp_transaction ( unsigned long destip, unsigned int destsock,
void *ptr,
int (*send)(int len, void *buf, void *ptr),
int (*recv)(int len, const void *buf, void *ptr));
#endif /* _TCP_H */

286
src/proto/tcp.c Normal file
View File

@ -0,0 +1,286 @@
#include "etherboot.h"
#include "ip.h"
#include "tcp.h"
void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
int window, int flags, int ttl, int len, const void *buf)
{
struct iphdr *ip;
struct tcphdr *tcp;
ip = (struct iphdr *)buf;
build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
tcp = (struct tcphdr *)(ip + 1);
tcp->src = htons(srcsock);
tcp->dst = htons(destsock);
tcp->seq = htonl(send_seq);
tcp->ack = htonl(recv_seq);
tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
tcp->window = htons(window);
tcp->chksum = 0;
if ((tcp->chksum = tcpudpchksum(ip)) == 0)
tcp->chksum = 0xffff;
}
/**************************************************************************
TCP_TRANSMIT - Send a TCP packet
**************************************************************************/
int tcp_transmit(unsigned long destip, unsigned int srcsock,
unsigned int destsock, long send_seq, long recv_seq,
int window, int flags, int len, const void *buf)
{
build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
window, flags, 60, len, buf);
return ip_transmit(len, buf);
}
int tcp_reset(struct iphdr *ip) {
struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
if (!(tcp->ctrl & htons(RST))) {
long seq = ntohl(tcp->seq) + ntohs(ip->len) -
sizeof(struct iphdr) -
((ntohs(tcp->ctrl) >> 10) & 0x3C);
if (tcp->ctrl & htons(SYN|FIN))
seq++;
return tcp_transmit(ntohl(ip->src.s_addr),
ntohs(tcp->dst), ntohs(tcp->src),
tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
}
return (1);
}
/**************************************************************************
TCP - Simple-minded TCP stack. Can only send data once and then
receive the response. The algorithm for computing window
sizes and delaying ack's is currently broken, and thus
disabled. Performance would probably improve a little, if
this gets fixed. FIXME
**************************************************************************/
static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
struct iphdr *ip, struct udphdr *udp __unused,
struct tcphdr *tcp)
{
if (!tcp) {
return 0;
}
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
return 0;
if (ntohs(tcp->dst) != ival) {
tcp_reset(ip);
return 0;
}
*(void **)ptr = tcp;
return 1;
}
int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
int (*send)(int len, void *buf, void *ptr),
int (*recv)(int len, const void *buf, void *ptr)) {
static uint16_t srcsock = 0;
int rc = 1;
long send_seq = currticks();
long recv_seq = 0;
int can_send = 0;
int sent_all = 0;
struct iphdr *ip;
struct tcphdr *tcp;
int ctrl = SYN;
char buf[128]; /* Small outgoing buffer */
long payload;
int header_size;
int window = 3*TCP_MIN_WINDOW;
long last_ack = 0;
long last_sent = 0;
long rtt = 0;
long srtt = 0;
long rto = TCP_INITIAL_TIMEOUT;
int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
enum { CLOSED, SYN_RCVD, ESTABLISHED,
FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
if (!srcsock) {
srcsock = currticks();
}
if (++srcsock < 1024)
srcsock += 1024;
rx_qdrain();
send_data:
if (ctrl & ACK)
last_ack = recv_seq;
if (!tcp_transmit(destip, srcsock, destsock, send_seq,
recv_seq, window, ctrl,
sizeof(struct iphdr) + sizeof(struct tcphdr)+
can_send, buf)) {
return (0);
}
last_sent = currticks();
recv_data:
if (!await_reply(await_tcp, srcsock, &tcp,
(state == ESTABLISHED && !can_send)
? TCP_MAX_TIMEOUT : rto)) {
if (state == ESTABLISHED) {
close:
ctrl = FIN|ACK;
state = FIN_WAIT_1;
rc = 0;
goto send_data;
}
if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
return (rc);
if (--retry <= 0) {
/* time out */
if (state == SYN_RCVD) {
tcp_transmit(destip, srcsock, destsock,
send_seq, 0, window, RST,
sizeof(struct iphdr) +
sizeof(struct tcphdr), buf);
}
return (0);
}
/* retransmit */
goto send_data;
}
/* got_data: */
retry = TCP_MAX_RETRY;
if (tcp->ctrl & htons(ACK) ) {
char *data;
int syn_ack, consumed;
if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
state = FIN_WAIT_2;
ctrl = ACK;
goto consume_data;
}
syn_ack = state == CLOSED || state == SYN_RCVD;
consumed = ntohl(tcp->ack) - send_seq - syn_ack;
if (consumed < 0 || consumed > can_send) {
tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
goto recv_data;
}
rtt = currticks() - last_sent;
srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
rto = srtt + srtt/2;
if (rto < TCP_MIN_TIMEOUT)
rto = TCP_MIN_TIMEOUT;
else if (rto > TCP_MAX_TIMEOUT)
rto = TCP_MAX_TIMEOUT;
can_send -= consumed;
send_seq += consumed + syn_ack;
data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
if (can_send) {
memmove(data, data + consumed, can_send);
}
if (!sent_all) {
int more_data;
data += can_send;
more_data = buf + sizeof(buf) - data;
if (more_data > 0) {
more_data = send(more_data, data, ptr);
can_send += more_data;
}
sent_all = !more_data;
}
if (state == SYN_RCVD) {
state = ESTABLISHED;
ctrl = PSH|ACK;
goto consume_data;
}
if (tcp->ctrl & htons(RST))
return (0);
} else if (tcp->ctrl & htons(RST)) {
if (state == CLOSED)
goto recv_data;
return (0);
}
consume_data:
ip = (struct iphdr *)&nic.packet[ETH_HLEN];
header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
payload = ntohs(ip->len) - header_size;
if (payload > 0 && state == ESTABLISHED) {
int old_bytes = recv_seq - (long)ntohl(tcp->seq);
if (old_bytes >= 0 && payload - old_bytes > 0) {
recv_seq += payload - old_bytes;
if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
!recv(payload - old_bytes,
&nic.packet[ETH_HLEN+header_size+old_bytes],
ptr)) {
goto close;
}
if ((state == ESTABLISHED || state == SYN_RCVD) &&
!(tcp->ctrl & htons(FIN))) {
int in_window = window - 2*TCP_MIN_WINDOW >
recv_seq - last_ack;
ctrl = can_send ? PSH|ACK : ACK;
if (!can_send && in_window) {
/* Window scaling is broken right now, just fall back to acknowledging every */
/* packet immediately and unconditionally. FIXME */ /***/
/* if (await_reply(await_tcp, srcsock,
&tcp, rto))
goto got_data;
else */
goto send_data;
}
if (!in_window) {
window += TCP_MIN_WINDOW;
if (window > TCP_MAX_WINDOW)
window = TCP_MAX_WINDOW;
}
goto send_data;
}
} else {
/* saw old data again, must have lost packets */
window /= 2;
if (window < 2*TCP_MIN_WINDOW)
window = 2*TCP_MIN_WINDOW;
}
}
if (tcp->ctrl & htons(FIN)) {
if (state == ESTABLISHED) {
ctrl = FIN|ACK;
} else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
ctrl = ACK;
} else {
ctrl = RST;
}
return (tcp_transmit(destip, srcsock, destsock,
send_seq, recv_seq + 1, window, ctrl,
sizeof(struct iphdr) +
sizeof(struct tcphdr), buf) &&
(state == ESTABLISHED ||
state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
!can_send);
}
if (state == CLOSED) {
if (tcp->ctrl & htons(SYN)) {
recv_seq = ntohl(tcp->seq) + 1;
if (!(tcp->ctrl & htons(ACK))) {
state = SYN_RCVD;
ctrl = SYN|ACK|PSH;
goto send_data;
} else {
state = ESTABLISHED;
ctrl = PSH|ACK;
}
}
}
if (can_send || payload) {
goto send_data;
}
goto recv_data;
}