Libnids-API(中文版)

Libnids-API 中Libnids-API(中文版)文版1.16


==================== 
libnids-1.16 
====================


1. 简介 
2. IP碎片重组 
3. TCP流还原 
4. 例子程序 
5. 基本的Libnids结构和函数 
6. 其它有用的技巧 


1. 简介 

Libnids定义的数据结构和函数的声明集中在头文件nids.h中。使用Libnids的应用程序 
必须包含这个文件,并且要与Libnids.a静态库进行连接。 


应用程序的Main函数一般是这样的形式: 
main() 

应用程序的私有处理,与Libnids无关; 
可选择的Libnids参数的修改; 
if(!nids_init())那里出了问题,结束; 
出测回调函数; 
nids_run(); 
//正常情况下不可达; 


另外的方法将在以后提及。 

2.IP碎片重组 

为了接收到Libnids看到的所有的IP包(包括分片的;带有无效校验和的IP包)程序员应该 
定义一个如下类型的回调函数: 

void ip_frag_func(struct ip * a_packet, int len) 

在调用nids_init之后,这个函数应该使用Libnids进行注册: 

nids_register_ip_frag(ip_frag_func); 

函数ip_frag_func 将被Libnids调用;参数a_packet将指向一个收到的数据报,len是其长度。 

类似的,为了接受到适当地数据包,这些包是要被目标主机接收的(就是,没有分片的包或者已经 
完成重组的分片包;头部的正确性已经校验过),应该定义一个回调函数: 

void ip_func(struct ip * a_packet) 

并注册它: 

nids_register_ip(ip_func); 

3. TCP流的重组 

为了接收到一个TCP流中交换的数据,必须定义一个回调函数: 

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

tcp_stream结构包含了所有的关于一个TCP连接的信息,例如:它包含两个half_stream(名为 
client和server)结构类型的域,每一个描述了一侧的连接,我们将在后面解释所有的域。 

tcp_stream的一个域的名字为nids_state,tcp_callback的行为依赖于这个域的值。 

* ns->nids_state==NIDS_JUST_EST 

这里,ns描述了一个已经建立的连接。Tcp_callback必须判断当将来这个连接中 
的数据到达的时候,它是否希望被通知。所有的连接参数都是可用的(ip地址,端 
口号等等)。如果这个连接是我们感兴趣的,tcp_callback函数通知Libnids它希 
望接收什么数据(发到客户端的数据;发到服务器的数据;发到客户端的紧急数据; 
发到服务器的紧急数据),然后函数返回。 


* ns->nids_state==NIDS_DATA 

这里,新的数据已经到达,half_stream结构(tcp_stream结构的成员)包含数据 
缓冲区。 

* nids_state域的其它值(NIDS_CLOSE, NIDS_RESET,NIDS_TIMEOUT)意味着连接 
已经被关闭。Tcp_callback应给释放其所分配的资源,即使它需要。 

4. 例子程序 

现在,让我们看看一个简单的例子程序,这个程序在stderr中显示Libnids所监视到的所 
有TCP连接所交换的数据。 

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netinet/in_systm.h> 
#include <arpa/inet.h> 
#include <string.h> 
#include <stdio.h> 
#include "nids.h" 

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

// struct tuple4 包含TCP连接的地址和端口号 
// 下面的辅助函数生成一个类似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) 

// 由a_tcp描述的连接已经建立 
// 这里我们决定是否希望跟踪这个流 
// 例子条件: if (a_tcp->addr.dest!=23) return; 
// 在本程序中我们跟踪所有的流所以。。。。。: 
a_tcp->client.collect++; // 我们需要客户端接收到的数据....... 
a_tcp->server.collect++; // 我们需要服务器接收到的数据....... 
a_tcp->server.collect_urg++; // 我们需要服务器接收到的紧急数据....... 

#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT 

a_tcp->client.collect_urg++; // 如果我们不增加这个值,当紧急数据到达 
//时我们不会被通知。 
#endif 
fprintf (stderr, "%s establishedn", buf); 
return; 

if (a_tcp->nids_state == NIDS_CLOSE) 

// 连接已经正常结束 
fprintf (stderr, "%s closingn", buf); 
return; 

if (a_tcp->nids_state == NIDS_RESET) 

// 连接已经通过RST关闭。 
fprintf (stderr, "%s resetn", buf); 
return; 


if (a_tcp->nids_state == NIDS_DATA) 

// 新的数据已经到达,必须判断其数据流向 
// 判断其是否紧急数据 

struct half_stream *hlf; 

if (a_tcp->server.count_new_urg) 

// 紧急数据的新字节已经到达 
strcat(buf,"(urgent->)"); 
buf[strlen(buf)+1]=0; 
buf[strlen(buf)]=a_tcp->server.urgdata; 
write(1,buf,strlen(buf)); 
return; 

// 我们不必必须检查是否客户端的紧急数据已经到达 
// 因为我们没有增加a_tcp->client.collect_urg的值 
// 因此,我们还有一些正常的数据关心 

if (a_tcp->client.count_new) 

