最近在自己PC上安装的虚拟机VMware里安装的CentOS7.0操作系统,在编译和使用libnids-1.24的时候,一直会出现奇怪的警告,最开始仅仅是编译的时候报警,库文件还是能正常生成,但是在使用用例的时候,调用库的时候还是会报错,也就是这问题不解决没法继续了;可服务器上CentOS6.4编译运行都无异常(可参考上篇),但是刚刚在疑惑中,仔细调试了一把,就更奇怪了,关于一个disable的选项貌似十分重要,大年初一,我也够拼的
编译libnids-1.24的时候会报出警告
libnids.c: In function ?.ids_run?. libnids.c:689:5: warning: ?._thread_create_full?.is deprecated (declared at /usr/include/glib-2.0/glib/deprecated/gthread.h:106): Use 'g_thread_new' instead [-Wdeprecated-declarations] START_CAP_QUEUE_PROCESS_THREAD(); /* threading... */ ^
有很多类似警告,大概可以看到是nids_run里,有个大写的关于Thread的函数调用导致,但是编译还是会正常生成库文件libnids.a;但是,在编译sample里的用例时
[lihui@localhost samples]$ make gcc -c -g -O2 -D_BSD_SOURCE -I. -I../src -I/usr/local/include -I/usr/local/include -D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD -DHAVE_NET_ETHERNET_H overflows.c gcc -o overflows overflows.o -L../src -lnids -L/usr/local/lib -lpcap -lnet -lgthread-2.0 -lnsl /usr/bin/ld: ../src/libnids.a(libnids.o): undefined reference to symbol 'g_thread_exit' /usr/bin/ld: note: 'g_thread_exit' is defined in DSO /lib64/libglib-2.0.so.0 so try adding it to the linker command line /lib64/libglib-2.0.so.0: could not read symbols: Invalid operation collect2: error: ld returned 1 exit status make: *** [overflows] Error 1
这里就比较重要了,因为动态库文件的问题,但是从错误信息来看,还是跟带thread相关的错误,因此大概可以确定,编译报的那个警告十分重要,由此,我查看了下nids_run这个函数的内容
683 int nids_run() 684 { 685 if (!desc) { 686 strcpy(nids_errbuf, "Libnids not initialized"); 687 return 0; 688 } 689 START_CAP_QUEUE_PROCESS_THREAD(); /* threading... */ 690 pcap_loop(desc, -1, (pcap_handler) nids_pcap_handler, 0); 691 /* FIXME: will this code ever be called? Don't think so - mcree */ 692 STOP_CAP_QUEUE_PROCESS_THREAD(); 693 nids_exit(); 694 return 0; 695 }
为此还对比了libnids-1.24和libnids-1.19对于此函数的差异,1.19的定义如下
522 void nids_run() 523 { 524 if (!desc) { 525 strcpy(nids_errbuf, "Libnids not initialized"); 526 return; 527 } 528 pcap_loop(desc, -1, (pcap_handler) pcap_hand, 0); 529 clear_stream_buffers(); 530 strcpy(nids_errbuf, "loop: "); 531 strncat(nids_errbuf, pcap_geterr(desc), sizeof(nids_errbuf) - 7); 532 pcap_close(desc); 533 }
可见新版本才新添加了第689行,的的确确也与thread相关,查看下相关定义
541 #ifdef HAVE_LIBGTHREAD_2_0 542 543 #define START_CAP_QUEUE_PROCESS_THREAD() \ 544 if(nids_params.multiproc) { /* threading... */ \ 545 if(!(g_thread_create_full((GThreadFunc)cap_queue_process_thread,NULL,0,FALSE,TRUE,G_THREAD_PRIORITY_LOW,&gerror))) { \ 546 strcpy(nids_errbuf, "thread: "); \ 547 strncat(nids_errbuf, gerror->message, sizeof(nids_errbuf) - 8); \ 548 return 0; \ 549 }; \ 550 } 551 552 #define STOP_CAP_QUEUE_PROCESS_THREAD() \ 553 if(nids_params.multiproc) { /* stop the capture process thread */ \ 554 g_async_queue_push(cap_queue,&EOF_item); \ 555 }
这两个宏定义取决于是否定义了宏HAVE_LIBGTHREAD_2_0,可以从configure里查看到
4350 #define HAVE_LIBGTHREAD_2_0 1
这么说在编译过程中,的确这个START_XXXXX是存在问题,而且仔细看看,才发现前面写的关于libnids的执行流程根本补完备,因为START_CAP_QUEUE_PROCESS_THREAD()函数和pcap_loop里的函数指针的调用最终都是调用的同一个函数call_ip_frag_procs,当然最终还是调用链表里各自处理函数
为此,特地在服务器CentOS6.4上编译运行无误的版本上通过gdb来调试了一把libnids,但是很奇怪的是程序经过这个大写字母函数,并没有创建新的线程,而且由于它是宏定义函数,不太好调试,无法定位,但是pcap_loop是可以进入的
于是仔细看了又看call_ip_frag_procs这个函数
192 /* called either directly from pcap_hand() or from cap_queue_process_thread() 193 * depending on the value of nids_params.multiproc - mcree 194 */ 195 static void call_ip_frag_procs(void *data,bpf_u_int32 caplen) 196 { 197 struct proc_node *i; 198 for (i = ip_frag_procs; i; i = i->next) 199 (i->item) (data, caplen); 200 }
作者有两行注释,果然,这个函数可以从两个地方调用,到底从pcap_loop还是那个大写字母的玩意,取决于nids_params.multiproc的结果
再次查看START_CAP_QUEUE_PROCESS_THREAD()函数的定义,的确是nids_params.multiproc为真才会执行;继续查看下pcap_loop的函数指针调用的函数,的确也有这个判断
331 #ifdef HAVE_LIBGTHREAD_2_0 332 if(nids_params.multiproc) { 333 /* 334 * Insert received fragment into the async capture queue. 335 * We hope that the overhead of memcpy 336 * will be saturated by the benefits of SMP - mcree 337 */ 338 qitem=malloc(sizeof(struct cap_queue_item)); 339 if (qitem && (qitem->data=malloc(hdr->caplen - nids_linkoffset))) { 340 qitem->caplen=hdr->caplen - nids_linkoffset; 341 memcpy(qitem->data,data_aligned,qitem->caplen); 342 g_async_queue_lock(cap_queue); 343 /* ensure queue does not overflow */ 344 if(g_async_queue_length_unlocked(cap_queue) > nids_params.queue_limit) { 345 /* queue limit reached: drop packet - should we notify user via syslog? */ 346 free(qitem->data); 347 free(qitem); 348 } else { 349 /* insert packet to queue */ 350 g_async_queue_push_unlocked(cap_queue,qitem); 351 } 352 g_async_queue_unlock(cap_queue); 353 } 354 } else { /* user requested simple passthru - no threading */ 355 call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 356 } 357 #else 358 call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 359 #endif
说实话看到这些大括号也if else,差不多也醉了,去掉中间一些匹配了的,不关心的
331 #ifdef HAVE_LIBGTHREAD_2_0 332 if(nids_params.multiproc) { .................省略中间...................... 354 } else { /* user requested simple passthru - no threading */ 355 call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 356 } 357 #else 358 call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 359 #endif
终于,可以看到这里nids_params.multiproc为假才会调用这个call函数;正好和START_CAP_QUEUE_PROCESS_THREAD()的调用错开了,也就说明了作者的那几行注释,either or,最终执行链表处理函数的调用只能两者其中一个调用的
下面是gdb的信息
Breakpoint 1, nids_run () at libnids.c:685 685 if (!desc) { Missing separate debuginfos, use: debuginfo-install glib2-2.28.8-4.el6.x86_64 glibc-2.12-1.149.el6_6.5.x86_64 (gdb) p nids_params $1 = {n_tcp_streams = 1040, n_hosts = 256, device = 0x7ffff7ff3a90 "eth0", filename = 0x0, sk_buff_size = 168, dev_addon = 16, syslog = 0x401d02 <nids_syslog>, syslog_level = 1, scan_num_hosts = 256, scan_delay = 3000, scan_num_ports = 10, no_mem = 0x406048 <nids_no_mem>, ip_filter = 0x401cf0 <nids_ip_filter>, pcap_filter = 0x0, promisc = 1, one_loop_less = 0, pcap_timeout = 1024, multiproc = 0, queue_limit = 20000, tcp_workarounds = 0, pcap_desc = 0x0} (gdb) p nids_params.multiproc $2 = 0 (gdb)
说实话,到了这里,虚拟机里关于这个错误的解决问题还没找到,更奇怪的是,服务器没这个错误,gdb调试也是通过pcap_loop里的handle里调用的处理函数,如此看来这个大写字母的START函数还没环境执行到,至于函数的含义,还没有深究,但是必须得感激虚拟机的这个错误,让我对执行流程某些细节有了更深的了解
新版本问题始终要必须解决,这里由于从错误信息里可以看到跟一个库有关的,而且这个函数貌似也执行不到,于是我在想能否编译前直接disable掉呢?在configure里搜索,果然,找到了相关的说明和方法
865 Optional Features: 866 --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) 867 --enable-FEATURE[=ARG] include FEATURE [ARG=yes] 868 --disable-libglib use glib2 for multiprocessing support 869 --disable-libnet whether to include code requiring libnet 870 --enable-shared enable building shared libraries
可以看到glib2的问题,可惜无论怎么安装都没有将虚拟机这个错误消除,既然目前看来用不上,就采用一种take around的方法,将它disable掉,而且libnet貌似也用不上,一并disable掉
[lihui@localhost libnids-1.24]$ ./configure --disable-libnet --disable-libglib [lihui@localhost libnids-1.24]$ make clean [lihui@localhost libnids-1.24]$ make [lihui@localhost libnids-1.24]$ sudo make install
这样编译安装都没有出现警告和错误,运行用例也正确无误