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;
}