//客户端的新数据 
hlf = &a_tcp->client; // 现在我们将处理hlf变量 
// 这个变量指向客户端一边的连接。 
strcat (buf, "(<-)"); // 数据的符号方向 

else 

hlf = &a_tcp->server; // 类似的 
strcat (buf, "(->)"); 

fprintf(stderr,"%s",buf); // 我们打印连接参数(saddr, daddr, sport, dport) 
// 和数据流向(-> or <-) 

write(2,hlf->data,hlf->count_new); // 我们打印最新到达的数据 


return ; 


int 
main () 

// 这里我们可以改变Libnids的params,例如 nids_params.n_hosts=256; 
if (!nids_init ()) 

fprintf(stderr,"%sn",nids_errbuf); 
exit(1); 

nids_register_tcp (tcp_callback); 
nids_run (); 
return 0; 


5. 基本的Libnids结构和函数 

现在是时候仔细描述一些提到的Libnids的结构的时候了,它们在头文件nids.h中声明。 

struct tuple4 // TCP连接参数 

unsigned short source,dest; // 客户端和服务器端口号 
unsigned long saddr,daddr; // 客户端和服务器IP地址 
}; 


struct half_stream // TCP连接一侧的描述结构 

char state; // socket 状态 (例如:TCP_ESTABLISHED ) 

char collect; // if >0, 那么数据应该被存放到data缓冲区中。否则, 
// 这个方向的数据流将被忽略 
// 看一下samples/sniff.c文件如何使用这个域 

char collect_urg; // 类似的,判断是否为紧急数据。 

char * data; // 正常数据缓冲区 

unsigned char urgdata; // one-byte buffer for urgent data 

int count; // 自连接建立以来已经有多少字节已经发送到data缓冲区中 

int offset; // offset (in data stream) of first byte stored in 
// the "data" buffer; additional explanations 
// follow 

int count_new; // 多少字节将被发送到data缓冲区中last (this) time; 
// if == 0, 没有新数据到达。 

char count_new_urg; // if != 0,新的紧急数据到达 

... // other fields are auxiliary for libnids 

}; 


struct tcp_stream 

struct tuple4 addr; // 连接参数(saddr, daddr, sport, dport) 
char nids_state; // 连接的逻辑状态 
struct half_stream client,server; // 描述连接的客户端和服务器端的结构 
... // other fields are auxiliary for libnids 
}; 

在上面的例子程序中,函数tcp_callback打印hlf->data缓冲区的数据到stderr上, 
并且这些数据不再需要了。在tcp_callback返回后,默认情况下Libnids释放这些数 
据占用的空间。hlf->offset域将增加丢弃的字节数,并接新的数据将存储到data缓冲区 
的开始。如果上面的操作不是我们所希望的,(例如,数据处理器需要至少N字节的输入来 
处理,并且迄今为止Libnids接受到的count_new<N字节)应该调用程序: 

void nids_discard(struct tcp_stream * a_tcp, int num_bytes) 

在tcp_callback返回前。结果,在tcp_callback返回后,Libnids将丢弃data缓冲区中至 
多num_bytes字节的前面的内容(更新offset域,移动剩下的数据到data缓冲区的开始位置)。 
如果nids_discard函数没有被调用(像上面的例子程序),缓冲区hlf->data包含 
hlf->count_new字节的内容。一般情况下,缓冲区hlf->data中的字节数等于 
hlf->count-hlf->offset. 

感谢函数nids_discard,程序员不得不必须把接受到的数据拷贝到一个独立的缓冲区中。 
hlf->data将一直包含尽可能多的数据。可是,经常发生对每一个ibnids_callback, tcp 
stream对维护其辅助数据结构的请求。例如,如果我们希望检测一个针对wu-ftpd的攻击(这个 
攻击包括在服务器上建立深层目录)我们需要将Ftpd守护进程的目录存储到某一个地方。 
通过Ftp客户端的CWD指令怾改变。这就是tcp_callback的第二个参数的目的。它是一个指向 
每一个(libnids_callback, tcp stream)对所私有的数据的指针。 

典型情况如下: 

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; 
如果连接是我们不感兴趣的,返回; 
a_conn=malloc of some data structure//内存分配 
init of a_conn//初始化 
*ptr=a_conn // 这个值在将来的调用中将被传递给tcp_callback_2 
增加一些 "collect" 域 
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_callback相似的函数允许跟踪同一个TCP流。(with acertain non-default exception). 

Libnids参数可以通过全局变量nids_params的域的改变来修改。如下声明: 
struct nids_prm 

