Native TCP rewrite

Refs #300
This commit is contained in:
M66B 2016-02-07 23:01:06 +01:00
parent 1413788373
commit 03e26b2550
1 changed files with 160 additions and 151 deletions

View File

@ -627,7 +627,7 @@ void check_allowed(const struct arguments *args) {
struct tcp_session *t = tcp_session; struct tcp_session *t = tcp_session;
while (t != NULL) { while (t != NULL) {
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE) { if (t->state != TCP_CLOSING && t->state != TCP_CLOSE) {
if (t->version == 4) { if (t->version == 4) {
inet_ntop(AF_INET, &t->saddr.ip4, source, sizeof(source)); inet_ntop(AF_INET, &t->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &t->daddr.ip4, dest, sizeof(dest)); inet_ntop(AF_INET, &t->daddr.ip4, dest, sizeof(dest));
@ -772,7 +772,8 @@ int check_sessions(const struct arguments *args) {
timeout = TCP_IDLE_TIMEOUT; timeout = TCP_IDLE_TIMEOUT;
else else
timeout = TCP_CLOSE_TIMEOUT; timeout = TCP_CLOSE_TIMEOUT;
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE && t->time + timeout < now) {
if (t->state != TCP_CLOSING && t->state != TCP_CLOSE && t->time + timeout < now) {
// TODO send keep alives? // TODO send keep alives?
log_android(ANDROID_LOG_WARN, "TCP idle %d/%d sec from %s/%u to %s/%u state %s", log_android(ANDROID_LOG_WARN, "TCP idle %d/%d sec from %s/%u to %s/%u state %s",
now - t->time, timeout, now - t->time, timeout,
@ -781,8 +782,8 @@ int check_sessions(const struct arguments *args) {
write_rst(args, t); write_rst(args, t);
} }
// Check finished sessions // Check closing sessions
if (t->state == TCP_TIME_WAIT) { if (t->state == TCP_CLOSING) {
log_android(ANDROID_LOG_INFO, "TCP close from %s/%u to %s/%u socket %d", log_android(ANDROID_LOG_INFO, "TCP close from %s/%u to %s/%u socket %d",
source, ntohs(t->source), dest, ntohs(t->dest), t->socket); source, ntohs(t->source), dest, ntohs(t->dest), t->socket);
@ -857,29 +858,40 @@ int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set
while (t != NULL) { while (t != NULL) {
// Select sockets // Select sockets
if (t->state == TCP_LISTEN) { if (t->state == TCP_LISTEN) {
// Check for connected / errors // Check for errors
FD_SET(t->socket, efds); FD_SET(t->socket, efds);
// Check for connected = writable
FD_SET(t->socket, wfds); FD_SET(t->socket, wfds);
if (t->socket > max) if (t->socket > max)
max = t->socket; max = t->socket;
} }
else if (t->state == TCP_ESTABLISHED || else if (t->state == TCP_ESTABLISHED) {
t->state == TCP_SYN_RECV || // Check errors
t->state == TCP_CLOSE_WAIT) {
// Check for data / errors
FD_SET(t->socket, efds); FD_SET(t->socket, efds);
// Check for incoming data
if (t->send_window > 0) if (t->send_window > 0)
FD_SET(t->socket, rfds); FD_SET(t->socket, rfds);
// Check for outgoing data
if (t->forward != NULL)
FD_SET(t->socket, wfds);
if (t->socket > max) if (t->socket > max)
max = t->socket; max = t->socket;
} }
else if (t->state == TCP_CLOSE_WAIT) {
// Check errors
FD_SET(t->socket, efds);
if (t->state != TCP_CLOSE) { // Check for outgoing data
if (t->forward != NULL) { if (t->forward != NULL)
FD_SET(t->socket, wfds); FD_SET(t->socket, wfds);
if (t->socket > max)
max = t->socket; if (t->socket > max)
} max = t->socket;
} }
t = t->next; t = t->next;
@ -1260,6 +1272,12 @@ void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
inet_ntop(AF_INET6, &cur->saddr.ip6, source, sizeof(source)); inet_ntop(AF_INET6, &cur->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &cur->daddr.ip6, dest, sizeof(dest)); inet_ntop(AF_INET6, &cur->daddr.ip6, dest, sizeof(dest));
} }
char session[250];
sprintf(session, "Socket from %s/%u to %s/%u %s loc %u rem %u",
source, ntohs(cur->source), dest, ntohs(cur->dest),
strstate(cur->state),
cur->local_seq - cur->local_start,
cur->remote_seq - cur->remote_start);
// Check socket error // Check socket error
if (FD_ISSET(cur->socket, efds)) { if (FD_ISSET(cur->socket, efds)) {
@ -1269,10 +1287,11 @@ void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
socklen_t optlen = sizeof(int); socklen_t optlen = sizeof(int);
int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen); int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0) if (err < 0)
log_android(ANDROID_LOG_ERROR, "getsockopt error %d: %s", errno, log_android(ANDROID_LOG_ERROR, "%s getsockopt error %d: %s",
strerror(errno)); session, errno, strerror(errno));
else if (serr) else if (serr)
log_android(ANDROID_LOG_ERROR, "SO_ERROR %d: %s", serr, strerror(serr)); log_android(ANDROID_LOG_ERROR, "%s SO_ERROR %d: %s",
session, serr, strerror(serr));
write_rst(args, cur); write_rst(args, cur);
} }
@ -1283,8 +1302,7 @@ void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
if (FD_ISSET(cur->socket, wfds)) { if (FD_ISSET(cur->socket, wfds)) {
cur->time = time(NULL); cur->time = time(NULL);
log_android(ANDROID_LOG_INFO, "Connected from %s/%u to %s/%u", log_android(ANDROID_LOG_INFO, "%s connected", session);
source, ntohs(cur->source), dest, ntohs(cur->dest));
if (write_syn_ack(args, cur) >= 0) { if (write_syn_ack(args, cur) >= 0) {
cur->local_seq++; // local SYN cur->local_seq++; // local SYN
@ -1292,130 +1310,131 @@ void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
cur->state = TCP_SYN_RECV; cur->state = TCP_SYN_RECV;
} }
} }
} } else {
// Always forward data
if (FD_ISSET(cur->socket, wfds) && cur->forward != NULL) {
// Forward buffered data
int fwd = 0;
while (cur->forward != NULL && cur->forward->seq == cur->remote_seq) {
log_android(ANDROID_LOG_DEBUG, "%s fwd %u...%u+%u",
session,
cur->forward->seq - cur->remote_start,
cur->forward->seq + cur->forward->len -
cur->remote_start,
cur->forward->confirm);
else if (cur->state == TCP_SYN_RECV || unsigned int more = MSG_NOSIGNAL | (cur->forward->psh ? 0 : MSG_MORE);
cur->state == TCP_ESTABLISHED || if (cur->forward->len + cur->forward->confirm &&
cur->state == TCP_CLOSE_WAIT) { send(cur->socket, cur->forward->data, cur->forward->len, more)
// Check socket read < 0) {
if (FD_ISSET(cur->socket, rfds) && cur->send_window > 0) { log_android(ANDROID_LOG_ERROR, "%s send error %d: %s",
cur->time = time(NULL); session, errno, strerror(errno));
if (errno == EINTR || errno == EAGAIN) {
size_t len = (cur->send_window > TCP_SEND_WINDOW // Retry later
? TCP_SEND_WINDOW break;
: cur->send_window); } else {
uint8_t *buffer = malloc(len); write_rst(args, cur);
ssize_t bytes = recv(cur->socket, buffer, len, 0); break;
if (bytes < 0) {
// Socket error
log_android(ANDROID_LOG_ERROR, "recv error %d: %s",
errno, strerror(errno));
if (errno != EINTR && errno != EAGAIN)
write_rst(args, cur);
}
else if (bytes == 0) {
// Socket eof
// TCP: application close
log_android(ANDROID_LOG_INFO, "recv empty state %s",
strstate(cur->state));
if (cur->forward == NULL) {
if (write_fin_ack(args, cur) >= 0) {
cur->local_seq++; // local FIN
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1;
else if (cur->state == TCP_CLOSE_WAIT)
cur->state = TCP_LAST_ACK;
else
log_android(ANDROID_LOG_ERROR, "Unknown state %s",
strstate(cur->state));
log_android(ANDROID_LOG_INFO, "Half close state %s",
strstate(cur->state));
} }
}
else {
// There was still data to send
write_rst(args, cur);
}
} else {
// Socket read data
log_android(ANDROID_LOG_DEBUG,
"recv bytes %d state %s", bytes, strstate(cur->state));
// Forward to tun
if (write_data(args, cur, buffer, (size_t) bytes) >= 0)
cur->local_seq += bytes;
}
free(buffer);
}
}
// Forward data
if (cur->forward != NULL && FD_ISSET(cur->socket, wfds)) {
// Forward buffered data
int fwd = 0;
while (cur->forward != NULL && cur->forward->seq == cur->remote_seq) {
log_android(ANDROID_LOG_DEBUG, "%s/%u to %s/%u fwd %u...%u+%u",
source, ntohs(cur->source), dest, ntohs(cur->dest),
cur->forward->seq - cur->remote_start,
cur->forward->seq + cur->forward->len - cur->remote_start,
cur->forward->confirm);
unsigned int more = MSG_NOSIGNAL | (cur->forward->psh ? 0 : MSG_MORE);
if (cur->forward->len + cur->forward->confirm &&
send(cur->socket, cur->forward->data, cur->forward->len, more) < 0) {
log_android(ANDROID_LOG_ERROR,
"send error %d: %s", errno, strerror(errno));
if (errno == EINTR || errno == EAGAIN) {
// Retry later
break;
} else { } else {
write_rst(args, cur); fwd = 1;
break; cur->time = time(NULL);
} cur->remote_seq =
} else { cur->forward->seq + cur->forward->len +
fwd = 1; cur->forward->confirm;
cur->time = time(NULL);
cur->remote_seq =
cur->forward->seq + cur->forward->len + cur->forward->confirm;
struct segment *p = cur->forward; struct segment *p = cur->forward;
cur->forward = cur->forward->next; cur->forward = cur->forward->next;
free(p->data); free(p->data);
free(p); free(p);
}
}
// Log data buffered
struct segment *s = cur->forward;
while (s != NULL) {
log_android(ANDROID_LOG_WARN, "%s buffered %u...%u+%u",
session,
s->seq - cur->remote_start,
s->seq + s->len - cur->remote_start,
s->confirm);
s = s->next;
}
// Acknowledge forwarded data
// TODO send less ACKs?
if (fwd)
write_ack(args, cur, 0);
}
if (FD_ISSET(cur->socket, wfds) &&
cur->state == TCP_CLOSE_WAIT && cur->forward == NULL) {
if (close(cur->socket)) // Remote could have closed already
log_android(ANDROID_LOG_ERROR, "%s close error %d: %s",
session, errno, strerror(errno));
else
log_android(ANDROID_LOG_DEBUG, "%s closed", session);
cur->state = TCP_CLOSE;
} else if (cur->state == TCP_ESTABLISHED) {
// Check socket read
if (FD_ISSET(cur->socket, rfds) && cur->send_window > 0) {
cur->time = time(NULL);
size_t len = (cur->send_window > TCP_SEND_WINDOW
? TCP_SEND_WINDOW
: cur->send_window);
uint8_t *buffer = malloc(len);
ssize_t bytes = recv(cur->socket, buffer, len, 0);
if (bytes < 0) {
// Socket error
log_android(ANDROID_LOG_ERROR, "%s recv error %d: %s",
session, errno, strerror(errno));
if (errno != EINTR && errno != EAGAIN)
write_rst(args, cur);
}
else if (bytes == 0) {
// Socket eof
// TCP: application close
log_android(ANDROID_LOG_WARN, "%s recv empty", session);
if (cur->forward == NULL) {
if (cur->state == TCP_CLOSE_WAIT) {
if (write_fin_ack(args, cur) >= 0) {
cur->local_seq++; // local FIN
cur->state = TCP_LAST_ACK;
}
} else {
log_android(ANDROID_LOG_ERROR, "%s unexpected close",
session);
write_rst(args, cur);
}
}
else {
// There was still data to send
log_android(ANDROID_LOG_ERROR, "%s unexpected close (2)",
session);
write_rst(args, cur);
}
} else {
// Socket read data
log_android(ANDROID_LOG_DEBUG, "%s recv bytes %d", session, bytes);
// Forward to tun
if (write_data(args, cur, buffer, (size_t) bytes) >= 0)
cur->local_seq += bytes;
}
free(buffer);
} }
} }
// Log data buffered
struct segment *s = cur->forward;
while (s != NULL) {
log_android(ANDROID_LOG_WARN,
"%s/%u to %s/%u buffered data %u...%u seq %d state %s",
source, ntohs(cur->source), dest, ntohs(cur->dest),
s->seq - cur->remote_start,
s->seq + s->len - cur->remote_start,
cur->remote_seq - cur->remote_start,
strstate(cur->state));
s = s->next;
}
// Acknowledge forwarded data
// TODO send less ACKs?
if (fwd)
write_ack(args, cur, 0);
} }
} }
if (cur->state != oldstate || cur->local_seq != oldlocal || if (cur->state != oldstate || cur->local_seq != oldlocal ||
cur->remote_seq != oldremote) cur->remote_seq != oldremote)
log_android(ANDROID_LOG_DEBUG, log_android(ANDROID_LOG_DEBUG, "%s new state", session);
"TCP session %s/%u new state %s local %u remote %u",
dest, ntohs(cur->dest), strstate(cur->state),
cur->local_seq - cur->local_start,
cur->remote_seq - cur->remote_start);
} }
cur = cur->next; cur = cur->next;
} }
@ -2376,7 +2395,7 @@ jboolean handle_tcp(const struct arguments *args,
cur->remote_seq - cur->remote_start); cur->remote_seq - cur->remote_start);
// Session found // Session found
if (cur->state == TCP_CLOSE) { if (cur->state == TCP_CLOSING || cur->state == TCP_CLOSE) {
log_android(ANDROID_LOG_WARN, "%s was closed", session); log_android(ANDROID_LOG_WARN, "%s was closed", session);
write_rst(args, cur); write_rst(args, cur);
return 0; return 0;
@ -2402,7 +2421,7 @@ jboolean handle_tcp(const struct arguments *args,
// TODO half-duplex close sequence // TODO half-duplex close sequence
// http://tools.ietf.org/html/rfc1122#page-87 // http://tools.ietf.org/html/rfc1122#page-87
log_android(ANDROID_LOG_WARN, "%s received reset", session); log_android(ANDROID_LOG_WARN, "%s received reset", session);
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
return 0; return 0;
} }
else { else {
@ -2414,12 +2433,6 @@ jboolean handle_tcp(const struct arguments *args,
} else if (tcphdr->fin /* +ACK */) { } else if (tcphdr->fin /* +ACK */) {
if (cur->state == TCP_ESTABLISHED) if (cur->state == TCP_ESTABLISHED)
cur->state = TCP_CLOSE_WAIT; cur->state = TCP_CLOSE_WAIT;
else if (cur->state == TCP_FIN_WAIT1 && tcphdr->ack)
cur->state = TCP_TIME_WAIT;
else if (cur->state == TCP_FIN_WAIT1 && !tcphdr->ack)
cur->state = TCP_CLOSING;
else if (cur->state == TCP_FIN_WAIT2)
cur->state = TCP_TIME_WAIT;
else { else {
log_android(ANDROID_LOG_ERROR, "%s invalid FIN", session); log_android(ANDROID_LOG_ERROR, "%s invalid FIN", session);
return 0; return 0;
@ -2433,11 +2446,7 @@ jboolean handle_tcp(const struct arguments *args,
else if (cur->state == TCP_ESTABLISHED) { else if (cur->state == TCP_ESTABLISHED) {
// Do nothing // Do nothing
} else if (cur->state == TCP_LAST_ACK) } else if (cur->state == TCP_LAST_ACK)
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
else if (cur->state == TCP_FIN_WAIT1)
cur->state = TCP_FIN_WAIT2;
else if (cur->state == TCP_CLOSING)
cur->state = TCP_TIME_WAIT;
else { else {
log_android(ANDROID_LOG_ERROR, "%s invalid state", session); log_android(ANDROID_LOG_ERROR, "%s invalid state", session);
return 0; return 0;
@ -2668,7 +2677,7 @@ int32_t get_local_port(const int sock) {
int write_syn_ack(const struct arguments *args, struct tcp_session *cur) { int write_syn_ack(const struct arguments *args, struct tcp_session *cur) {
if (write_tcp(args, cur, NULL, 0, 1, 1, 1, 0, 0) < 0) { if (write_tcp(args, cur, NULL, 0, 1, 1, 1, 0, 0) < 0) {
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
return -1; return -1;
} }
return 0; return 0;
@ -2676,7 +2685,7 @@ int write_syn_ack(const struct arguments *args, struct tcp_session *cur) {
int write_ack(const struct arguments *args, struct tcp_session *cur, size_t bytes) { int write_ack(const struct arguments *args, struct tcp_session *cur, size_t bytes) {
if (write_tcp(args, cur, NULL, 0, bytes, 0, 1, 0, 0) < 0) { if (write_tcp(args, cur, NULL, 0, bytes, 0, 1, 0, 0) < 0) {
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
return -1; return -1;
} }
return 0; return 0;
@ -2685,7 +2694,7 @@ int write_ack(const struct arguments *args, struct tcp_session *cur, size_t byte
int write_data(const struct arguments *args, struct tcp_session *cur, int write_data(const struct arguments *args, struct tcp_session *cur,
const uint8_t *buffer, size_t length) { const uint8_t *buffer, size_t length) {
if (write_tcp(args, cur, buffer, length, 0, 0, 1, 0, 0) < 0) { if (write_tcp(args, cur, buffer, length, 0, 0, 1, 0, 0) < 0) {
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
return -1; return -1;
} }
return 0; return 0;
@ -2693,7 +2702,7 @@ int write_data(const struct arguments *args, struct tcp_session *cur,
int write_fin_ack(const struct arguments *args, struct tcp_session *cur) { int write_fin_ack(const struct arguments *args, struct tcp_session *cur) {
if (write_tcp(args, cur, NULL, 0, 0, 0, 1, 1, 0) < 0) { if (write_tcp(args, cur, NULL, 0, 0, 0, 1, 1, 0) < 0) {
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
return -1; return -1;
} }
return 0; return 0;
@ -2702,7 +2711,7 @@ int write_fin_ack(const struct arguments *args, struct tcp_session *cur) {
void write_rst(const struct arguments *args, struct tcp_session *cur) { void write_rst(const struct arguments *args, struct tcp_session *cur) {
write_tcp(args, cur, NULL, 0, 0, 0, 0, 0, 1); write_tcp(args, cur, NULL, 0, 0, 0, 0, 0, 1);
if (cur->state != TCP_CLOSE) if (cur->state != TCP_CLOSE)
cur->state = TCP_TIME_WAIT; cur->state = TCP_CLOSING;
} }
// TODO common UDP/TCP // TODO common UDP/TCP
@ -3666,7 +3675,7 @@ const char *strstate(const int state) {
return "LAST_ACK"; return "LAST_ACK";
case TCP_LISTEN: case TCP_LISTEN:
return "LISTEN"; return "LISTEN";
case TCP_CLOSING: case TCP_CLOSING:
return "CLOSING"; return "CLOSING";
default: default:
return "UNKNOWN"; return "UNKNOWN";