nids_run调用了pcap_loop,无止境地循环抓包,抓到的包传给回调函数nids_pcap_handle来处理,而回调函数最终都会调用gen_ip_proc来根据不同类型协议的包进行处理重组,process_tcp,process_udp等等
首先是IP和TCP的结构体,可能不同的linux操作系统定义不同,下面头文件CentOS6.4里的定义
IP结构体:/usr/include/netinet/ip.h
107 struct ip 108 { 109 #if __BYTE_ORDER == __LITTLE_ENDIAN 110 unsigned int ip_hl:4; /* header length */ 111 unsigned int ip_v:4; /* version */ 112 #endif 113 #if __BYTE_ORDER == __BIG_ENDIAN 114 unsigned int ip_v:4; /* version */ 115 unsigned int ip_hl:4; /* header length */ 116 #endif 117 u_int8_t ip_tos; /* type of service */ 118 u_short ip_len; /* total length */ 119 u_short ip_id; /* identification */ 120 u_short ip_off; /* fragment offset field */ 121 #define IP_RF 0x8000 /* reserved fragment flag */ 122 #define IP_DF 0x4000 /* dont fragment flag */ 123 #define IP_MF 0x2000 /* more fragments flag */ 124 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ 125 u_int8_t ip_ttl; /* time to live */ 126 u_int8_t ip_p; /* protocol */ 127 u_short ip_sum; /* checksum */ 128 struct in_addr ip_src, ip_dst; /* source and dest address */ 129 };
TCP结构体:/usr/include/netinet/tcp.h
struct tcphdr { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ # if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t th_x2:4; /* (unused) */ u_int8_t th_off:4; /* data offset */ # endif # if __BYTE_ORDER == __BIG_ENDIAN u_int8_t th_off:4; /* data offset */ u_int8_t th_x2:4; /* (unused) */ # endif u_int8_t th_flags; # define TH_FIN 0x01 # define TH_SYN 0x02 # define TH_RST 0x04 # define TH_PUSH 0x08 # define TH_ACK 0x10 # define TH_URG 0x20 u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ };
下面就是process_tcp,在libnids里tcp.c里面:
void process_tcp(u_char * data, int skblen) //数据和长度 { struct ip *this_iphdr = (struct ip *)data; struct tcphdr *this_tcphdr = (struct tcphdr *)(data + 4 * this_iphdr->ip_hl); //通过IP部分偏移,指到了TCP头部的位置 int datalen, iplen; //TCP数据部分的长度,IP包的长度 int from_client = 1; unsigned int tmp_ts; struct tcp_stream *a_tcp; //TCP流全部信息 struct half_stream *snd, *rcv; //每个方向的TCP流 ugly_iphdr = this_iphdr; iplen = ntohs(this_iphdr->ip_len); if ((unsigned)iplen < 4 * this_iphdr->ip_hl + sizeof(struct tcphdr)) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } // ktos sie bawi datalen = iplen - 4 * this_iphdr->ip_hl - 4 * this_tcphdr->th_off; //TCP数据部分的长度:IP包长度减去IP头部长度减去TCP头部长度 if (datalen < 0) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } // ktos sie bawi if ((this_iphdr->ip_src.s_addr | this_iphdr->ip_dst.s_addr) == 0) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } if (!(this_tcphdr->th_flags & TH_ACK)) //检测TCP中的ACk信息,是否是攻击 detect_scan(this_iphdr); //下面都是检查数据包是否是没用的包 if (!nids_params.n_tcp_streams) return; if (my_tcp_check(this_tcphdr, iplen - 4 * this_iphdr->ip_hl, this_iphdr->ip_src.s_addr, this_iphdr->ip_dst.s_addr)) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } #if 0 check_flags(this_iphdr, this_tcphdr); //ECN #endif //经过了上面的一些检查,认为TCP数据包正常,首先对数据包的状态进行判断 //是否是崭新的TCP连接,是否是三次握手的第一个包 if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) { if ((this_tcphdr->th_flags & TH_SYN) && !(this_tcphdr->th_flags & TH_ACK) && !(this_tcphdr->th_flags & TH_RST)) add_new_tcp(this_tcphdr, this_iphdr); //结点加入到链表中 //如果TCP流不存在,而且是第一个SYN包,非ACK非RST,添加新的TCP流 return; }//第一个握手包结束 //判定TCP流的方向,发送端和接收端,而find stream里已经指定了方向 if (from_client) { snd = &a_tcp->client; rcv = &a_tcp->server; } else { rcv = &a_tcp->client; snd = &a_tcp->server; } //此时经过了上面的if find_stream,可见已经找到了存在的TCP流 if ((this_tcphdr->th_flags & TH_SYN)) { //又来一个SYN包,这里只能来自服务器,因为走到了这里,肯定不是建立 //TCP连接的(前面已经处理),肯定来自服务器带ACK的,检查状态ing if (from_client || a_tcp->client.state != TCP_SYN_SENT || a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK)) return; //SEQ和ACK对不上的包,当作是错误的包 if (a_tcp->client.seq != ntohl(this_tcphdr->th_ack)) return; //第二个握手包,服务器端赋值 a_tcp->server.state = TCP_SYN_RECV; a_tcp->server.seq = ntohl(this_tcphdr->th_seq) + 1; a_tcp->server.first_data_seq = a_tcp->server.seq; a_tcp->server.ack_seq = ntohl(this_tcphdr->th_ack); a_tcp->server.window = ntohs(this_tcphdr->th_win); //TCP选项的赋值,初始化client和server的timestamp if (a_tcp->client.ts_on) { a_tcp->server.ts_on = get_ts(this_tcphdr, &a_tcp->server.curr_ts); if (!a_tcp->server.ts_on) a_tcp->client.ts_on = 0; } else a_tcp->server.ts_on = 0; //初始化窗口大小 if (a_tcp->client.wscale_on) { a_tcp->server.wscale_on = get_wscale(this_tcphdr, &a_tcp->server.wscale); if (!a_tcp->server.wscale_on) { a_tcp->client.wscale_on = 0; a_tcp->client.wscale = 1; a_tcp->server.wscale = 1; } } else { a_tcp->server.wscale_on = 0; a_tcp->server.wscale = 1; } return; //第二个握手包结束,返回 } //假如有存在SEQ和ACK不等,并且SEQ还在窗口之外或者已经确认过的序号 if ( ! ( !datalen && ntohl(this_tcphdr->th_seq) == rcv->ack_seq ) && ( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) || before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq) ) ) return; //发送th_rst,重新建立一个连接 //如果是rst包,直接关闭连接,将数据还给注册的回调方,销毁连接 if ((this_tcphdr->th_flags & TH_RST)) { if (a_tcp->nids_state == NIDS_DATA) { //如果是TCP数据 struct lurker_node *i; a_tcp->nids_state = NIDS_RESET; //又到了回调那群处理函数的链表地方了 for (i = a_tcp->listeners; i; i = i->next) (i->item) (a_tcp, &i->data); } nids_free_tcp_stream(a_tcp); return; } /* PAWS check */ //这里是检查重复包,检查timestamp if (rcv->ts_on && get_ts(this_tcphdr, &tmp_ts) && before(tmp_ts, snd->curr_ts)) return; //来一个ACK包 if ((this_tcphdr->th_flags & TH_ACK)) { //从client来的包,而且两边的接收和发送状态统一 if (from_client && a_tcp->client.state == TCP_SYN_SENT && a_tcp->server.state == TCP_SYN_RECV) { //如果序号也能对得上,那么这是第三个握手包,连接建立成功 if (ntohl(this_tcphdr->th_ack) == a_tcp->server.seq) { a_tcp->client.state = TCP_ESTABLISHED; //更新客户端状态 a_tcp->client.ack_seq = ntohl(this_tcphdr->th_ack); //更新序号 { struct proc_node *i; struct lurker_node *j; void *data; a_tcp->server.state = TCP_ESTABLISHED; a_tcp->nids_state = NIDS_JUST_EST; //全双工传输,client和server连接已经建立起来了 //这样TCP三次握手建立连接 for (i = tcp_procs; i; i = i->next) { //根据调用者设定来判断哪些数据需要回调返回 char whatto = 0; char cc = a_tcp->client.collect; char sc = a_tcp->server.collect; char ccu = a_tcp->client.collect_urg; char scu = a_tcp->server.collect_urg; //进入回调函数来处理 (i->item) (a_tcp, &data); if (cc < a_tcp->client.collect) whatto |= COLLECT_cc; if (ccu < a_tcp->client.collect_urg) whatto |= COLLECT_ccu; if (sc < a_tcp->server.collect) whatto |= COLLECT_sc; if (scu < a_tcp->server.collect_urg) whatto |= COLLECT_scu; if (nids_params.one_loop_less) { if (a_tcp->client.collect >=2) { a_tcp->client.collect=cc; whatto&=~COLLECT_cc; } if (a_tcp->server.collect >=2 ) { a_tcp->server.collect=sc; whatto&=~COLLECT_sc; } } //加入监听队列,开始数据接收 if (whatto) { j = mknew(struct lurker_node); j->item = i->item; j->data = data; j->whatto = whatto; j->next = a_tcp->listeners; a_tcp->listeners = j; } } if (!a_tcp->listeners) { //没有监听者 nids_free_tcp_stream(a_tcp); return; } a_tcp->nids_state = NIDS_DATA; } } // return; } } //下面是四次结束过程 //数据传输过程中不停更新client和server的seq和ack //直到接收到了FIN包,数据传输结束 if ((this_tcphdr->th_flags & TH_ACK)) { //调用handle_ack更新ack序号 handle_ack(snd, ntohl(this_tcphdr->th_ack)); if (rcv->state == FIN_SENT) //来了FIN包,更新状态 rcv->state = FIN_CONFIRMED; if (rcv->state == FIN_CONFIRMED && snd->state == FIN_CONFIRMED) { //回调告知连接关闭,释放连接 struct lurker_node *i; a_tcp->nids_state = NIDS_CLOSE; for (i = a_tcp->listeners; i; i = i->next) (i->item) (a_tcp, &i->data); nids_free_tcp_stream(a_tcp); return; } } //数据处理部分 if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0) //将数据更新到接收方缓冲区 tcp_queue(a_tcp, this_tcphdr, snd, rcv, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off, datalen, skblen); //更新窗口大小 snd->window = ntohs(this_tcphdr->th_win); //如果缓存溢出,除了故障,释放掉连接 if (rcv->rmem_alloc > 65535) prune_queue(rcv, this_tcphdr); if (!a_tcp->listeners) nids_free_tcp_stream(a_tcp); }
在此过程中,还有两个关键的调用查找TCP的find_stream和添加新的TCP的add_new_tcp两个函数
新版本的find_stream里,哈希操作放到了一个新的函数nids_find_tcp_stream中进行
635 struct tcp_stream * 636 find_stream(struct tcphdr * this_tcphdr, struct ip * this_iphdr, 637 int *from_client) 638 { 639 struct tuple4 this_addr, reversed; 640 struct tcp_stream *a_tcp; 641 642 this_addr.source = ntohs(this_tcphdr->th_sport); 643 this_addr.dest = ntohs(this_tcphdr->th_dport); 644 this_addr.saddr = this_iphdr->ip_src.s_addr; 645 this_addr.daddr = this_iphdr->ip_dst.s_addr; 646 a_tcp = nids_find_tcp_stream(&this_addr); 647 if (a_tcp) {//如果找到,设为1,否则一直找到最后 //看是否为同一个TCP流主要看其地址是否一样 648 *from_client = 1; 649 return a_tcp; 650 } 651 reversed.source = ntohs(this_tcphdr->th_dport); 652 reversed.dest = ntohs(this_tcphdr->th_sport); 653 reversed.saddr = this_iphdr->ip_dst.s_addr; 654 reversed.daddr = this_iphdr->ip_src.s_addr; 655 a_tcp = nids_find_tcp_stream(&reversed); 656 if (a_tcp) { 657 *from_client = 0; 658 return a_tcp; 659 } 660 return 0; 661 } 663 struct tcp_stream * 664 nids_find_tcp_stream(struct tuple4 *addr) 665 { 666 int hash_index; 667 struct tcp_stream *a_tcp; 668 669 hash_index = mk_hash_index(*addr); //找到传入数据的哈希地址 670 for (a_tcp = tcp_stream_table[hash_index]; 671 a_tcp && memcmp(&a_tcp->addr, addr, sizeof (struct tuple4)); 672 a_tcp = a_tcp->next_node); //如果此地址开始就是空,表明没有与这个包对应的TCP流;如果地址不为空,则 //看地址值是否一样 673 return a_tcp ? a_tcp : 0; 674 }
add_new_tcp还是没有变化
static void add_new_tcp(struct tcphdr * this_tcphdr, struct ip * this_iphdr) { struct tcp_stream *tolink; struct tcp_stream *a_tcp; int hash_index; struct tuple4 addr; addr.source = ntohs(this_tcphdr->th_sport); addr.dest = ntohs(this_tcphdr->th_dport); addr.saddr = this_iphdr->ip_src.s_addr; addr.daddr = this_iphdr->ip_dst.s_addr; hash_index = mk_hash_index(addr); //计算新的TCP流哈希地址 if (tcp_num > max_stream) { //TCP流大小超过了阀值,线释放空间 struct lurker_node *i; int orig_client_state=tcp_oldest->client.state; tcp_oldest->nids_state = NIDS_TIMED_OUT; for (i = tcp_oldest->listeners; i; i = i->next) (i->item) (tcp_oldest, &i->data); nids_free_tcp_stream(tcp_oldest); if (orig_client_state!=TCP_SYN_SENT) nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr); } a_tcp = free_streams; if (!a_tcp) { fprintf(stderr, "gdb me ...\n"); pause(); } free_streams = a_tcp->next_free; tcp_num++; //TCP流总数 tolink = tcp_stream_table[hash_index]; memset(a_tcp, 0, sizeof(struct tcp_stream)); a_tcp->hash_index = hash_index; a_tcp->addr = addr; a_tcp->client.state = TCP_SYN_SENT; a_tcp->client.seq = ntohl(this_tcphdr->th_seq) + 1; a_tcp->client.first_data_seq = a_tcp->client.seq; a_tcp->client.window = ntohs(this_tcphdr->th_win); a_tcp->client.ts_on = get_ts(this_tcphdr, &a_tcp->client.curr_ts); a_tcp->client.wscale_on = get_wscale(this_tcphdr, &a_tcp->client.wscale); a_tcp->server.state = TCP_CLOSE; a_tcp->next_node = tolink; a_tcp->prev_node = 0; if (tolink) tolink->prev_node = a_tcp; tcp_stream_table[hash_index] = a_tcp; a_tcp->next_time = tcp_latest; a_tcp->prev_time = 0; if (!tcp_oldest) tcp_oldest = a_tcp; if (tcp_latest) tcp_latest->prev_time = a_tcp; tcp_latest = a_tcp; }