Goto sanos source index
//
// tcp.c
//
// Transmission Control Protocol (TCP)
//
// This file contains common functions for the TCP implementation, such as functinos
// for manipulating the data structures and the TCP timer functions. TCP functions
// related to input and output is found in tcp_input.c and tcp_output.c respectively.
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
// Portions Copyright (C) 2001, Swedish Institute of Computer Science.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <net/net.h>
unsigned long tcp_ticks;
unsigned long iss;
unsigned short tcp_next_port;
struct timer tcpslow_timer;
struct timer tcpfast_timer;
struct task tcp_slow_task;
struct task tcp_fast_task;
unsigned char tcp_backoff[13] = {1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
// TCP PCB lists
struct tcp_pcb_listen *tcp_listen_pcbs; // TCP PCBs in LISTEN state
struct tcp_pcb *tcp_active_pcbs; // TCP PCBs that are in a state in which they accept or send data
struct tcp_pcb *tcp_tw_pcbs; // TCP PCBs in TIME-WAIT
#define MIN(x,y) ((x) < (y) ? (x): (y))
//
// tcpstat_proc
//
static int tcpstat_proc(struct proc_file *pf, void *arg) {
static char *statename[] = {"CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", "ESTABLISHED", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSE_WAIT", "CLOSING", "LAST_ACK", "TIME_WAIT"};
struct tcp_pcb *pcb;
pprintf(pf, "local port remote port local ip remote ip state\n");
pprintf(pf, "----------- ----------- --------------- --------------- -----------\n");
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
pprintf(pf, "%8d %8d %-15a %-15a %s\n", pcb->local_port, pcb->remote_port, &pcb->local_ip, &pcb->remote_ip, statename[pcb->state]);
}
for (pcb = (struct tcp_pcb *) tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) {
pprintf(pf, "%8d %8d %-15a %-15a %s\n", pcb->local_port, pcb->remote_port, &pcb->local_ip, &pcb->remote_ip, statename[pcb->state]);
}
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
pprintf(pf, "%8d %8d %-15a %-15a %s\n", pcb->local_port, pcb->remote_port, &pcb->local_ip, &pcb->remote_ip, statename[pcb->state]);
}
return 0;
}
//
// tcp_new_port
//
// A nastly hack featuring 'goto' statements that allocates a
// new TCP local port.
//
static unsigned short tcp_new_port() {
struct tcp_pcb *pcb;
again:
if (++tcp_next_port > 0x7FFF) tcp_next_port = 4096;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == tcp_next_port) goto again;
}
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == tcp_next_port) goto again;
}
for (pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == tcp_next_port) goto again;
}
return tcp_next_port;
}
//
// tcp_close
//
// Closes the connection held by the PCB.
//
err_t tcp_close(struct tcp_pcb *pcb) {
err_t err;
switch (pcb->state) {
case LISTEN:
err = 0;
tcp_pcb_remove((struct tcp_pcb **) &tcp_listen_pcbs, pcb);
kfree(pcb);
pcb = NULL;
break;
case SYN_SENT:
err = 0;
tcp_pcb_remove(&tcp_active_pcbs, pcb);
kfree(pcb);
pcb = NULL;
break;
case SYN_RCVD:
err = tcp_send_ctrl(pcb, TCP_FIN);
if (err == 0) pcb->state = FIN_WAIT_1;
break;
case ESTABLISHED:
err = tcp_send_ctrl(pcb, TCP_FIN);
if (err == 0) pcb->state = FIN_WAIT_1;
break;
case CLOSE_WAIT:
err = tcp_send_ctrl(pcb, TCP_FIN);
if (err == 0) pcb->state = LAST_ACK;
break;
default:
// Has already been closed, do nothing
err = 0;
pcb = NULL;
break;
}
if (pcb != NULL && err == 0) err = tcp_output(pcb);
return err;
}
//
// tcp_abort
//
// Aborts a connection by sending a RST to the remote host and deletes
// the local protocol control block.
//
void tcp_abort(struct tcp_pcb *pcb) {
unsigned long seqno, ackno;
unsigned short remote_port, local_port;
struct ip_addr remote_ip, local_ip;
void (*errf)(void *arg, err_t err);
void *errf_arg;
// Figure out on which TCP PCB list we are, and remove us. If we
// are in an active state, call the receive function associated with
// the PCB with a NULL argument, and send an RST to the remote end.
if (pcb->state == TIME_WAIT) {
tcp_pcb_remove(&tcp_tw_pcbs, pcb);
kfree(pcb);
} else {
seqno = pcb->snd_nxt;
ackno = pcb->rcv_nxt;
ip_addr_set(&local_ip, &pcb->local_ip);
ip_addr_set(&remote_ip, &pcb->remote_ip);
local_port = pcb->local_port;
remote_port = pcb->remote_port;
errf = pcb->errf;
errf_arg = pcb->callback_arg;
tcp_pcb_remove(&tcp_active_pcbs, pcb);
if (pcb->unacked) tcp_segs_free(pcb->unacked);
if (pcb->unsent) tcp_segs_free(pcb->unsent);
if (pcb->ooseq) tcp_segs_free(pcb->ooseq);
kfree(pcb);
if (errf != NULL) errf(errf_arg, -EABORT);
//kprintf("tcp_abort: sending RST\n");
tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
}
}
//
// tcp_bind
//
// Binds the connection to a local portnumber and IP address. If the
// IP address is not given (i.e., ipaddr == NULL), the IP address of
// the outgoing network interface is used instead.
//
err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, unsigned short port) {
struct tcp_pcb *cpcb;
// Assign new port if port number is zero
if (port == 0) port = tcp_new_port();
// Check if the address already is in use
for (cpcb = (struct tcp_pcb *) tcp_listen_pcbs; cpcb != NULL; cpcb = cpcb->next) {
if (cpcb->local_port == port) {
if (ip_addr_isany(&cpcb->local_ip) ||
ip_addr_isany(ipaddr) ||
ip_addr_cmp(&cpcb->local_ip, ipaddr)) {
return -EADDRINUSE;
}
}
}
for (cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next) {
if (cpcb->local_port == port) {
if (ip_addr_isany(&cpcb->local_ip) ||
ip_addr_isany(ipaddr) ||
ip_addr_cmp(&cpcb->local_ip, ipaddr)) {
return -EADDRINUSE;
}
}
}
if (!ip_addr_isany(ipaddr)) {
if (!ip_ownaddr(ipaddr)) return -EADDRNOTAVAIL;
pcb->local_ip = *ipaddr;
}
pcb->local_port = port;
//kprintf("tcp_bind: bind to port %d\n", port);
return 0;
}
//
// tcp_listen
//
// Set the state of the connection to be LISTEN, which means that it
// is able to accept incoming connections. The protocol control block
// is reallocated in order to consume less memory. Setting the
// connection to LISTEN is an irreversible process.
//
struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) {
if (pcb->state == LISTEN) return pcb;
pcb->state = LISTEN;
TCP_REG((struct tcp_pcb **) &tcp_listen_pcbs, pcb);
return pcb;
}
//
// tcp_recved
//
// This function should be called by the application when it has
// processed the data. The purpose is to advertise a larger window
// when the data has been processed.
//
void tcp_recved(struct tcp_pcb *pcb, int len) {
pcb->rcv_wnd += len;
if (pcb->rcv_wnd > TCP_WND) pcb->rcv_wnd = TCP_WND;
//if (!(pcb->flags & TF_ACK_DELAY) && !(pcb->flags & TF_ACK_NOW)) tcp_ack(pcb);
if (!(pcb->flags & TF_IN_RECV)) pcb->flags |= TF_ACK_DELAY;
//kprintf("tcp_recved: received %d bytes, wnd %u (%u).\n", len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd);
}
//
// tcp_connect()
//
// Connects to another host. The function given as the "connected"
// argument will be called when the connection has been established.
//
//
err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, unsigned short port,
err_t (*connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) {
unsigned long optdata;
err_t ret;
unsigned long iss;
//kprintf("tcp_connect to port %d\n", port);
if (ip_addr_isany(ipaddr) || port == 0) return -EADDRNOTAVAIL;
pcb->remote_ip = *ipaddr;
pcb->remote_port = port;
if (pcb->local_port == 0) pcb->local_port = tcp_new_port();
iss = tcp_next_iss();
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
pcb->snd_lbb = iss - 1;
pcb->rcv_wnd = TCP_WND;
pcb->snd_wnd = TCP_WND;
pcb->mss = TCP_MSS;
pcb->cwnd = 1;
pcb->ssthresh = pcb->mss * 10;
pcb->state = SYN_SENT;
pcb->connected = connected;
TCP_REG(&tcp_active_pcbs, pcb);
// Build an MSS option
optdata = HTONL(((unsigned long) 2 << 24) |
((unsigned long) 4 << 16) |
(((unsigned long) pcb->mss / 256) << 8) |
(pcb->mss & 255));
ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, (unsigned char *) &optdata, 4);
if (ret == 0) tcp_output(pcb);
return ret;
}
//
// tcp_slowtmr
//
// Called every 500 ms and implements the retransmission timer and the timer that
// removes PCBs that have been in TIME-WAIT for enough time. It also increments
// various timers such as the inactivity timer in each PCB.
//
void tcp_slowtmr(void *arg) {
struct tcp_pcb *pcb, *pcb2, *prev;
unsigned long eff_wnd;
int pcb_remove; // flag if a PCB should be removed
tcp_ticks++;
//kprintf("tcp_slowtmr: ticks %d\n", tcp_ticks);
// Steps through all of the active PCBs.
prev = NULL;
pcb = tcp_active_pcbs;
while (pcb != NULL) {
pcb_remove = 0;
if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
pcb_remove++;
} else if (pcb->nrtx == TCP_MAXRTX) {
pcb_remove++;
} else {
pcb->rtime++;
if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
//kprintf("tcp_slowtmr: rtime %ld pcb->rto %d\n", tcp_ticks - pcb->rtime, pcb->rto);
// Double retransmission time-out unless we are trying to
// connect to somebody (i.e., we are in SYN_SENT)
if (pcb->state != SYN_SENT) {
pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
}
// Retransmit unacknowled segments
tcp_rexmit(pcb);
// Reduce congestion window and ssthresh
eff_wnd = MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
if (pcb->ssthresh < (unsigned long) pcb->mss) pcb->ssthresh = pcb->mss * 2;
pcb->cwnd = pcb->mss;
//kprintf("tcp_rexmit_seg: cwnd %u ssthresh %u\n", pcb->cwnd, pcb->ssthresh);
}
}
// Check if this PCB has stayed too long in FIN-WAIT-2
if (pcb->state == FIN_WAIT_2) {
if ((unsigned long) (tcp_ticks - pcb->tmr) > TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) pcb_remove++;
}
// If this PCB has queued out of sequence data, but has been
// inactive for too long, will drop the data (it will eventually
// be retransmitted).
if (pcb->ooseq != NULL && (unsigned long) (tcp_ticks - pcb->tmr) >= (unsigned long) (pcb->rto * TCP_OOSEQ_TIMEOUT)) {
tcp_segs_free(pcb->ooseq);
pcb->ooseq = NULL;
}
// Check if this PCB has stayed too long in SYN-RCVD
if (pcb->state == SYN_RCVD) {
if ((unsigned long)(tcp_ticks - pcb->tmr) > TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) pcb_remove++;
}
// If the PCB should be removed, do it
if (pcb_remove) {
tcp_pcb_purge(pcb);
// Remove PCB from tcp_active_pcbs list
if (prev != NULL) {
prev->next = pcb->next;
} else {
tcp_active_pcbs = pcb->next;
}
if (pcb->errf != NULL) {
pcb->errf(pcb->callback_arg, -EABORT);
}
pcb2 = pcb->next;
kfree(pcb);
pcb = pcb2;
} else {
// We check if we should poll the connection
pcb->polltmr++;
if (pcb->polltmr >= pcb->pollinterval && pcb->poll != NULL) {
pcb->polltmr = 0;
pcb->poll(pcb->callback_arg, pcb);
tcp_output(pcb);
}
prev = pcb;
pcb = pcb->next;
}
}
// Steps through all of the TIME-WAIT PCBs.
prev = NULL;
pcb = tcp_tw_pcbs;
while (pcb != NULL) {
pcb_remove = 0;
// Check if this PCB has stayed long enough in TIME-WAIT
if ((unsigned long)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) pcb_remove++;
// If the PCB should be removed, do it
if (pcb_remove) {
tcp_pcb_purge(pcb);
// Remove PCB from tcp_tw_pcbs list
if (prev != NULL) {
prev->next = pcb->next;
} else {
tcp_tw_pcbs = pcb->next;
}
pcb2 = pcb->next;
kfree(pcb);
pcb = pcb2;
} else {
prev = pcb;
pcb = pcb->next;
}
}
}
//
//
// tcp_slow_handler
//
void tcp_slow_handler(void *arg) {
queue_task(&sys_task_queue, &tcp_slow_task, tcp_slowtmr, NULL);
mod_timer(&tcpslow_timer, ticks + TCP_SLOW_INTERVAL / MSECS_PER_TICK);
}
//
//
// tcp_fasttmr
//
// Is called every 100 ms and sends delayed ACKs
//
void tcp_fasttmr(void *arg) {
struct tcp_pcb *pcb;
// Send delayed ACKs
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->flags & TF_ACK_DELAY) {
//kprintf("tcp_fasttmr: delayed ACK\n");
pcb->flags |= TF_ACK_NOW;
tcp_output(pcb);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
}
}
//
//
// tcp_fast_handler
//
void tcp_fast_handler(void *arg) {
queue_task(&sys_task_queue, &tcp_fast_task, tcp_fasttmr, NULL);
mod_timer(&tcpfast_timer, ticks + TCP_FAST_INTERVAL / MSECS_PER_TICK);
}
//
// tcp_segs_free
//
// Deallocates a list of TCP segments (tcp_seg structures).
//
//
int tcp_segs_free(struct tcp_seg *seg) {
int count = 0;
struct tcp_seg *next;
while (seg != NULL) {
next = seg->next;
count += tcp_seg_free(seg);
seg = next;
}
return count;
}
//
// tcp_seg_free
//
// Frees a TCP segment.
//
int tcp_seg_free(struct tcp_seg *seg) {
int count = 0;
if (seg != NULL) {
if (seg->p == NULL) {
kfree(seg);
} else {
count = pbuf_free(seg->p);
kfree(seg);
}
}
return count;
}
//
// tcp_seg_copy
//
// Returns a copy of the given TCP segment.
//
struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg) {
struct tcp_seg *cseg;
cseg = (struct tcp_seg *) kmalloc(sizeof(struct tcp_seg));
if (cseg == NULL) return NULL;
memcpy(cseg, seg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
return cseg;
}
//
// tcp_new
//
// Creates a new TCP protocol control block but doesn't place it on
// any of the TCP PCB lists.
//
struct tcp_pcb *tcp_new() {
struct tcp_pcb *pcb;
unsigned long iss;
pcb = (struct tcp_pcb *) kmalloc(sizeof(struct tcp_pcb));
if (pcb == NULL) return NULL;
memset(pcb, 0, sizeof(struct tcp_pcb));
pcb->snd_buf = TCP_SND_BUF;
pcb->snd_queuelen = 0;
pcb->rcv_wnd = TCP_WND;
pcb->mss = TCP_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
pcb->sa = 0;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = 0;
pcb->cwnd = 1;
iss = tcp_next_iss();
pcb->snd_wl2 = iss;
pcb->snd_nxt = iss;
pcb->snd_max = iss;
pcb->lastack = iss;
pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->polltmr = 0;
return pcb;
}
//
// tcp_init
//
// Initializes the TCP layer
//
void tcp_init() {
// Initialize timer
iss = time(0) + 6510;
tcp_next_port = (unsigned short) (4096 + (time(0) % 1024));
tcp_ticks = 0;
init_task(&tcp_slow_task);
init_task(&tcp_fast_task);
init_timer(&tcpslow_timer, tcp_slow_handler, NULL);
init_timer(&tcpfast_timer, tcp_fast_handler, NULL);
mod_timer(&tcpslow_timer, ticks + TCP_SLOW_INTERVAL / MSECS_PER_TICK);
mod_timer(&tcpfast_timer, ticks + TCP_FAST_INTERVAL / MSECS_PER_TICK);
register_proc_inode("tcpstat", tcpstat_proc, NULL);
}
//
// tcp_shutdown
//
// Shutdown TCP layer by resetting all active connections
//
void tcp_shutdown() {
struct tcp_pcb *pcb;
// Send RST for all active connections
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
tcp_rst(pcb->snd_nxt, pcb->rcv_nxt,
&pcb->local_ip, &pcb->remote_ip,
pcb->local_port, pcb->remote_port);
}
}
//
// tcp_arg
//
// Used to specify the argument that should be passed callback
// functions.
//
void tcp_arg(struct tcp_pcb *pcb, void *arg) {
pcb->callback_arg = arg;
}
//
// tcp_recv
//
// Used to specify the function that should be called when a TCP
// connection receives data.
//
void tcp_recv(struct tcp_pcb *pcb, err_t (*recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) {
pcb->recv = recv;
}
//
// tcp_sent
//
// Used to specify the function that should be called when TCP data
// has been successfully delivered to the remote host.
//
void tcp_sent(struct tcp_pcb *pcb, err_t (*sent)(void *arg, struct tcp_pcb *tpcb, unsigned short len)) {
pcb->sent = sent;
}
//
// tcp_err():
//
// Used to specify the function that should be called when a fatal error
// has occured on the connection.
//
void tcp_err(struct tcp_pcb *pcb, void (*errf)(void *arg, err_t err)) {
pcb->errf = errf;
}
//
// tcp_poll
//
// Used to specify the function that should be called periodically
// from TCP. The interval is specified in terms of the TCP coarse
// timer interval, which is called twice a second.
//
void tcp_poll(struct tcp_pcb *pcb, err_t (*poll)(void *arg, struct tcp_pcb *tpcb), int interval) {
pcb->poll = poll;
pcb->pollinterval = interval;
}
//
// tcp_accept
//
// Used for specifying the function that should be called when a
// LISTENing connection has been connected to another host.
//
void tcp_accept(struct tcp_pcb *pcb, err_t (*accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) {
pcb->accept = accept;
}
//
// tcp_pcb_purge
//
// Purges a TCP PCB. Removes any buffered data and frees the buffer memory.
//
void tcp_pcb_purge(struct tcp_pcb *pcb) {
if (pcb->state != CLOSED && pcb->state != TIME_WAIT && pcb->state != LISTEN) {
tcp_segs_free(pcb->unsent);
tcp_segs_free(pcb->ooseq);
tcp_segs_free(pcb->unacked);
pcb->unacked = pcb->unsent = pcb->ooseq = NULL;
}
}
//
// tcp_pcb_remove
//
// Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
//
void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) {
TCP_RMV(pcblist, pcb);
tcp_pcb_purge(pcb);
// If there is an outstanding delayed ACKs, send it
if (pcb->state != TIME_WAIT && pcb->state != LISTEN && pcb->flags & TF_ACK_DELAY) {
pcb->flags |= TF_ACK_NOW;
tcp_output(pcb);
}
pcb->state = CLOSED;
}
//
// tcp_next_iss
//
// Calculates a new initial sequence number for new connections.
//
unsigned long tcp_next_iss() {
iss += tcp_ticks;
return iss;
}
void tcp_debug_print(struct tcp_hdr *tcphdr) {
kprintf("+-------------------------------+\n");
kprintf("| %4d | %4d | (src port, dest port)\n", ntohs(tcphdr->src), ntohs(tcphdr->dest));
kprintf("+-------------------------------+\n");
kprintf("| %10lu | (seq no)\n", ntohl(tcphdr->seqno));
kprintf("+-------------------------------+\n");
kprintf("| %10lu | (ack no)\n", ntohl(tcphdr->ackno));
kprintf("+-------------------------------+\n");
kprintf("| %2d | |%d%d%d%d%d| %5d | (offset, flags (",
ntohs(tcphdr->_offset_flags) >> 4 & 1,
ntohs(tcphdr->_offset_flags) >> 4 & 1,
ntohs(tcphdr->_offset_flags) >> 3 & 1,
ntohs(tcphdr->_offset_flags) >> 2 & 1,
ntohs(tcphdr->_offset_flags) >> 1 & 1,
ntohs(tcphdr->_offset_flags) & 1,
ntohs(tcphdr->wnd));
tcp_debug_print_flags(ntohs(tcphdr->_offset_flags));
kprintf("), win)\n");
kprintf("+-------------------------------+\n");
kprintf("| 0x%04x | %5d | (chksum, urgp)\n", ntohs(tcphdr->chksum), ntohs(tcphdr->urgp));
kprintf("+-------------------------------+\n");
}
void tcp_debug_print_state(enum tcp_state s) {
kprintf("State: ");
switch (s) {
case CLOSED: kprintf("CLOSED\n"); break;
case LISTEN: kprintf("LISTEN\n"); break;
case SYN_SENT: kprintf("SYN_SENT\n"); break;
case SYN_RCVD: kprintf("SYN_RCVD\n"); break;
case ESTABLISHED: kprintf("ESTABLISHED\n"); break;
case FIN_WAIT_1: kprintf("FIN_WAIT_1\n"); break;
case FIN_WAIT_2: kprintf("FIN_WAIT_2\n"); break;
case CLOSE_WAIT: kprintf("CLOSE_WAIT\n"); break;
case CLOSING: kprintf("CLOSING\n"); break;
case LAST_ACK: kprintf("LAST_ACK\n"); break;
case TIME_WAIT: kprintf("TIME_WAIT\n"); break;
}
}
void tcp_debug_print_flags(int flags) {
if (flags & TCP_FIN) kprintf("FIN ");
if (flags & TCP_SYN) kprintf("SYN ");
if (flags & TCP_RST) kprintf("RST ");
if (flags & TCP_PSH) kprintf("PSH ");
if (flags & TCP_ACK) kprintf("ACK ");
if (flags & TCP_URG) kprintf("URG ");
}
void tcp_debug_print_pcbs() {
struct tcp_pcb *pcb;
kprintf("Active PCB states:\n");
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
kprintf("Local port %d, remote port %d snd_nxt %lu rcv_nxt %lu ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt);
tcp_debug_print_state(pcb->state);
}
kprintf("Listen PCB states:\n");
for (pcb = (struct tcp_pcb *) tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) {
kprintf("Local port %d, remote port %d snd_nxt %lu rcv_nxt %lu ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt);
tcp_debug_print_state(pcb->state);
}
kprintf("TIME-WAIT PCB states:\n");
for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
kprintf("Local port %d, remote port %d snd_nxt %lu rcv_nxt %lu ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt);
tcp_debug_print_state(pcb->state);
}
}