libnet的使用详解
最近搬砖需要对libnet进行介绍在这里对知识进行汇总。
1、libnet简介
在libnet出现以前,如果要构造数据包并发送到网络中,程序员要通过一些复杂的接口来处理。libnet的出现,为程序员提供了一个简单而易于使用的编程接口,可以帮助程序员方便地构造网络数据包,编写网络应用程序。
libnet是一个与libpcap类似的数据包注入器,可以用来编写有关网络测试、网络故障诊断和网络安全等方面的应用程序和工具。
许多网络工具 (如snort,Nmap)都是利用它来实现其部分功能。
libnet提供一系列的接口函数,实现和封装了数据包的构造和发送过程。利用它可以亲自构造从应用层到链路层的各层协议的数据包头,并将这些包头与有效数据有序地组合在一起发送出去。当然,它也是基于TCP/IP协议网络模型的。
全部源代码包括18,000行代码,109个导出函数,其中包括67个建包函数。这使得它支持现有的TCP/IP族的所有协议。
2、libnet的特点
libnet是一个小型的接口函数库,主要用C语言写成,提供了低层网络数据报的构造、处理和发送功能。libnet的开发目的是建立一个简单统一的网络编程接口以屏蔽不同操作系统低层网络编程的差别,使得程序员将精力集中在解决关键问题上。
它具有以下特点:
(1)高层接口:libnet被用于提取低 层数据报转移的专用体系结构细节。
(2)低层数据报构建:libnet的一个主要特点就是它完全控制每个数据报的头 域。
(3)可移植性的接口:与具体的操作系统平台无关。libnet目前可以在Windows、Linux、OS、FreeBSD、Solaris等操作系统上运行,并且提供了统一的接口。
(4)数据包构造:libnet提供了一系列的TCP/IP数据报文的构造函数以方便用户使用。
(5)数据包的处理:libnet提供了一系列处理底层网络编程的辅助函数,利用这些辅助函数,帮助用户简化那些烦琐的事务性的编程工作。
(6)数据包发送:libnet允许用户在两种不同的数据包发送方法中选择。
(7)libnet支持TCP/IP协议族中的所有协议。
libnet函数库提供的接口函数包含 15 种数据包生成器和两种数据包发送器(IP 层和数据链路层)。
提供的接口函数包括:
1)内存管理(分配和释放)函数
2)地址解析函数
3)各种协议类型的数据包构造函数
4)数据包发送函数(IP层和链路层)
5)一些辅助函数,如产生随机数、错误报告、端口列表管理等。
利用libnet函数库开发应用程序的基本步骤:
1)数据包内存初始化
2)构造数据包
3)发送数据
4)释放资源
以发送 UDP 数据包为例,流程图如图
常用函数介绍
以下函数的使用需要包含头文件libnet.h
1、libnet_t *libnet_init(int injection_type, char *device, char *err_buf)
功能:
数据包内存初始化及环境建立
参数:
injection_type:构造的类型
LIBNET_LINK,链路层
LIBNET_RAW4,网络接口层(网络层)
LIBNET_LINK_ADV,链路层高级版本
LIBNET_RAW4_ADV,网络层高级版本
device:网络接口,如 "eth0",或IP地址,亦可为NULL (自动查询搜索)
err_buf:存放出错的信息
返回值:
成功:一个libnet *类型的指针,后面的操作都得使用这个指针
失败:NULL
该函数初始化libnet函数库,返回一个libnet_t类型的描述符,以备随后的构造数据包和发送数据包的函数中使用。
injection_type指明了发送数据包使用的 接口类型,如数据链路层或者原始套接字等。Device是一个网络设备名称的字符串,在linux下“eth0”等。如果函数错误,返回NULL,er_buf字符串中将携带有错误的原因。
2、void libnet_destroy(libnet_t *l);
功能:
释放资源
参数:
l:libnet_init() 返回的libnet *指针
返回值:
无
3、char* libnet_addr2name4(u_int32_t in, u_int8_t use_name)
功能:
将网络字节序转换成点分十进制数串
参数:
in:网络字节序的ip地址
use_name:
LIBNET_RESOLVE,对应主机名
LIBNET_DONT_RESOLVE,对应点分十进制IPv4地址
返回值:
成功:点分十进制ip地址
失败:NULL
4、u_int32_t libnet_name2addr4(libnet_t *l, char *host_name, u_int8_t use_name)
功能:
将点分十进制数串转换为网络字节序ip地址
参数:
l:libnet_init()返回的libnet *指针
host_name:
LIBNET_RESOLVE, 对应主机名
LIBNET_DONT_RESOLVE,对应点分十进制 IPv4 地址
返回值:
成功:网络字节序 ip 地址
失败:-1
5、struct libnet_ether_addr* libnet_get_hwaddr(libnet_t *l)
功能:
获取接口设备硬件地址
参数:
l:libnet_init()返回的libnet *指针
返回值:
成功:指向MAC地址的指针
失败:NULL
6、libnet_ptag_t libnet_build_udp(u_int16_t sp, u_int16_t dp,u_int16_t len, u_int16_t sum,u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
功能:
构造udp数据包
参数:
sp:源端口号
dp:目的端口号
len:udp包总长度
sum:校验和设为0,libnet自动填充
payload:负载为给应用程序发送的文本内容,没有内容时可设置为 NULL
payload_s:负载长度,给应用程序发送文本内容的长度,或为0
l:libnet_init()返回的libnet *指针
ptag:协议标记,第一次组新的发送包时,这里写0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
7、libnet_ptag_t libnet_build_tcp(u_int16_t sp, u_int16_t dp, u_int32_t seq, u_int32_t ack, u_int8_t control, u_int16_t winu_int16_t sum, u_int16_t urg, u_int16_t len, u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag );
功能:
构造 tcp 数据包
参数:
sp:源端口号
dp:目的端口号
seq:序号
ack:ack 标记
control:控制标记
win:窗口大小
sum:校验和,设为 0,libnet 自动填充
urg:紧急指针
len:tcp包长度
payload:负载,为给应用程序发送的文本内容,可设置为 NULL
payload_s:负载长度,或为 0
l:libnet_init() 返回的 libnet * 指针
ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
8、libnet_ptag_t libnet_build_ipv4( u_int16_t ip_len, u_int8_t tos, u_int16_t id, u_int16_t flag, u_int8_t ttl, u_int8_t prot, u_int16 sum, u_int32_t src, u_int32_t dst, u_int8_t *payload,u_int32_t payload_s,libnet_t *l, libnet_ptag_t ptag )
功能:
构造一个 IPv4 数据包
参数:
ip_len:ip 包总长
tos:服务类型
id:ip 标识
flag:片偏移
ttl:生存时间
prot:上层协议
sum:校验和,设为 0,libnet 自动填充
src:源ip地址
dst:目的ip地址
payload:负载,可设置为 NULL(这里通常写 NULL)
payload_s:负载长度,或为 0(这里通常写 0 )
l:libnet 句柄,libnet_init()返回的libnet * 指针
ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
9、libnet_ptag_t libnet_build_ethernet(u_int8_t*dst, u_int8_t *src, u_int16_ttype, u_int8_t*payload, u_int32_tpayload_s, libnet_t*l, libnet_ptag_t ptag )
功能:
构造一个以太网数据包
参数:
dst:目的mac
src:源mac
type:上层协议类型
payload:负载,即附带的数据,可设置为 NULL(这里通常写 NULL)
payload_s:负载长度,或为 0(这里通常写 0 )
l:libnet 句柄,libnet_init() 返回的 libnet * 指针
ptag:协议标记,第一次组新的发送包时,这里写0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
10、int libnet_write(libnet_t * l)
功能:
发送数据包
参数:
l:libnet 句柄,libnet_init()返回的 libnet * 指针
返回值:
成功:发送数据包的长度
失败:返回 -1
libnet的使用案例
1、在Ubuntu上安装libnet
安装命令:sudo apt-get install libnet-dev,图23-2
图23-2 libnet在linux中安装
2、按照图23-1编写发送UDP数据包的代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libnet.h>
int main(int argc, charchar *argv[])
{
char send_msg[1000] = "";
char err_buf[100] = "";
libnet_t *lib_net = NULL;
int lens = 0;
libnet_ptag_t lib_t = 0;
unsigned char src_mac[6] = {0x00,0x0c,0x29,0x97,0xc7,0xc1};
//发送者网卡地址00:0c:29:97:c7:c1
unsigned char dst_mac[6] = {0x74,0x27,0xea,0xb5,0xff,0xd8};
//接收者网卡地址74-27-EA-B5-FF-D8
char *src_ip_str = "192.168.31.163"; //源主机IP地址
char *dst_ip_str = "192.168.31.248"; //目的主机IP地址
unsigned long src_ip,dst_ip = 0;
lens = sprintf(send_msg, "%s", "this is for the udp test");
lib_net = libnet_init(LIBNET_LINK_ADV, NULL, err_buf); //初始化
if(NULL == lib_net)
{
perror("libnet_init");
exit(-1);
}
src_ip = libnet_name2addr4(lib_net,src_ip_str,LIBNET_RESOLVE);
//将字符串类型的ip转换为顺序网络字节流
dst_ip = libnet_name2addr4(lib_net,dst_ip_str,LIBNET_RESOLVE);
lib_t=libnet_build_udp( //构造udp数据包
8080, 8080, 8+lens, 0, send_msg, lens, lib_net, 0);
lib_t = libnet_build_ipv4( //构造ip数据包
20+8+lens,0,500,0,10,17, 0, src_ip,dst_ip,NULL,0,lib_net, 0);
lib_t = libnet_build_ethernet( //构造以太网数据包
(u_int8_t*)dst_mac,(u_int8_t *)src_mac,0x800,NULL,0,lib_net,0);
int res = 0;
res = libnet_write(lib_net); //发送数据包
if(-1 == res)
{
perror("libnet_write");
exit(-1);
}
libnet_destroy(lib_net); //销毁资源
printf("----ok-----\n");
return 0;
}
编译代码时,注意需要加上 -lnet,输入命令编译源代码,生成demo可执行文件,运行可执行文件,如图23-3
图23-3 libnet在linux中安装
3、抓包分析
利用wireshark进行抓包如图23-4
图23-4 libnet的抓包结果
可以看出成功的用libnet构造了一个UDP的数据包。