最近在自己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
这样编译安装都没有出现警告和错误,运行用例也正确无误
