📄 tcpstream.c
字号:
/* TODO: Always need to re-order any out of order * transmissions. Not sure what kind of mem policy * we are going to need here... */ dmesg(M_DEBUG, "tcpstream: Out of order: %u->%u %u->%u", tp->seq-rcv->isn, tp->seq_end-rcv->isn, tp->seq, rcv->rcv_nxt); }}/* Process incoming TCP segments here */static int tcpstream_tcpseg(struct packet *p, unsigned int i){ struct pkt_iphdr *iph=p->layer[i].h.ip; struct pkt_tcphdr *tcph=p->layer[i+1].h.tcp; struct tcp_session *s; struct tcp_stream *snd, *rcv; int to_server; int ret=0; struct tcpseg tp; /* Host order header values */ tp.ack=ntohl(tcph->ack); tp.seq=ntohl(tcph->seq); tp.win=ntohs(tcph->win); tp.len=ntohs(iph->tot_len) - (iph->ihl<<2) - (tcph->doff<<2); tp.seq_end=tp.seq+tp.len+tcph->flags.bits.syn+tcph->flags.bits.fin; tp.tsval=0; tp.saw_tstamp=0; /* Find the session this packet comes from */ if ( !(s=tcp_find(iph, tcph, &to_server)) ) { if ( (tcph->flags.bits.syn) && !(tcph->flags.bits.ack) && !(tcph->flags.bits.rst)) { /* SYN: part 1 of connection handshake */ p->layer[i+1].session=tcp_new(iph, tcph, p); p->layer[i+1].flags|=FLAG_TCP_STATE|FLAG_TCP_2SVR; return ret; } dmesg(M_DEBUG,"tcpstream: never heard of you"); return ret; }else p->layer[i+1].session=s; /* figure out who is sender and who is reciever */ if ( to_server ) { snd=&s->client; rcv=&s->server; p->layer[i+1].flags|=FLAG_TCP_2SVR; }else{ snd=&s->server; rcv=&s->client; p->layer[i+1].flags&=(~FLAG_TCP_2SVR); } /* LRU: Move to front of LRU and hash collision chain */ tcp_lru_mtf(&lru, s); tcp_hash_mtf(s); /* Deal with a SYN/ACK */ if ( rcv->state==TCP_SYN_SENT ) { /* fist check the ack field */ if ( tcph->flags.bits.ack ) { /* if SND.UNA =< SEG.ACK =< SND.NXT * then ACK is acceptable */ if ( !(between(tp.ack, rcv->snd_una, rcv->snd_nxt)) ) { return ret; } p->layer[i+1].flags|=FLAG_TCP_STATE|FLAG_TCP_SURE; } /* then check the rst flag */ if ( tcph->flags.bits.rst ) { return 1; } if ( tcph->flags.bits.syn ) { /* update the advertised window */ snd->rcv_wnd=tp.win; /* update sequencing information */ snd->snd_una=tp.seq+1; snd->snd_nxt=snd->snd_una+1; rcv->isn=tp.seq; rcv->rcv_nxt=tp.seq_end; rcv->rcv_wup=tp.seq_end; if ( tcph->flags.bits.ack ) rcv->snd_una=tp.ack; /* client now sees servers initial options */ tcp_syn_options(&s->client, tcph, p->time.tv_sec); /* Check whether to use window scaling */ if (!(rcv->flags&TF_WSCALE_OK && snd->flags&TF_WSCALE_OK)) { rcv->scale=0; snd->scale=0; } /* SYN|ACK: part 2 of connection handshake */ rcv->state=TCP_SYN_RECV; snd->state=TCP_SYN_SENT; transition(s, s->client.state, s->server.state); tcp_tmo_del(s); return ret; }else goto no_checks; } /* First check the sequence number */ if ( !tcp_sequence(rcv, tp.seq, tp.seq_end) ) { /* XXX: Don't alert here retransmits hit this */ return ret; }no_checks: /* rfc1323: H1. Apply PAWS checks first */ if ( rcv->flags&TF_TSTAMP_OK && (tp.saw_tstamp=tcp_fast_options(tcph, &tp.tsval)) ) { /* TODO: Fix PAWS check, its buggy as fuck */ if ( (int32_t)(rcv->ts_recent - tp.tsval) > TCP_PAWS_WINDOW && p->time.tv_sec<rcv->ts_recent_stamp + TCP_PAWS_24DAYS ) { alert(&tcpstream_gen, p, &alert_tcp3); return ret; } } /* Second, check the RST bit */ if ( tcph->flags.bits.rst ) { p->layer[i+1].flags|=FLAG_TCP_STATE|FLAG_TCP_SURE; dmesg(M_DEBUG,"TCP stream was reset"); return 1; } /* rfc1323: PAWS: update ts_recent */ if ( tp.saw_tstamp && !after(tp.seq,rcv->rcv_wup) ) { if((int32_t)(tp.tsval - rcv->ts_recent) >= 0 || p->time.tv_sec >= rcv->ts_recent_stamp + TCP_PAWS_24DAYS) { rcv->ts_recent=tp.tsval; rcv->ts_recent_stamp=p->time.tv_sec; } } /* Third, check security and precendece (pfft) */ /* Fourth, check the SYN bit */ if ( tcph->flags.bits.syn && !before(tp.seq,rcv->rcv_nxt) ) { alert(&tcpstream_gen, p, &alert_tcp1); return 1; } /* we now know that this packet is in-state */ p->layer[i+1].flags|=FLAG_TCP_STATE|FLAG_TCP_SURE; /* Scale the window */ tp.win<<=snd->scale; /* Fifth, check the ack field */ if ( !tcph->flags.bits.ack ) goto no_ack; switch(rcv->state) { case TCP_SYN_SENT: /* ACK: part 3 of connection handshake */ if ( rcv->snd_una <= tp.ack && tp.ack <= rcv->snd_nxt ) { p->layer[i+1].flags|=FLAG_TCP_CT_EST; tcp_established(snd,rcv,tp.seq,tp.ack,tp.win); transition(s, TCP_ESTABLISHED, TCP_ESTABLISHED); } break; case TCP_ESTABLISHED: tcp_established(snd,rcv,tp.seq,tp.ack,tp.win); rcv->rcv_nxt=tp.seq; break; case TCP_FIN_WAIT1: tcp_established(snd,rcv,tp.seq,tp.ack,tp.win); rcv->state=TCP_FIN_WAIT2; snd->state=TCP_LAST_ACK; transition(s, s->client.state, s->server.state); case TCP_FIN_WAIT2: break; case TCP_CLOSE_WAIT: tcp_established(snd,rcv,tp.seq,tp.ack,tp.win); break; case TCP_CLOSING: tcp_established(snd,rcv,tp.seq,tp.ack,tp.win); rcv->state=TCP_TIME_WAIT; transition(s, s->client.state, s->server.state); break; case TCP_LAST_ACK: rcv->state=0; transition(s, s->client.state, s->server.state); break; default: break; }no_ack: /* sixth check URG bit */ if ( !tcph->flags.bits.urg ) goto no_urg; /* XXX: What does processing urgent/OOB data * actually buy us in terms of intrusion detection * capability? Need to look in to this... */no_urg: /* seventh process the segment text */ /* We might not actually need to bother... */ if ( !tcp_reassemble ) goto no_data; if ( !tp.len || !s->proto ) goto no_data; /* There is data in the segment */ switch(rcv->state) { case TCP_CLOSE_WAIT: case TCP_CLOSING: case TCP_LAST_ACK: case TCP_TIME_WAIT: alert(&tcpstream_gen, p, &alert_tcp2); goto no_data; } tcpstream_data(s, rcv, tcph, &tp);no_data: /* eighth, check the FIN bit */ if ( !tcph->flags.bits.fin ) goto no_fin; /* XXX: Fin implies PSH */ rcv->rcv_nxt++; switch(rcv->state) { case TCP_SYN_RECV: case TCP_ESTABLISHED: rcv->state=TCP_CLOSE_WAIT; snd->state=TCP_FIN_WAIT1; transition(s, s->client.state, s->server.state); break; case TCP_FIN_WAIT1: /* if fin has been acked do time_wait else closing */ rcv->state=TCP_TIME_WAIT; transition(s, s->client.state, s->server.state); break; case TCP_FIN_WAIT2: rcv->state=TCP_TIME_WAIT; transition(s, s->client.state, s->server.state); break; case TCP_CLOSE_WAIT: case TCP_CLOSING: case TCP_LAST_ACK: case TCP_TIME_WAIT: /* Remain in the same state */ break; default: break; }no_fin: /* XXX: need to implement time_wait timer */ if ( (snd->state==TCP_TIME_WAIT && rcv->state==0) || (rcv->state==TCP_TIME_WAIT && snd->state==0) ) { ret=1; }else if ( rcv->state==0 && snd->state==TCP_SYN_SENT ) { /* XXX: we reset the timeout on retransmissions */ if ( (tcph->flags.bits.syn) && !(tcph->flags.bits.ack) && !(tcph->flags.bits.rst)) { tcp_tmo_del(s); s->expire=tcp_jiffies(p)+TCP_TMO_SYN1; tcp_tmo_add(&syn1, s); } } return ret;}/* Process an ICMP error message during connection */void icmperr_process(struct packet *p, unsigned int l){ struct pkt_iphdr *outer_iph; struct pkt_icmphdr *icmph; struct pkt_iphdr *iph; struct pkt_tcphdr *tcph; struct tcp_session *s; struct tcp_stream *snd,*rcv; int to_server; /* Get all the headers we need */ if ( l+2 >= p->llen ) return; outer_iph=p->layer[l-1].h.ip; icmph=p->layer[l].h.icmp; iph=p->layer[l+1].h.ip; tcph=p->layer[l+2].h.tcp; /* Make sure the packet is cool */ if ( iph->protocol!=6 ) return; if ( icmph->type != ICMP_DEST_UNREACH ) return; if ( icmph->code == ICMP_FRAG_NEEDED ) return; if ( icmph->code > NR_ICMP_UNREACH ) return; /* Check the IP packet came from the endpoint * ( if necessary ) */ if ( icmph->code==ICMP_PROT_UNREACH || icmph->code==ICMP_PORT_UNREACH ) { if ( iph->saddr!=outer_iph->daddr ) { alert(&icmperr_gen, p, &alert_icmp1); return; } } /* Lookup our session */ if ( !(s=tcp_find(iph, tcph, &to_server)) ) { /* XXX: Alert here? Seems to happen a lot... */ return; } /* Find out which side sent the offending packet */ if ( iph->daddr==s->c_addr ) { snd=&s->server; rcv=&s->client; }else{ snd=&s->client; rcv=&s->server; } /* Only reset opening connections */ if ( snd->state!=TCP_SYN_SENT && snd->state!=TCP_SYN_RECV ) { alert(&icmperr_gen, p, &alert_icmp2); return; } /* TODO: Check seq/ack numbers */ dmesg(M_DEBUG,"TCP reset by ICMP from %.8x", outer_iph->saddr); tcp_free(s);}/* Do some basic sanity checks to decide whether to even * bother processing a given segment or not. */void tcpstream_process(struct packet *pkt, unsigned int i){ struct pkt_iphdr *iph=pkt->layer[i].h.ip; struct layer *l=&pkt->layer[i+1]; struct tcp_session *s; int ret=0; l->flags|=FLAG_TCP_TRACK; /* Ignore fragments */ if ( iph->frag_off & ipfmask ) goto nodecode; /* Ignore bad IP checksums - ooh naughty! */ if ( !(pkt->layer[i].flags&FLAG_IP_CSUM) ) goto nodecode; /* Ignore bad TCP checksums - bad boy! */ if ( !(l->flags&FLAG_TCP_CSUM) ) goto nodecode; /* Ignore broadcast and multicast packets */ if ( (pkt->flags & (FP_MULTICAST|FP_BROADCAST)) ) { tcp_broadcast++; goto nodecode; } /* Ignore packets with TTLs lower than minttl */ if ( iph->ttl < tcp_minttl ) { tcp_lowttl++; goto nodecode; } /* Check we aren't in a icmp error message */ if ( i>0 && pkt->layer[i-1].proto==&icmp_p ) goto nodecode; /* Actually do the state tracking */ ret=tcpstream_tcpseg(pkt, i); /* Statistics counters */ if ( !(l->flags & FLAG_TCP_STATE) ) tcp_state_errs++; tcp_packets++; /* Have we actually been decoded? */ if ( !(s=l->session) || !s->proto ) goto nodecode; /* Dispatch to protocol handler */ pkt->layer[pkt->llen].flags=0; pkt->layer[pkt->llen].session=NULL; pkt->layer[pkt->llen].proto=s->proto; if ( s->proto->sdecode(pkt)<0 && !ret ) { /* TODO: queue this packet for reassembly */ mesg(M_DEBUG, "queue"); } goto done;nodecode: if ( pkt->layer[pkt->llen].h.raw<pkt->end ) pkt->layer[pkt->llen++].proto=NULL; dispatch(pkt);done: if (ret) tcp_free(l->session); tcp_tmo_check(pkt); return;}/* Called before firestorm shuts down */void tcpstream_free(void){ mesg(M_INFO,"tcpstream: max_concurrent=%u num_active=%u", max_concurrent, num_active); if ( tcp_reassemble ) { mesg(M_INFO,"tcpstream: max_flows=%u num_flows=%u", max_flows, num_flows); } mesg(M_INFO,"tcpstream: %u state errors out of %u packets", tcp_state_errs, tcp_packets); mesg(M_INFO,"tcpstream: %u broadcasts, %u ttl evasions, %u timeouts", tcp_broadcast, tcp_lowttl, tcp_timeouts); if ( tcp_hash ) free(tcp_hash); if ( all_sessions ) free(all_sessions);}/* Initialise flow cache */int tcpstream_flow_init(void){ void *p,*n=NULL; int i; /* Pointless if not reassembling, or have * no application layer protocols with flow * capabilities... */ if ( !tcp_reassemble || !flow_len ) return 0; /* not likely */ if ( flow_len < sizeof(void *) ) { flow_len=sizeof(void *); } /* flows are 1 to 1 with streams, even in worst case */ if ( tcp_numflows > tcp_numstreams ) { tcp_numflows=tcp_numstreams; mesg(M_WARN, "tcpstream: num_flows>num_streams, duh"); } if ( tcp_numflows < 256 ) { mesg(M_WARN, "tcpstream: you may be at risk with " "such a low num_flows value"); } /* Allocate it */ if ( !(flow_cache=calloc(tcp_numflows, flow_len)) ) { return -1; } /* Initialise it */ for(i=0; i<(tcp_numflows-1); ) { p=(char *)flow_cache + (i*flow_len); n=(char *)flow_cache + (++i*flow_len); *(void **)p = n; } *(void **)n=NULL; flow_next=flow_cache; return 0;}/* Initialise based on user parameters */int tcpstream_init(char *args){ int i; if ( tcp_stateful ) { mesg(M_ERR,"tcpstream: Can't add tcpstream twice!"); return 0; } if ( args && args_parse(tcpstream_args, args, NULL)<=0 ) { mesg(M_ERR,"tcpstream: parse error: %s", args); return 0; } if ( tcp_minttl > 255 ) { mesg(M_ERR,"tcpstream: minttl must be < 256"); return 0; } if ( tcp_numstreams < 32 ) { mesg(M_WARN,"tcpstream: num_streams is stupidly " "low, setting to 512"); tcp_numstreams=512; }else if ( tcp_numstreams < 512 ) { mesg(M_WARN,"tcpstream: num_streams is VERY low! " "are you sure?!"); } /* Select a size for the hash table such that * num_streams/hash_size is no larger than 128 * and such that the minimum size is 512 */ for(tcp_hashsz=512; tcp_numstreams/tcp_hashsz>128; tcp_hashsz<<=1); /* Make sure its always odd */ tcp_hashsz|=1; /* Initialise data structures */ if ( !(all_sessions=calloc(tcp_numstreams, sizeof(struct tcp_session))) ) { goto oom; } if ( !(tcp_hash=calloc(tcp_hashsz, sizeof(*tcp_hash))) ) { free(all_sessions); goto oom; } /* Initialise the slab-style allocator */ for(i=0; i<(tcp_numstreams-1); i++) { all_sessions[i].next=&all_sessions[i+1]; } all_sessions[i].next=NULL; tcp_next=all_sessions; /* Initialise state info */ if ( tcp_reassemble ) { struct proto_child *pc; for(pc=tcp_p.children; pc; pc=pc->next) { if ( pc->proto->state_len > flow_len ) flow_len = (size_t)pc->proto->state_len; } } /* Initialise flow cache */ if ( tcpstream_flow_init() ) goto oom; /* Print out some statistics */ mesg(M_INFO,"tcpstream: %u streams in %u buckets (%u KB)", tcp_numstreams, tcp_hashsz, (tcp_numstreams*sizeof(struct tcp_session))/1024); mesg(M_INFO,"tcpstream: TCP stream reassembly is %s: %u flows", tcp_reassemble ? "ENABLED" : "DISABLED", tcp_reassemble ? tcp_numflows : 0); tcp_stateful=1; return 1;oom: mesg(M_ERR,"tcpstream: calloc(): %s", get_err()); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -