📄 tcplib.c
字号:
/*
* "Copyright (c) 2008, 2009 The Regents of the University of California.
* All rights reserved."
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
*
*/
/* A nonblocking library-based implementation of TCP
*
* There are some things like timers which need to be handled
* externally with callbacks.
*
*/
#include <stdio.h>
#include <string.h>
#include "ip_malloc.h"
#include "in_cksum.h"
#include "6lowpan.h"
#include "ip.h"
#include "tcplib.h"
#include "circ.h"
static struct tcplib_sock *conns = NULL;
#define ONE_SEGMENT(X) ((X)->mss)
uint16_t alloc_local_port() {
return 32012;
}
static inline void conn_add_once(struct tcplib_sock *sock) {
struct tcplib_sock *iter;
for (iter = conns; iter != NULL; iter = iter->next) {
if (iter == sock) break;
}
if (iter == NULL) {
sock->next = conns;
conns = sock;
}
}
static int isInaddrAny(struct in6_addr *addr) {
int i;
for (i = 0; i < 8; i++)
if (addr->s6_addr16[i] != 0) break;
if (i != 8) return 0;
return 1;
}
#ifdef PC
#include <arpa/inet.h>
void print_conn(struct tcplib_sock *sock) {
char addr_buf[32];
printf("tcplib socket state: %i:\n", sock->state);
inet_ntop(AF_INET6, sock->l_ep.sin6_addr.s6_addr, addr_buf, 32);
printf(" local ep: %s port: %u\n", addr_buf, ntohs(sock->l_ep.sin6_port));
inet_ntop(AF_INET6, sock->r_ep.sin6_addr.s6_addr, addr_buf, 32);
printf(" remote ep: %s port: %u\n", addr_buf, ntohs(sock->r_ep.sin6_port));
printf(" tx buf length: %i\n", sock->tx_buf_len);
}
void print_headers(struct ip6_hdr *iph, struct tcp_hdr *tcph) {
char addr_buf[32];
printf("headers ip length: %i:\n", ntohs(iph->plen));
inet_ntop(AF_INET6, iph->ip6_src.s6_addr, addr_buf, 32);
printf(" source: %s port: %u\n", addr_buf, ntohs(tcph->srcport));
inet_ntop(AF_INET6, iph->ip6_dst.s6_addr, addr_buf, 32);
printf(" remote ep: %s port: %u\n", addr_buf, ntohs(tcph->dstport));
printf(" tcp seqno: %u ackno: %u\n", ntohl(tcph->seqno), ntohl(tcph->ackno));
}
#endif
static struct tcplib_sock *conn_lookup(struct ip6_hdr *iph,
struct tcp_hdr *tcph) {
struct tcplib_sock *iter;
//printf("looking up conns: %p %p\n", iph, tcph);
// print_headers(iph, tcph);
for (iter = conns; iter != NULL; iter = iter->next) {
// print_conn(iter);
printf("conn lport: %i\n", ntohs(iter->l_ep.sin6_port));
if (((memcmp(iph->ip6_dst.s6_addr, iter->l_ep.sin6_addr.s6_addr, 16) == 0) ||
isInaddrAny(&iter->l_ep.sin6_addr)) &&
tcph->dstport == iter->l_ep.sin6_port &&
(iter->r_ep.sin6_port == 0 ||
(memcmp(&iph->ip6_src, &iter->r_ep.sin6_addr, 16) == 0 &&
tcph->srcport == iter->r_ep.sin6_port)))
return iter;
}
return NULL;
}
static int conn_checkport(uint16_t port) {
struct tcplib_sock *iter;
for (iter = conns; iter != NULL; iter = iter->next) {
if (iter->l_ep.sin6_port == port)
return -1;
}
return 0;
}
struct tcp_hdr *find_tcp_hdr(struct split_ip_msg *msg) {
if (msg->hdr.nxt_hdr == IANA_TCP) {
return (struct tcp_hdr *)((msg->headers == NULL) ? msg->data :
msg->headers->hdr.data);
}
return NULL;
}
static struct split_ip_msg *get_ipmsg(int plen) {
struct split_ip_msg *msg =
(struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) + sizeof(struct tcp_hdr) + plen);
if (msg == NULL) return NULL;
memset(msg, 0, sizeof(struct split_ip_msg) + sizeof(struct tcp_hdr));
msg->hdr.nxt_hdr = IANA_TCP;
msg->hdr.plen = htons(sizeof(struct tcp_hdr) + plen);
msg->headers = NULL;
msg->data = (void *)(msg + 1);
msg->data_len = sizeof(struct tcp_hdr) + plen;
return msg;
}
static void __tcplib_send(struct tcplib_sock *sock,
struct split_ip_msg *msg) {
struct tcp_hdr *tcph = find_tcp_hdr(msg);
if (tcph == NULL) return;
memcpy(&msg->hdr.ip6_dst, &sock->r_ep.sin6_addr, 16);
sock->flags &= ~TCP_ACKPENDING;
// sock->ackno = ntohl(tcph->ackno);
tcph->srcport = sock->l_ep.sin6_port;
tcph->dstport = sock->r_ep.sin6_port;
tcph->offset = sizeof(struct tcp_hdr) * 4;
tcph->window = htons(sock->my_wind);
tcph->chksum = 0;
tcph->urgent = 0;
tcplib_send_out(msg, tcph);
}
static void tcplib_send_ack(struct tcplib_sock *sock, int fin_seqno, uint8_t flags) {
struct split_ip_msg *msg = get_ipmsg(0);
if (msg != NULL) {
struct tcp_hdr *tcp_rep = (struct tcp_hdr *)(msg + 1);
tcp_rep->flags = flags;
tcp_rep->seqno = htonl(sock->seqno);
tcp_rep->ackno = htonl(sock->ackno +
(fin_seqno ? 1 : 0));
// printf("sending ACK seqno: %u ackno: %u\n", ntohl(tcp_rep->seqno), ntohl(tcp_rep->ackno));
__tcplib_send(sock, msg);
ip_free(msg);
} else {
printf("Could not send ack-- no memory!\n");
}
}
static void tcplib_send_rst(struct ip6_hdr *iph, struct tcp_hdr *tcph) {
struct split_ip_msg *msg = get_ipmsg(0);
if (msg != NULL) {
struct tcp_hdr *tcp_rep = (struct tcp_hdr *)(msg + 1);
memcpy(&msg->hdr.ip6_dst, &iph->ip6_src, 16);
tcp_rep->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
tcp_rep->ackno = htonl(ntohl(tcph->seqno) + 1);
tcp_rep->seqno = tcph->ackno;;
tcp_rep->srcport = tcph->dstport;
tcp_rep->dstport = tcph->srcport;
tcp_rep->offset = sizeof(struct tcp_hdr) * 4;
tcp_rep->window = 0;
tcp_rep->chksum = 0;
tcp_rep->urgent = 0;
tcplib_send_out(msg, tcp_rep);
ip_free(msg);
}
}
/* send all the data in the tx buffer, starting at sseqno */
static int tcplib_output(struct tcplib_sock *sock, uint32_t sseqno) {
// the output size is the minimum of the advertised window and the
// conjestion window. of course, if we have less data we send even
// less.
int seg_size = min(sock->seqno - sseqno, sock->r_wind);
printf("r_wind: %i\n", sock->r_wind);
seg_size = min(seg_size, sock->cwnd);
while (seg_size > 0 && sock->seqno > sseqno) {
// printf("sending seg_size: %i\n", seg_size);
struct split_ip_msg *msg = get_ipmsg(seg_size);
struct tcp_hdr *tcph;
uint8_t *data;
if (msg == NULL) return -1;
tcph = (struct tcp_hdr *)(msg + 1);
data = (uint8_t *)(tcph + 1);
tcph->flags = TCP_FLAG_ACK;
tcph->seqno = htonl(sseqno);
tcph->ackno = htonl(sock->ackno);
printf("tcplib_output: seqno: %u ackno: %u len: %i headno: %u\n",
ntohl(tcph->seqno), ntohl(tcph->ackno), seg_size,
circ_get_seqno(sock->tx_buf));
if (seg_size != circ_buf_read(sock->tx_buf, sseqno, data, seg_size)) {
printf("WARN: circ could not read!\n");
}
__tcplib_send(sock, msg);
ip_free(msg);
sseqno += seg_size;
seg_size = min(sock->seqno - sseqno, sock->mss);
}
return 0;
}
int tcplib_init_sock(struct tcplib_sock *sock) {
memset(sock, 0, sizeof(struct tcplib_sock) - sizeof(struct tcplib_sock *));
sock->mss = 200;
sock->my_wind = 200;
sock->cwnd = ONE_SEGMENT(sock);
sock->ssthresh = 0xffff;
conn_add_once(sock);
return 0;
}
/* called when a new segment arrives. */
/* deliver as much data to the app as possible, and update the ack
* number of the socket to reflect how much was delivered
*/
static void receive_data(struct tcplib_sock *sock, struct tcp_hdr *tcph, int len) {
uint8_t *ptr;
int payload_len;
ptr = ((uint8_t *)tcph) + (tcph->offset / 4);
payload_len = len - (tcph->offset / 4);
sock->ackno = ntohl(tcph->seqno) + payload_len;
if (payload_len > 0) {
tcplib_extern_recv(sock, ptr, payload_len);
}
}
static void reset_ssthresh(struct tcplib_sock *conn) {
uint16_t new_ssthresh = min(conn->cwnd, conn->r_wind) / 2;
if (new_ssthresh < 2 * ONE_SEGMENT(conn))
new_ssthresh = 2 * ONE_SEGMENT(conn);
conn->ssthresh = new_ssthresh;
}
int tcplib_process(struct ip6_hdr *iph, void *payload) {
int rc = 0;
struct tcp_hdr *tcph;
struct tcplib_sock *this_conn;
// uint8_t *ptr;
int len = ntohs(iph->plen) + sizeof(struct ip6_hdr);
int payload_len;
uint32_t hdr_seqno, hdr_ackno;
tcph = (struct tcp_hdr *)payload;
payload_len = len - sizeof(struct ip6_hdr) - (tcph->offset / 4);
/* if there's no local */
this_conn = conn_lookup(iph, tcph);
// printf("conn: %p\n", this_conn);
if (this_conn != NULL) {
hdr_seqno = ntohl(tcph->seqno);
hdr_ackno = ntohl(tcph->ackno);
if (tcph->flags & TCP_FLAG_RST) {
/* Really hose this connection if we get a RST packet.
* still TODO: RST generation for unbound ports */
printf("connection reset by peer\n");
tcplib_extern_closedone(this_conn);
// tcplib_init_sock(this_conn);
return 0;
}
// always get window updates from new segments
// TODO : this should be after we detect out-of-sequence ACK
// numbers!
this_conn->r_wind = ntohs(tcph->window);
switch (this_conn->state) {
case TCP_LAST_ACK:
if (tcph->flags & TCP_FLAG_ACK &&
hdr_ackno == this_conn->seqno + 1) {
this_conn->state = TCP_CLOSED;
tcplib_extern_closedone(this_conn);
break;
}
case TCP_FIN_WAIT_1:
if (tcph->flags & TCP_FLAG_ACK &&
hdr_ackno == this_conn->seqno + 1) {
if (tcph->flags & TCP_FLAG_FIN) {
this_conn->seqno++;
this_conn->state = TCP_TIME_WAIT;
// the TIME_WAIT state is problematic, since it holds up the
// resources while we're in it...
this_conn->timer.retx = TCPLIB_TIMEWAIT_LEN;
} else {
this_conn->state = TCP_FIN_WAIT_2;
}
// this generate the ACK we need here
goto ESTABLISHED;
}
case TCP_FIN_WAIT_2:
break;
case TCP_SYN_SENT:
if (tcph->flags & (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
// got a syn-ack
// send the ACK this_conn
this_conn->state = TCP_ESTABLISHED;
this_conn->ackno = hdr_seqno + 1;
// skip the LISTEN processing
// this will also generate an ACK
goto ESTABLISHED;
} else if (this_conn->flags & TCP_FLAG_SYN) {
// otherwise the state machine says we're in a simultaneous open, so continue doen
this_conn->state = TCP_SYN_RCVD;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -