libnids学习笔记

学习资料

[1]下载包内的doc/API.html,version:1.24

[2]http://blog.csdn.net/ningxiaowei2013/article/details/53035976

简介

​ libnids(Network Intrusion Detection System Library),网络入侵监测系统函数库。具有重组TCP数据段、处理IP分片包和监测TCP端 口扫描的功能。

​ 所有的结构体和函数都在在“nids.h”,使用libnids的函数必须包含这个头文件和libnids.a的链接。

​ 一个main函数的伪代码:

int main()
{
    application private processing, not related to libnids
    optional modification of libnids parameters
    if (!nids_init() ) something's wrong, terminate;
    registration of callback functions
    nids_run();//or nids_next();
    // not reached in normal situation
}

IP重组

为了使libnids能接收所有的IP数据包(包括分片包、畸形包等),程序员需要定义如下的回调函数:

void ip_frag_func(struct ip * a_packet, int len)

在调用nids_init()函数初始化后,使用nids的函数进行注册:

nids_register_ip_frag(ip_frag_func);

这样回调函数ip_frag_func会在适当的时候由libnids调用,参数a_packet指针将指向接收到的数据报,len是数据报长度。

类似地,如果仅接收目标主机会接受的数据包(如非碎片包、重组包或头部校验正确的数据包等),需要定义如下回调函数:

void ip_func(struct ip * a_packet, int len)

然后注册:

nids_register_ip(ip_func);

TCP数据流重组

要接收TCP流在交换的数据,必须定义如下回调函数:

void tcp_callback(struct tcp_stream * ns, void ** param)

结构体tcp_stream提供了一个TCP连接的所有信息。例如,它包含了客户端与服务器端的half_stream (named client and server)结构。下文会对该结构的字段进行解释。

tcp_stream结构有一个名为nids_state的字段。此字段的数值将决定tcp_callback的操作。

  1. ns->nids_state==NIDS_JUST_EST

    ns是一个刚刚建立的连接。tcp_callback可以据此决定是否对该连接的后续数据进行检查。所有的连接参数(ip,端口号等)都可获得。如需要检查,tcp_callback回调函数将通知libnids它希望接收哪些数据(如到客户端的数据、到服务器端的数据、到客户端的紧急数据或到服务器端的紧急数据等),然后返回。

  2. ns->nids_state==NIDS_DATA

    此时,新数据已经到达。结构体half_stream(tcp_stream的元素)包含数据buffer。

  3. 下面的nids_state状态:

    • NIDS_CLOSE
    • NIDS_RESET
    • NIDS_TIMEOUT

    意味着连接已经关闭。若有资源占用的话,tcp_callback需要释放资源。

  4. ns->nids_state==NIDS_EXITING

    此时,libnids已经退出。这是程序使用保存在half_stream buffer中数据的最后一次机会。当在抓包文件中读取而不是从网络流读取时,libnids可能永远不会close, reset, or timeout。如果程序有未处理的数据(例如,调用了nids_discard()),这个状态下允许程序去处理这些数据。

示例程序

