/* If an incoming SYN or SYNACK frame contains a payload and/or FIN, * queue this additional data / FIN. */ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); if (TCP_SKB_CB(skb)->end_seq == tp->rcv_nxt) return; skb = skb_clone(skb, GFP_ATOMIC); if (!skb) return; skb_dst_drop(skb); /* segs_in has been initialized to 1 in tcp_create_openreq_child(). * Hence, reset segs_in to 0 before calling tcp_segs_in() * to avoid double counting. Also, tcp_segs_in() expects * skb->len to include the tcp_hdrlen. Hence, it should * be called before __skb_pull(). */ tp->segs_in = 0; tcp_segs_in(tp, skb); __skb_pull(skb, tcp_hdrlen(skb)); sk_forced_mem_schedule(sk, skb->truesize); skb_set_owner_r(skb, sk); TCP_SKB_CB(skb)->seq++; TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_SYN; tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; __skb_queue_tail(&sk->sk_receive_queue, skb); tp->syn_data_acked = 1; /* u64_stats_update_begin(&tp->syncp) not needed here, * as we certainly are not changing upper 32bit value (0) */ tp->bytes_received = skb->len; if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) tcp_fin(sk); }
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; }