static void sink_input(struct flow *f, int dir, struct pkt *p)
{
+ pkt_free(p);
}
struct appl_hooks appl_sink = {
stat_tcp_on_closed;
struct pipe {
- list queue;
- u32 last_acked_seq;
- enum {
+ list queue; /* incoming packets */
+ u32 last_acked_seq; /* last sequence number for which I sent ACK */
+ u32 syn_or_fin_seq; /* sequence number of SYN/FIN I sent */
+ enum { /* very simplified TCP state machine */
FLOW_IDLE,
FLOW_SYN_SENT, /* sent SYN, waiting for SYN ACK */
FLOW_SYN_SENT_ACK, /* sent SYN ACK, waiting for first ACK */
FLOW_ESTABLISHED, /* established state including waiting for ACK of SYN ACK */
FLOW_FIN_SENT, /* sent FIN, waiting for its ACK */
- FLOW_CLOSED /* closed, ignoring further packets */
+ FLOW_FINISHED /* closed, ignoring further packets */
} state;
+ struct pkt_stats stat_in;
};
+static byte *pipe_state_names[] = { "IDLE", "SYNSENT", "SYNACK", "ESTAB", "FINSENT", "FINISH" };
+
struct flow {
struct flow *hash_next;
u32 saddr, daddr, sport, dport;
return p->timestamp >> 20;
}
+static inline int tcp_seq_le(u32 a, u32 b)
+{
+ return ((b - a) < 0x80000000);
+}
+
+static inline int tcp_seq_lt(u32 a, u32 b)
+{
+ return (a != b && tcp_seq_le(a, b));
+}
+
static void tcp_time_step(uns now)
{
while (num_flows && flow_heap[1]->timeout <= now)
{
struct flow *f = flow_heap[1];
HEAP_DELMIN(struct flow *, flow_heap, num_flows, FLOW_HEAP_LESS, FLOW_HEAP_SWAP);
- DBG("TIMEOUT for flow %p(%d/%d)\n", f, f->pipe[0].state, f->pipe[1].state);
- if (f->pipe[0].state != FLOW_CLOSED || f->pipe[1].state != FLOW_CLOSED)
+ DBG("TIMEOUT for flow %p(%s/%s)\n", f, pipe_state_names[f->pipe[0].state], pipe_state_names[f->pipe[1].state]);
+ if (f->pipe[0].state != FLOW_FINISHED || f->pipe[1].state != FLOW_FINISHED)
f->appl->close(f, (now == ~0U) ? CAUSE_DOOMSDAY : CAUSE_TIMEOUT);
uns h = flow_calc_hash(f->saddr, f->daddr, f->sport, f->dport);
struct flow **gg = &flow_hash[h];
}
}
+static void tcp_enqueue_data(struct pipe *b, struct pkt *p)
+{
+ struct pkt *q, *prev, *new;
+ u32 last_seq;
+
+ DBG("DATA:");
+ if (tcp_seq_lt(b->last_acked_seq, p->seq) && p->seq - b->last_acked_seq >= 0x40000)
+ {
+ DBG(" OUT OF WINDOW (last-ack=%u)\n", b->last_acked_seq);
+ pkt_free(p);
+ return;
+ }
+ prev = (struct pkt *) &b->queue.head;
+ last_seq = b->last_acked_seq;
+ while (p)
+ {
+ if (tcp_seq_lt(p->seq, last_seq))
+ {
+ if (tcp_seq_le(p->seq + pkt_len(p), last_seq))
+ {
+ DBG(" have\n");
+ pkt_free(p);
+ return;
+ }
+ pkt_pop(p, p->seq + pkt_len(p) - last_seq);
+ p->seq = last_seq;
+ DBG(" clip");
+ }
+ q = list_next(&b->queue, &prev->n);
+ if (q && tcp_seq_le(q->seq, p->seq))
+ {
+ /* next packet starts before us => skip it */
+ prev = q;
+ last_seq = q->seq + pkt_len(q);
+ }
+ else
+ {
+ new = NULL;
+ if (q && tcp_seq_lt(q->seq, p->seq + pkt_len(p)))
+ {
+ /* overlap with next packet => split */
+ DBG(" split");
+ uns keeplen = q->seq - p->seq;
+ uns newlen = pkt_len(p) - keeplen;
+ new = pkt_new(0, newlen);
+ memcpy(pkt_append(new, newlen), pkt_unappend(p, newlen), newlen);
+ new->seq = p->seq + keeplen;
+ }
+ DBG(" insert");
+ list_insert(&p->n, &prev->n);
+ prev = p;
+ last_seq = p->seq + pkt_len(p);
+ p = new;
+ }
+ }
+ DBG("\n");
+}
+
static void tcp_got_packet(struct iphdr *iph, struct pkt *p)
{
struct tcphdr *tcph;
a = &f->pipe[0];
b = &f->pipe[1];
list_init(&a->queue);
- a->last_acked_seq = seq;
+ a->syn_or_fin_seq = a->last_acked_seq = seq;
a->state = FLOW_SYN_SENT;
list_init(&b->queue);
b->state = FLOW_IDLE;
goto drop;
}
- DBG("\t%p(%d/%d) ", f, a->state, b->state);
- if (a->state == FLOW_CLOSED && b->state == FLOW_CLOSED)
+ DBG("\t%p %s (%s/%s) ", f, (a == &f->pipe[0] ? "A->B" : "B->A"), pipe_state_names[f->pipe[0].state], pipe_state_names[f->pipe[1].state]);
+ if (a->state == FLOW_FINISHED && b->state == FLOW_FINISHED)
{
DBG("closed\n");
pkt_account(&stat_tcp_on_closed, p);
{
DBG("RESET\n");
f->appl->close(f, CAUSE_RESET);
- a->state = b->state = FLOW_CLOSED;
+ a->state = b->state = FLOW_FINISHED;
flow_set_timeout(f, now + 300); /* FIXME */
goto drop;
}
goto inval;
if (tcph->ack)
{ /* SYN ACK */
- if (b->state == FLOW_SYN_SENT && b->last_acked_seq+1 == ack)
+ if (b->state == FLOW_SYN_SENT && b->syn_or_fin_seq+1 == ack)
{
DBG("SYN ACK\n");
- b->last_acked_seq = ack;
+ a->last_acked_seq = ack;
+ a->syn_or_fin_seq = seq;
a->state = FLOW_SYN_SENT_ACK;
- a->last_acked_seq = seq;
+ b->last_acked_seq = seq;
goto drop;
}
else if (b->state == FLOW_ESTABLISHED)
if (tcph->ack)
{
- if (b->state == FLOW_SYN_SENT_ACK && b->last_acked_seq+1 == ack)
- {
- a->state = b->state = FLOW_ESTABLISHED;
- b->last_acked_seq = ack+1;
- DBG("ACKED SYN, ");
- }
- if (b->state == FLOW_FIN_SENT && b->last_acked_seq+1 == ack)
+ if (tcp_seq_le(ack, a->last_acked_seq))
+ DBG("DUP ACK, ");
+ else
{
- b->state = FLOW_CLOSED;
- if (a->state == FLOW_CLOSED)
+ struct pkt *q;
+ a->last_acked_seq = ack;
+ while ((q = list_head(&a->queue)) && tcp_seq_le(q->seq+pkt_len(q), ack))
{
- DBG("CLOSED BOTH WAYS\n");
- f->appl->close(f, CAUSE_CLOSE);
- flow_set_timeout(f, now + 300); /* FIXME */
- goto drop;
+ list_remove(&q->n);
+ DBG("data(%Ld-%Ld), ", a->stat_in.bytes, a->stat_in.bytes+pkt_len(q)-1);
+ pkt_account(&a->stat_in, q);
+ f->appl->input(f, (a == &f->pipe[1]), q);
}
- else
- DBG("CLOSED ONE-WAY, ");
+ if (b->state == FLOW_SYN_SENT_ACK && b->syn_or_fin_seq+1 == ack)
+ {
+ a->state = b->state = FLOW_ESTABLISHED;
+ DBG("ACKED SYN, ");
+ }
+ else if (b->state == FLOW_FIN_SENT && b->syn_or_fin_seq+1 == ack)
+ {
+ b->state = FLOW_FINISHED;
+ if (a->state == FLOW_FINISHED)
+ {
+ DBG("CLOSED BOTH WAYS\n");
+ f->appl->close(f, CAUSE_CLOSE);
+ flow_set_timeout(f, now + 300); /* FIXME */
+ goto drop;
+ }
+ else
+ DBG("CLOSED ONE-WAY, ");
+ }
+ else if ((q = list_head(&a->queue)) && tcp_seq_lt(ack, q->seq))
+ {
+ DBG("DAMNED, ACK FOR UNCAUGHT DATA!\n");
+ goto invalid;
+ }
+ else if (b->state == FLOW_SYN_SENT_ACK || b->state == FLOW_SYN_SENT)
+ goto unex;
}
- else if (b->state == FLOW_ESTABLISHED || b->state == FLOW_FIN_SENT)
- DBG("ACK, ");
- else if (b->state == FLOW_CLOSED)
- ;
- else
- goto unex;
}
if (tcph->fin)
if (a->state == FLOW_ESTABLISHED)
{
a->state = FLOW_FIN_SENT;
- a->last_acked_seq = seq;
+ a->syn_or_fin_seq = seq;
DBG("FIN SENT, waiting for FIN ACK, ");
}
else if (a->state == FLOW_FIN_SENT)
goto drop;
}
- if (b->state == FLOW_ESTABLISHED || b->state == FLOW_FIN_SENT || b->state == FLOW_CLOSED)
+ if (b->state == FLOW_ESTABLISHED || b->state == FLOW_FIN_SENT || b->state == FLOW_FINISHED)
{
- DBG("DATA\n");
- // list_add_tail(&b->queue, &p->n); /* FIXME */
- if (pkt_len(p))
- f->appl->input(f, (a == &f->pipe[1]), p);
+ p->seq = seq;
+ tcp_enqueue_data(b, p);
return;
}
else