/*http://libnids.sourceforge.net/printall.c*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "nids.h"

#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))

// struct tuple4 contains addresses and port numbers of the TCP connections
// the following auxiliary function produces a string looking like
// 10.0.0.1,1024,10.0.0.2,23
char * adres (struct tuple4 addr)
{
  static char buf[256];
  strcpy (buf, int_ntoa (addr.saddr));
  sprintf (buf + strlen (buf), ",%i,", addr.source);
  strcat (buf, int_ntoa (addr.daddr));
  sprintf (buf + strlen (buf), ",%i", addr.dest);
  return buf;
}

void tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
  char buf[1024];
  strcpy (buf, adres (a_tcp->addr)); // we put conn params into buf
  if (a_tcp->nids_state == NIDS_JUST_EST)
    {
    // connection described by a_tcp is established
    // here we decide, if we wish to follow this stream
    // sample condition: if (a_tcp->addr.dest!=23) return;
    // in this simple app we follow each stream, so..
      a_tcp->client.collect++; // we want data received by a client
      a_tcp->server.collect++; // and by a server, too
      a_tcp->server.collect_urg++; // we want urgent data received by a
                                   // server
#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT
      a_tcp->client.collect_urg++; // if we don't increase this value,
                                   // we won't be notified of urgent data
                                   // arrival
#endif
      fprintf (stderr, "%s established\n", buf);
      return;
    }
  if (a_tcp->nids_state == NIDS_CLOSE)
    {
      // connection has been closed normally
      fprintf (stderr, "%s closing\n", buf);
      return;
    }
  if (a_tcp->nids_state == NIDS_RESET)
    {
      // connection has been closed by RST
      fprintf (stderr, "%s reset\n", buf);
      return;
    }

  if (a_tcp->nids_state == NIDS_DATA)
    {
      // new data has arrived; gotta determine in what direction
      // and if it's urgent or not

      struct half_stream *hlf;

      if (a_tcp->server.count_new_urg)
      {
        // new byte of urgent data has arrived 
        strcat(buf,"(urgent->)");
        buf[strlen(buf)+1]=0;
        buf[strlen(buf)]=a_tcp->server.urgdata;
        write(1,buf,strlen(buf));
        return;
      }
      // We don't have to check if urgent data to client has arrived,
      // because we haven't increased a_tcp->client.collect_urg variable.
      // So, we have some normal data to take care of.
      if (a_tcp->client.count_new)
    {
          // new data for the client
      hlf = &a_tcp->client; // from now on, we will deal with hlf var,
                                // which will point to client side of conn
      strcat (buf, "(<-)"); // symbolic direction of data
    }
      else
    {
      hlf = &a_tcp->server; // analogical
      strcat (buf, "(->)");
    }
    fprintf(stderr,"%s",buf); // we print the connection parameters
                              // (saddr, daddr, sport, dport) accompanied
                              // by data flow direction (-> or <-)

   write(2,hlf->data,hlf->count_new); // we print the newly arrived data

    }
  return ;
}

int main ()
{
  // here we can alter libnids params, for instance:
  // nids_params.n_hosts=256;
  if (!nids_init ())
  {
    fprintf(stderr,"%s\n",nids_errbuf);
    exit(1);
  }
  nids_register_tcp (tcp_callback);
  nids_run ();
  return 0;
}

基础的libnids结构体和方法

正是时候系统的介绍libnids的结构体了。正如之前提到的,他们都在nids.h中声明。

  • 四元组(源IP,源端口号,目的IP,目的端口号)
 struct tuple4 // TCP connection parameters
   {
   unsigned short source,dest; // client and server port numbers
   unsigned long saddr,daddr;  // client and server IP addresses
   };
  • half_stream
 struct half_stream // structure describing one side of a TCP connection
   {
   char state;            // socket state (ie TCP_ESTABLISHED )
   char collect;          // if >0, then data should be stored in "data" buffer; else data                                // flowing in this direction will be ignored have a look at                                 // examples/sniff.c for an example how one can use this field
   char collect_urg;      // analogically, determines if to collect urgent data
   char * data;           // buffer for normal data
   unsigned char urgdata; // one-byte buffer for urgent data
   int count;             // how many bytes has been appended to buffer "data" since the                                  // creation of a connection 
   int offset;            // offset (in data stream) of first byte stored in 
                          // the "data" buffer; additional explanations follow
   int count_new;         // how many bytes were appended to "data" buffer 
                          // last (this) time; if == 0, no new data arrived 
   char count_new_urg;    // if != 0, new data arrived

   ... // other fields are auxiliary for libnids

   };
  • tcp_stream
struct tcp_stream
   {
   struct tuple4 addr;   // connections params (saddr, daddr, sport, dport)
   char nids_state;                  // logical state of the connection
   struct half_stream client,server; // structures describing client and
                                     // server side of the connection 
   ...                               // other fields are auxiliary for libnids
   };

​ 在上面的实例程序中,回调函数tcp_callback输出显示hlf->data缓冲区中的数据到标准输出设备上。这些数据在 tcp_callback函数返回后,由libnids自动释放这些数据所占用的内存空间。同时,hlf->offset字段将增加被丢弃数据的字 节数,而新接收到的数据则存放到”data”缓冲区的起始处。
如果在其它应用中不进行如上例的操作(例如,数据处理过程至少需要N个字节的输入数据,而libnids只接收到的数据字节数count_new

 void nids_discard(struct tcp_stream * a_tcp, int num_bytes)

此时,当回调函数tcp_callback返回后linids将”data”缓冲区的前num_bytes字节数据,同时计算调整offset字段的数值,并将剩余数据移动到缓冲区的起始处。

如果始终不调用nids_discard()函数(如上面实例),hlf->data缓冲区中将包含hlf->count_new字节数据。 通常情况下,在hlf->data缓冲区中的数据字节数等于hlf->count - hlf->offset。
有了nids_discard()函数,程序员就不必拷贝接收到的数据到另外的缓冲区中,hlf->data缓冲区将总是尽可能保存足够的数据。然后,有时会有保留数据包特定数据的需要。例如,我们希望能监测到针对wu-ftpd服务器的”CWD”溢出攻击,就需要跟踪检查ftp客户端发送的 “CWD”命令。此时就需要tcp_callback回调函数具有第二个参数了。此参数是某TCP连接私有数据的指针。处理过程如下:

 void tcp_callback_2 (struct tcp_stream * a_tcp, struct conn_param **ptr)
   {
   if (a_tcp->nids_state==NIDS_JUST_EST)
   {
        struct conn_param * a_conn;
    if the connection is uninteresting, return;
        a_conn=malloc of some data structure
        init of a_conn
        *ptr=a_conn // this value will be passed to tcp_callback_2 in future
                    // calls
        increase some of "collect" fields
        return;
   }
   if (a_tcp->nids_state==NIDS_DATA)
   {
    struct conn_param *current_conn_param=*ptr;
        using current_conn_param and the newly received data from the net
        we search for attack signatures, possibly modyfying
        current_conn_param  
        return ;
   }

nids_register_tcp和nids_register_ip*函数可被任意次调用。在同一个TCP连接中使用两种不同的回调函数是允许的。
libnids库定义了一个全局变量结构nids_params,其声明如下:

struct nids_prm
   {
   int n_tcp_streams; // size of the hash table used for storing structures 
                      // tcp_stream; libnis will follow no more than 
                      // 3/4 * n_tcp_streams connections simultaneously
                      // default value: 1040. If set to 0, libnids will
                      // not assemble TCP streams.
   int n_hosts;       // size of the hash table used for storing info on
                      // IP defragmentation; default value: 256
   char * filename;   // capture filename from which to read packets; 
                      // file must be in libpcap format and device must
                      // be set to NULL; default value: NULL
   char * device;     // interface on which libnids will listen for packets;
                      // default value == NULL, in which case device will
                      // be determined by call to pcap_lookupdev; special
                      // value of "all" results in libnids trying to
                      // capture packets on all interfaces (this works only
                      // with Linux kernel > 2.2.0 and libpcap >= 0.6.0); 
                      // see also doc/LINUX 
   int sk_buff_size;  // size of struct sk_buff, a structure defined by
                      // Linux kernel, used by kernel for packets queuing. If 
                      // this parameter has different value from 
                      // sizeof(struct sk_buff), libnids can be bypassed
                      // by attacking resource managing of libnis (see TEST
                      // file). If you are paranoid, check sizeof(sk_buff)
                      // on the hosts on your network, and correct this 
                      // parameter. Default value: 168
   int dev_addon;     // how many bytes in structure sk_buff is reserved for
                      // information on net interface; if dev_addon==-1, it
                      // will be corrected during nids_init() according to
                      // type of the interface libnids will listen on.
                      // Default value: -1.
   void (*syslog)();  // see description below the nids_params definition
   int syslog_level;  // if nids_params.syslog==nids_syslog, then this field
                      // determines loglevel used by reporting events by
                      // system daemon syslogd; default value: LOG_ALERT
   int scan_num_hosts;// size of hash table used for storing info on port
                      // scanning; the number of simultaneuos port
              // scan attempts libnids will detect. if set to 
              // 0, port scanning detection will be turned
              // off. Default value: 256.
   int scan_num_ports;// how many TCP ports has to be scanned from the same
                      // source. Default value: 10.
   int scan_delay;    // with no more than scan_delay milisecond pause
                      // between two ports, in order to make libnids report
                      // portscan attempt. Default value: 3000
   void (*no_mem)();  // called when libnids runs out of memory; it should
                      // terminate the current process
   int (*ip_filter)(struct ip*);  // this function is consulted when an IP
                      // packet arrives; if ip_filter returns non-zero, the
                      // packet is processed, else it is discarded. This way
                      // one can monitor traffic directed at selected hosts
                      // only, not entire subnet. Default function 
                      // (nids_ip_filter) always returns 1
   char *pcap_filter; // filter string to hand to pcap(3). Default is
              // NULL. be aware that this applies to the
              // link-layer, so filters like "tcp dst port 23"
              // will NOT correctly handle fragmented traffic; one
                      // should add "or (ip[6:2] & 0x1fff != 0)" to process
                      // all fragmented packets
   int promisc;       // if non-zero, the device(s) libnids reads packets
                      // from will be put in promiscuous mode. Default: 1
   int one_loop_less; // disabled by default; see the explanation
   int pcap_timeout;  // the "timeout" parameter to pcap_open_live
                      // 1024 (ms) by default ; change to a lower value
                      // if you want a quick reaction to traffic; this
                      // is present starting with libnids-1.20
   int multiproc;     // start ip defragmentation and tcp stream assembly in a 
                      // different thread parameter to a nonzero value and 
                      // compiling libnids in an environment where  glib-2.0 is 
                      // available enables libnids to use two different threads 
                      // - one for receiving IP fragments from libpcap, 
                      // and one, with lower priority, to process fragments, 
                      // streams and to notify callbacks. Preferrably using 
                      // nids_run() this behavior is invisible to the user.
                      // Using this functionality with nids_next() is quite
                      // useless since the thread must be started and stopped
                      // for every packet received.
                      // Also, if it is enabled, global variables (nids_last_pcap_header
                      // and nids_last_pcap_data) may not point to the
              // packet currently processed by a callback
   int queue_limit;   // limit on the number of packets to be queued;
                      // used only when multiproc=true; 20000 by default
   int tcp_workarounds; // enable (hopefully harmless) workarounds for some
                      // non-rfc-compliant TCP/IP stacks
   pcap_t *pcap_desc; // pcap descriptor 
   } nids_params;

​ nids_params的syslog字段缺省时指向nids_syslog函数,声明如下:

void nids_syslog (int type, int errnum, struct ip *iph, void *data);

​ s_params.syslog函数用于记录异常情况,如端口扫描企图,无效TCP头标志等。该字段应指向自定义的日志处理函数。函数nids_syslog(在libnids.c中定义)可以作为例子阐述怎样解码传递到nids_params.syslog的数据。nids_syslog()函数向系统守护服务syslogd发送日志消息,但是这个函数不考虑传输速率和磁盘剩余空间(这也是它应该被替换的原因)。

​ 如果对UDP数据报文感兴趣,应该声明:

void udp_callback(struct tuple4 * addr, char * buf, int len, struct ip * iph);

然后注册它:

nids_register_udp(udp_callback)

​ addr包含地址信息,buf指向UPD报文携带的数据,len时数据长度,iph指向携带UDP报文的IP报文,校验码是已查证的。

Misc的技巧

作为一个很好的toy function:

void nids_killtcp(struct tcp_stream * a_tcp)

它通过发送一个RST片段a_tcp来终止tcp连接。

因为RST片段的序列号是某一个tcp窗口提供的,包含MS05-019补丁的windows系统不会因收到这样一条报文而终止连接,所以现在libnids发送两条报文——附加的报文有最小的(预期的)序列号。不幸的是,它是不可靠的:如果信号拥堵,程序就会有延迟,获得的预期序列号就是错误的。

通常来讲,把发送RST报文作为防御措施是不可靠的设计,除非不部署在在线NIDS(网络入侵侦测系统),或者NIDS(基于网络的入侵防护系统),这就是为什么称呼它toy function的原因。


使用nids_run()有一个缺点:应用程序将完全由数据包驱动(运行)。有时需要在没有数据包到达时也能处理一些任务,作为nids_run()函数的替代,程序员可使用如下函数:

int nids_next()

​ 此函数将调用pcap_next()函数,而不是pcap_loop()函数,也就是说,它只处理一个包。如果没有包到来,这是进程就会sleep,nids_next()函数成功时返回1,出错时返回0,且nids_errbuf缓冲区存放相应错误消息。

​ 典型地,当使用nids_next()函数时,应用程序调用I/O复用函数select()阻塞,并在read fd_set中呈现socket监听fd。fd可通过如下函数获得:

int nids_getfd()

​ 成功时返回一个文件描述字,出错时返回-1,且nids_errbuf缓冲区存放相应错误消息。

相似的函数:

int nids_dispatch(int cnt)

是pcap_dispatch的封装,当我们需要区分返回数据(ie 文件末尾或错误)时使用nids_dispatch比使用nids_next更好。


下面是跳过某些包的校验和检验的几条原因:

  1. 如今,一些NIC驱动能够为流出的包计算校验和。在这种情况下,流向libpcap的包含有未计算的校验和。所以,你可能不想检验这些包的校验和。
  2. 为了提高效率。

为了让libnids知道哪些包需要校验,你应该分配一个struct nids_chksum_ctl (defined in nids.h)的数组:

struct nids_chksum_ctl
{       u_int netaddr;
        u_int mask;
        u_int action;
    /* reserved fields */
};

