u_int8_t th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
#if (LIBNET_BIG_ENDIAN)
u_int8_t th_off:4, /* data offset */
th_x2:4; /* (unused) */
#endif
u_int8_t th_flags; /* control flags */
…….
u_int16_t th_win; /* window */
u_int16_t th_sum; /* checksum */
u_int16_t th_urp; /* urgent pointer */
};
它的长度是:
#define LIBNET_TCP_H 0x14 /**< TCP header: 20 bytes */
当程序员调用libnet_built_tcp()构建一个TCP包头时,libnet会分配一个这样的结构,并且按照程序员的意愿填充这个结构的各个字段。并且生成一个libnet_pblock_t结构,使它的buf指针指向这个数据包头结构,并用b_len保存这个包头的长度(20字节)。这个libnet_pblock_t的其它相关字段将会
被按规则填充。之后,这个libnet_pblock_t会被加入到libnet_t结构所维护的协议块链的适当位置。所谓“适当位置”,将会在后面加以说明。下图是进程中一种可能出现的情形:
利益于这种精巧的整体架构,libnet的具体实现就不显得困难了,而我们对它的理解也因此受益。剩下来的只是一些细节性的东西需要去把握,这一点显然更适合亲自去阅读代码。
下面是大概的处理流程。
在开始之前,需要插入一点说明:libnet使用条件编译的方式消除平台间的差异,由此产生了很多同名的函数(当然它们都在内部被调用),比如libnet_open_link()有五个。程序在运行时选择哪一个调用取决于你使用的平台或者你强加于编译器的预编译选项。
一般情况下事情会从libnet_init()开始。libnet_init()首先开启系统的网络功能:在linux下面,它会验证是否具有超级用户权限(这也就意味着低权限的用户不能使用成功),而如果是在windows下面,则会调用大名鼎鼎的WSAStartup()函数。之后分配一块内存区域建立libnet_t结构。在简单的初始化后,根据程序员传入的发送类型参数,进行相应的操作:如果是基于链路层的发送方式
(LIBNET_LINK,LIBNET_LINK_ADV),则会向操作系统申请设备,并开启底层的socket服务。如果是基于IP层的发送方式,则开启该层的socket服务。
准备工作做好之后,转入直接的建包工作,这一工作实际表现为建一系列的协议包头。程序员需要按照自顶向下的顺序建协议包头,而要发送的正文数据往往被作为第一个协议包头的“有效荷载(payload)”被加载。此外,某些协议可能具有可选数据项,这一部分被libnet独立出来,由一个单独的libnet_pblock_t来负责,libnet同时也为此提供相应的功能函数,其名称被定为libnet_build_*_options().在这种情况下,libnet会自动调整这些协议块在链表中的位置,使payload的数据块在前面,中间是options块,其后跟随着该层协议的固定包头。
在合适的时候,libnet会把计算校验和这样的繁琐的工作优雅地完成,你甚至感觉不到它已经为你这样做了。
这样,只要程序员的使用正确,数据以及协议包头已经在链表中按顺序排列了,这为简化后面的工作极为有益。在libnet_write()过程中,链表中的数据被按顺序地拷贝到一个大的缓冲内。拷贝虽然是按照从高层协议到低层协议的顺序进行,但是却是从缓冲的后部向前部拷贝,从这里,我们可以看到“协议栈”的思想在闪光。
当一切工作完成之后,程序员简单地调用libnet_destroy()函数,关闭使用的网络通讯设备,释放占用的内存。程序员的工作是如此地简单:他甚至仅仅需要传递一个参数给这个简短的函数。
除了完成这样一些最直接的工作以外,libnet还提供了一组丰富实用的功能。它提供的地址解析功能能够实现IP地址在网络字节顺序、域名、点分形式之间的转换,它也提供了一套完整的随机数生成方案供你使用,如此等等。这一些功能使得它得以从一套单纯的库跃升为一个完整的系统。
以上文章由凌阳嵌入式培训 讲师浩哥提供,
http://bbs.sunplusedu.com/thread-12302-1-1.html 转载请注明出处!
使用libnet与libpcap构造TCP/IP协议软件
褚蓬飞 (white_cpf@21cn.com), 中国科学院软件技术研究所 2003 年 6 月 01 日
本文在RED HAT Linux8.0+以太网环境下,利用libnet和libpcap库实现了一个以太网上用户态的单进程的TCP/IP协议软件包:minitcpip,该软件实现了TCP协议的基本通讯功能,并提供了一个调试接口和一个与标准SOCKET接口类似的接口函数库minisocket,方便用户的调试与应用软件的调用。这个用户态的协议软件包的实现,为学习综合使用libnet和libpcap提供了良好的范例;通过对这个软件包的学习,还可以加深对TCP/IP协议(尤其是在以太网上)的运行原理的理解;另外,由于这个软件包运行在单进程、用户态环境下,也为调试和学习带来了极大的方便。
概述
目前有许多不同的成熟的TCP/IP协议的实现版本,其中大部分都在操作系统的核心实现,这种方案固然是提高TCP/IP协议软件的效率的必然所选,但却给TCP/IP协议的学习、研究和调试带来了很大的困难。于是,如果不考虑TCP/IP协议软件实现的效率问题,在应用进程中实现一个TCP/IP协议软件,是具有一定的意义和价值的。
本文作者构造了一个单进程的TCP/IP协议软件:minitcpip,并提供了一个SOCKET接口函数库:minisocket。在实现这个协议软件函数库时,作者选择采用了libnet+libpcap的方式在用户态下实现这个软件,不仅是因为这样可以避开一些操作系统对底层网络开发的种种限制带来的不便,将精力集中在对协议软件本身的理解上;另外一个原因,则是为大家学习和综合使用libnet和libpcap提供一个范例。
下文首先介绍了libnet和libpcap函数库及其使用,并给出了一个利用其实现ARP协议的例程--该协议的实现也包括在minitcpip软件之中,然后给出了本文的协议软件和SOCKET函数库实现的方案,并围绕本文主题,对涉及到的一些关键技术问题进行了分析,最后,对这种实现方法做了一个简单的总结,指出了这种实现方法的一些局限。
回页首
何谓libnet、libpcap
目前众多的网络安全程序、工具和软件都是基于socket设计和开发的。由于在安全程序中通常需要对网络通讯的细节(如连接双方地址/端口、服务类型、传输控制等)进行检查、处理或控制,象数据包截获、数据包头分析、数据包重写、甚至截断连接等,都几乎在每个网络安全程序中必须实现。为了简化网络安全程序的编写过程,提高网络安全程序的性能和健壮性,同时使代码更易重用与移植,最好的方法就是将最常用和最繁复的过程函数,如监听套接口的打开/关闭、数据包截获、数据包构造/发送/接收等,封装起来,以API library的方式提供给开发人员使用。
在众多的API library中,对于类Unix系统平台上的网络安全工具开发而言,目前最为流行的C API library有libnet、libpcap、libnids和libicmp等。它们分别从不同层次和角度提供了不同的功能函数。使网络开发人员能够忽略网络底层细节的实现,从而专注于程序本身具体功能的设计与开发。其中, libnet提供的接口函数主要实现和封装了数据包的构造和发送过程。 libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。 利用这些C函数库的接口,网络安全工具开发人员可以很方便地编写出具有结构化强、健壮性好、可移植性高等特点的程序。因此,这些函数库在网络安全工具的开发中具有很大的价值,在scanner、sniffer、firewall、IDS等领域都获得了极其广泛的应用,著名的tcpdump软件、ethereal软件等就是在libpcap的基础上开发的。
另外也应该指出:由于其功能强大,这些函数库也被黑客用来构造TCP/IP网络程序对目标主机进行攻击。然而,TCP/IP网络的安全不可能也不应该建立在禁止大家使用工具的基础上,一个理想的网络,首先必须是一个开放的网络,这个网络应该在使用任何工具的情况下都是安全的和健壮的。从这点考虑,这些工具的使用,对促进现有网络系统的不断完善是大有裨益的。
回页首
libnet函数库框架和使用
libnet是一个小型的接口函数库,主要用C语言写成,提供了低层网络数据报的构造、处理和发送功能。libnet的开发目的是:建立一个简单统一的网络编程接口以屏蔽不同操作系统低层网络编程的差别,使得程序员将精力集中在解决关键问题上。他的主要特点是: 高层接口:libnet主要用C语言写成
可移植性:libnet目前可以在Linux、FreeBSD、Solaris、WindowsNT等操作系统上运行,并且提供了统一的接口
数据报构造:libnet提供了一系列的TCP/IP数据报文的构造函数以方便用户使用