int n_tcp_streams; // 用于存储tcp_stream结构信息的Hash表的大小 
// libnis将只能同时跟踪 3/4 * n_tcp_streams连接的数据流 
// 默认值:1040 
// 如果设置为0,Libnids将不再进行TCP流的组装 
int n_hosts; // 用于存储关于IP碎片重组的信息的Hash表的大小 
// 默认值: 256 
char * device; // Libnids用于监听数据包的设备接口 
// 默认值 :NULL, 将通过调用pcap_lookupdev函数来接决定; 
// 特殊值all将致使Libnids试图通过所有的设备接口截获数据包 
// (这个参数在高于2.2.0Linux 核心版本有效) 
// 参见 doc/NEW_LIBPCAP 
int sk_buff_size; // 结构sk_buff的大小,这个结构是由Linux核心定义的,核心用于 
// 数据包排列,如果这个参数和sizeof(struct sk_buff)地值不同, 
// Libnids可以通过攻击其资源管理而被绕过。见TEST文件。 
// 如果你是一个喜欢妄想的人,那么检查你网络中主机的sizeof(sk_buff) 
// 并调整这个参数,默认值:168 
int dev_addon; // 在sk_buff结构中保留了多少字节用于存储网络接口信息;如果dev_addon==-1, 
// 将在nids_init()中根据Libnids监听的接口的类型进行改正。it 
// 默认值: -1. 
void (*syslog)(); // 参见nids_params定义部分的描述 
int syslog_level; // 如果 nids_params.syslog==nids_syslog,那么这个域将决定 
// 系统守护进程syslogd报告事件所使用的等级loglevel. 
// 默认值: LOG_ALERT 
int scan_num_hosts;// 用于存储关于端口扫描的信息的Hash表的大小;Libndis能够检测 
// 到的同时发生的端口扫描企图。如果设置为0,端口扫描检测将被关闭 
// 默认值:256 
int scan_num_ports;// 多少个TCP端口必须被同一个源地址扫描 
// 默认值: 10 
int scan_delay; // 两个端口之间最大的扫描间隔 
// 用于使Libnids可以报告端口扫描企图 
// 默认值:3000 
void (*no_mem)(); // 当Libndis的内存资源耗尽时调用此函数 
// 它应该终止当前进程 
int (*ip_filter)(struct ip*); // 这个参数当IP数据包到达时才会被考虑 
// 如果ip_filter返回non-zero, 处理这个包否则忽略掉 
// 通过这种方式,可以只监控所选中的主机,而不是整个子网 
// 默认函数: (nids_ip_filter) 一般返回值为:1 
char *pcap_filter; // 用于pcap地过滤字符串,默认情况下为NULL。 
// 需要了解的是这强应用到link-layer,所以象"tcp dst port 23" 
// 一样的过滤器无法控制碎片传输。 
int promisc; // 如果非零,Libnids读取数据包的设备将被设置为混杂模式 
// 默认为:1 
int one_loop_less; // 默认情况下不可用 
} nids_params; 

nids_params变量中的syslog域在默认情况下包含函数nids_syslog的地址,如下声明: 

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

函数nids_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指针指向UDP数据包携带的数据,len是数据长度,iph是包 
含此UDP数据报的Ip包的指针。校验和已经进行过。 

6. 其他有用的技巧 

void nids_killtcp(struct tcp_stream * a_tcp) 

通过发送RST结束一个a_tcp描述的TCP连接。 


使用nids_run()存在一个不利的因素,应用程序变成了完全的包驱动,有时在没有包到达的 
时候执行某些操作也是必须得。代替nids_run(),我们可以使用函数: 

int nids_next() 

这个函数调用 pcap_next() 而不是 pcap_loop, 就是说它之处理一个包,如果没有包可用, 
程序将处于睡眠状态,Nids_next()成功时返回1;出错返回0。(nids_errbuf 包含相关联 
的信息) 

标准情况下,当时用nids_next()时,因各应用程序将睡眠在一个select()函数中,同时在fd_set 
中引入了一个监听得socket fd. 这个fd可以通过下面的调用获得: 

int nids_getfd() 

它返回一个文件描述符,失败的时候返回-1。(nids_errbuf包含出错信息)。 


头文件nids.h定义了常量 NIDS_MAJOR (1) 和NIDS_MINOR (16), 这些常量用于判定Libnids的 
现行版本。如果HAVE_NEW_PCAP(同样在nids.h中定义) 设置为1,则Libnids已被编译为支持 
通过所有设备截获数据包。(见 NEW_LIBPCAP 文件) 


典型情况下,TCP流携带的数据可以被分成协议依赖(protocol-dependent)记录(say, lines of 
input)。一个tcp_callback函数可以接收一定数量的数据,这些数据可以包含多余一个地记录。因此 
tcp_callback函数应该对所有接受到的数据重复进行协议解析程序。这增加了代码的复杂性。 

如果nids_params.one_loop_less是非零的,Libnids的行为会发生轻微的变化。如果一个callback 
消耗了部分而不是全部的新到达的数据,Libnids立刻再次调用它。只有在缓冲区中不再有未处理的保 
留数据,并且rcv->count_new相应减少。因而,callback能一次处理一个记录,Libnids将再次调用 
它,知道没有新的数据剩余或者没有数据可以处理。不幸的,在两个以上的callback函数读取同一个 
TCP流的一半的时候,这一行为导致了可怕的语法问题。所以,如果nids_params.one_loop_less如果 
为非零,你不允许分配两个或者更多callback函数处理同一个TCP流的一半。不幸的是现存的接口不能 
向callback函数宣布这个错误,因此你必须自己小心,你已经被警告过了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值