然后用下面的函数注册它:

nids_register_chksum_ctl(struct nids_chksum_ctl *, int);

第二个参数表明了数组的长度。

校验函数会对数组的元素一个接一个的校验,如果源地址SRCIP满足下列条件

(SRCIP&chksum_ctl_array[i].mask)==chksum_ctl_array[i].netaddr

如果”action” 是NIDS_DO_CHKSUM, 这个报文就会接受校验; 如果”action” 是 NIDS_DONT_CHKSUM, 这个报文就不会接受校验. 如果报文没有匹配的到任何的数组元素,默认进行校验。

对所有包跳过检验:

struct nids_chksum_ctl temp;

temp.netaddr = inet_addr("0.0.0.0");
temp.mask = inet_addr("0.0.0.0");
temp.action = NIDS_DONT_CHKSUM;
//保证(SRCIP&chksum_ctl_array[i].mask)==chksum_ctl_array[i].netaddr
nids_register_chksum_ctl(&temp,1);

使用示例可以参照samples/chksum_ctl.c


nids.h定义了constants NIDS_MAJOR (1) 和NIDS_MINOR (21), 可以在运行时指定libnids的版本. Nids.h之前也定义HAVE_NEW_PCAP 但在1.19之后被废弃了。


Typically, data carried by a tcp stream can be divided into protocol-dependent records (say, lines of input). A tcp callback can receive an amount of data, which contains more then one record. Therefore, a tcp callback should iterate its protocol parsing routine over the whole amount of data received. This adds complexity to the code.

