// track a tcp stream. returns the difference between this packet's // SEQ and the expected next SEQ. int IpConnTrack_track_stream(IpConnTrack *self, IpConn *conn, IpConnTcpQueue *queue, struct netpkt *pkt, int buffer_stream) { netpkt_tcp *tcp; netpkt_udp *udp; tcp_seq_t seq; char *dst, *src; int len, diff=0; do { tcp = pkt->pkt_tcp; udp = pkt->pkt_udp; src = pkt->pkt_msg; len = pkt->pkt_len; diff = 0; if( tcp ) { if( tcp_ack(tcp) ) { queue->ack = ntohl(tcp->ack_seq); } queue->win = ntohs(tcp->window); seq = ntohl(tcp->seq); if( !queue->seq_ok ) { queue->seq_syn = seq; queue->seq = seq; queue->seq_ok = 1; if( tcp_syn(tcp) ) { queue->seq++; } else { conn->conn_pkt_flags |= CONN_PKT_TCP_MISSED_SYN; } } diff = tcp_seq_diff(seq, queue->seq); if( diff > 1 ) { // ignore packets (far) in the future conn->conn_pkt_flags |= CONN_PKT_TCP_FUTURE_SEQ; break; } src += -diff; len -= -diff; if( len <= 0 ) { // ignore past packets break; } queue->seq += len; } else if( udp ) { } if( buffer_stream ) { dst = (char*)array_add(&queue->buf, len); assertb(dst); memcpy(dst, src, len); } } while(0); return diff; }
void process_tcp(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { struct { unsigned src_port; unsigned dst_port; unsigned seqno; unsigned ackno; unsigned header_length; unsigned flags; unsigned window; unsigned checksum; unsigned urgent; } tcp; ferret->statistics.tcp++; if (length == 0) { FRAMERR(frame, "tcp: frame empty\n"); return; } if (length < 20) { FRAMERR(frame, "tcp: frame too short\n"); return; } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ tcp.src_port = ex16be(px+0); tcp.dst_port = ex16be(px+2); tcp.seqno = ex32be(px+4); tcp.ackno = ex32be(px+8); tcp.header_length = px[12]>>2; tcp.flags = px[13]; tcp.window = ex16be(px+14); tcp.checksum = ex16be(px+16); tcp.urgent = ex16be(px+18); frame->src_port = tcp.src_port; frame->dst_port = tcp.dst_port; if (tcp.header_length < 20) { /* Regress: defcon2008\dump027.pcap(39901) */ //FRAMERR(frame, "tcp: header too short, expected length=20, found length=%d\n", tcp.header_length); return; } if (tcp.header_length > length) { //FRAMERR(frame, "tcp: header too short, expected length=%d, found length=%d\n", tcp.header_length, length); return; } if ((tcp.flags & 0x20) && tcp.urgent > 0) { FRAMERR(frame, "tcp: found %d bytes of urgent data\n", tcp.urgent); return; } /* Check the checksum */ if (!validate_tcp_checksum(px, length, frame->src_ipv4, frame->dst_ipv4)) { /* Regress: defcon2008-msnmsgr.pcap(24066) */ ferret->statistics.errs_tcp_checksum++; return; } /*TODO: need to check checksum */ if (tcp.header_length > 20) { unsigned o = 20; unsigned max = tcp.header_length; while (o < tcp.header_length) { unsigned tag = px[o++]; unsigned len; if (tag == 0) break; if (tag == 1) continue; if (o >= max) { FRAMERR(frame, "tcp: options too long\n"); break; } len = px[o++]; if (len < 2) { FRAMERR(frame, "tcp: invalid length field\n"); break; } if (o+len-2 > max) { FRAMERR(frame, "tcp: options too long\n"); break; } switch (tag) { case 0x02: /* max seg size */ if (len != 4) FRAMERR(frame, "tcp: unknown length: option=%d, length=%d\n", tag, len); break; case 0x04: /* SACK permitted */ if (len != 2) FRAMERR(frame, "tcp: unknown length: option=%d, length=%d\n", tag, len); break; case 0x05: /* SACK */ break; case 0x08: /*timestamp*/ break; case 0x03: /*window scale*/ break; default: FRAMERR(frame, "tcp: unknown option=%d, length=%d\n", tag, len); } o += len-2; } } SAMPLE(ferret,"TCP", JOT_NUM("flags", tcp.flags)); /* Process an "acknowledgement". Among other things, this will identify * when packets have been missed: if the other side claims to have * received a packet, but we never saw it, then we know that it was * dropped somewhere on the network (probably because we are getting * a weak signal via wireless). */ if (tcp.flags & TCP_ACK) { tcp_ack_data(ferret, frame, tcp.ackno); } switch (tcp.flags & 0x3F) { case TCP_SYN: tcp_syn(ferret, frame); break; case TCP_SYN|TCP_ACK: tcp_synack(ferret, frame); break; case TCP_FIN: case TCP_FIN|TCP_ACK: case TCP_FIN|TCP_ACK|TCP_PSH: tcp_fin(ferret, frame); break; case TCP_ACK: case TCP_ACK|TCP_PSH: if (length > tcp.header_length) tcp_data(ferret, frame, px+tcp.header_length, length-tcp.header_length, tcp.seqno, tcp.ackno); break; case TCP_RST: case TCP_RST|TCP_ACK: break; case 0x40|TCP_ACK: break; case TCP_RST|TCP_ACK|TCP_FIN: case TCP_RST|TCP_ACK|TCP_PSH: break; default: FRAMERR(frame, "tcp: unexpected combo of flags: 0x%03x\n", tcp.flags); } }
int IpConnTrack_track_state(IpConnTrack *self, IpConn *conn, struct netpkt *pkt) { netpkt_tcp *tcp = pkt->pkt_tcp; IpConnStats *st=0; if( conn->conn_pkt_flags & CONN_PKT_FROM_CLIENT ) { st = &conn->conn_stats_client; if( conn->conn_flags & CONN_LOCAL_CLIENT ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_SRC; } if( conn->conn_flags & CONN_LOCAL_SERVER ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_DST; } } else if( conn->conn_pkt_flags & CONN_PKT_FROM_SERVER ) { st = &conn->conn_stats_server; if( conn->conn_flags & CONN_LOCAL_CLIENT ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_DST; } if( conn->conn_flags & CONN_LOCAL_SERVER ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_SRC; } } if( st ) { st->packets++; st->bytes += pkt->pkt_len; // track the next expected sequence numbers to mark duplicate // packets. I won't complain if retries are different. pkt->pkt_tcp_seq_diff = 0; if( tcp ) { u32 seq; seq = ntohl(tcp->seq); if( !st->tcp_seq_next_ok || seq == st->tcp_seq_next ) { if( tcp_syn(tcp) ) { st->tcp_seq_next = seq + 1; } else { st->tcp_seq_next = seq + pkt->pkt_len; } st->tcp_seq_next_ok = 1; } else { pkt->pkt_tcp_seq_diff = tcp_seq_diff(seq, st->tcp_seq_next); } } } conn->conn_time_prev = conn->conn_time_last; conn->conn_time_last = mstime(); if( tcp ) { if( tcp_fin(tcp) || tcp_rst(tcp) ) { conn->conn_state = CONN_STATE_FIN; } } return 0; }