If

nids_params.one_loop_less

is non-zero, libnids behaviour changes slightly. If a callback consumes some (but not all) of newly arrived data, libnids calls it immediately again. Only non-processed data remain in the buffer, and

rcv->count_new

is decreased appropriately. Thus, a callback can process only one record at the time - libnids will call it again, until no new data remain or no data can be processed. Unfortunately, this behaviour introduces horrible semantics problems in case of 2+ callbacks reading the same half of a tcp stream. Therefore, if

nids_params.one_loop_less

is non-zero, you are not allowed to attach two or more callbacks to the same half of tcp stream. Unfortunately, the existing interface is unable to propagate the error to the callback - therefore, you must watch it yourself. You have been warned.


最后一个包的pcap header为

extern struct pcap_pkthdr *nids_last_pcap_header;

可以获得timestamp, 更好的准确性,或者保存到系统调用。


其他使用libnids的应用可以在examples中找到。

在1.21中的更新

1.21带来了bug修复, 优化和一些新的特色,提供了更多的extern变量和函数。

nids_last_pcap_data 是最新的pcap包的数据, 类似nids_last_pcap_header 来获取最新数据报头。

nids_linkoffset是一个用来计算当前pcap设备链路层和网络层偏移量的外部变量。It is useful to reconstruct PCAP frames from IP defragmented packets which you get in your ip_func (see [chapter on IP defragmentation](#IP defragmentation)) by copying the same amount of bytes from the beginning of nids_last_pcap_data representing the link layer, like this:

void ip_callback(struct ip *pkt, int len)
{
  u_char                        *frame;
  struct pcap_pkthdr            ph;

  frame = malloc(len + nids_linkoffset);
  memcpy(frame, nids_last_pcap_data, nids_linkoffset);
  memcpy(frame + nids_linkoffset, pkt, len);
  ph.ts = nids_last_pcap_header->ts;
  ph.caplen = ph.len = len + nids_linkoffset;
  pcap_dump(nids_params.pcap_desc, &ph, frame);
  free(frame);
}

In versions prior to 1.21 it was only possible to give libnids a device or file name and have it take total control over libpcap operations when using nids_run() or nids_next(). Now, with nids_params.pcap_desc it is possible to have your pcap_handler outside libnids and choose which frames you want to be processed by libnids (e.g. only TCP packets to keep track of TCP connections whilst this is not your only objective); all you have to do is copy your pointer to the pcap_t structure (returned by pcap_open_live(), pcap_open_dead() or pcap_open_offline()) to nids_params.pcap_desc and call nids_pcap_handler(), normally with the same parameters as your own pcap_handler (the one you registered with pcap_dispatch() or pcap_loop()) was called with. NOTE: since libnids cannot know when you are finished if you interactively pass packets to it with nids_pcap_handler(), you must tell it when to free the allocated resources by calling nids_exit().

nids_params.tcp_workarounds is a new libnids runtime option which can be used to enable extra checks for faulty implementations of TCP such as the ones which allow connections to be closed despite the fact that there should be retransmissions for lost packets first, thus violating section 3.5 of RFC 793. In those cases, and if this option is non-zero, libnids will set the NIDS_TIMED_OUT state for TCP connections that were savagely closed.

nids_find_tcp_stream() is a new external function that can be used to find the corresponding tcp_stream structure for a given pointer to a tuple4 structure.

nids_free_tcp_stream() is a new external function that can be used for example to force libnids into not following a TCP stream anymore. BEWARE! Calling nids_free_tcp_stream() from inside one of your registered tcp_callbacks on a TCP stream that is already in a closing state (NIDS_CLOSE, NIDS_TIMED_OUT, NIDS_RESET or NIDS_EXITING) will result in a double free (because libnids will call nids_free_tcp_stream() internally when your tcp_callback returns) and your program will crash.

nids_unregister_ip_frag(), nids_unregister_ip(), nids_unregister_udp() and nids_unregister_tcp() are new external functions that can be used to unregister callbacks previous registed with the corresponding nids_register_*(), at any time.

tcp_stream.user is a new field in the structure passed to TCP callbacks. It is similar to their void **param argument, except that it is global to all the TCP callbacks for the same stream, whereas param is specific to each callback.

FAQ

  • Q.1 对于某个链接,我的tcp回调函数只能获取服务器发送的数据?
    A.1 You probably run a libnids app on a host that is the client side of X; and your NIC driver offloads checksums computing to the hardware. So, when libnids sees packets sent by the client, their checksum is not computed, and they are dropped. See the API.html file on the description of the nids_register_chksum_ctl(), and configure libnids app to skip checksum verification of packets sent by the host you run libnids on.

  • Q.2 如何使libnids检测已经建立的TCP连接

    A.2 计划有但是没有实现; it would be unreliable, due to the fact that some crucial information (like TCP window scale) is present only in SYN packets. If you really need this functionality, you can try the included libnids-track-established.patch, prepared by Alon Bar-Lev; of course, the responsibility is yours.

运行安装包中的示例代码

示例代码在安装文件下的sample下,我的测试环境是centos7,安装包中的sample无法正确执行,需要对Makefile文件进行更改。在命令行执行pkg-config --cflags --libs glib-2.0,根据执行结果修改Makefile中的一些参数。我修改后的Makefile。


srcdir      = .


CC      = gcc
CFLAGS      = -g -O2 -D_BSD_SOURCE
LDFLAGS     = 

PCAP_CFLAGS = -I/usr/include/pcap
PCAPLIB     = -lpcap

LNET_CFLAGS = -D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD -DHAVE_NET_ETHERNET_H
LNETLIB     = -lnet

GLIB_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include

LIBS_CFLAGS = -I../src $(PCAP_CFLAGS) $(LNET_CFLAGS) $(GLIB_CFLAGS) -fno-builtin-exit -fno-builtin-strcpy -fno-builtin-strlen -fno-builtin-strcat -fno-builtin-malloc
LIBS        = -L../src -lnids $(PCAPLIB) $(LNETLIB) -lgthread-2.0 -lnsl -pthread -lglib-2.0 -fno-builtin-exit
GLIB        = pkg-config --cflags --libs glib-2.0

.c.o:
    $(CC) -c $(CFLAGS) -I. $(LIBS_CFLAGS) $<

all: overflows printall sniff
static shared: all

overflows: overflows.o
    $(CC) -o $@ overflows.o $(LDFLAGS) $(LIBS) 

printall: printall.o
    $(CC) -o $@ printall.o $(LDFLAGS) $(LIBS)

sniff: sniff.o
    $(CC) -o $@ sniff.o $(LDFLAGS) $(LIBS)

static shared install installshared:
    @true

clean:
    rm -f *.o *~ overflows printall sniff

# EOF

你可能感兴趣的:(计算机网络)