Linux 网络编程—— libnet库详细使用指南
概述
通过《原始套接字实例:发送 UDP 数据包》的学习,我们组 UDP 数据包时常考虑字节流顺序、校验和计算等问题,有时候会比较繁琐,那么,有没有一种更简单的方法呢?答案是:借助 libnet 函数库。
libnet 是一个小型的接口函数库,主要用 C 语言写成,提供了低层网络数据包的构造、处理和发送功能。
libnet 的开发目的是:建立一个简单统一的网络编程接口以屏蔽不同操作系统底层网络编程的差别,使得程序员将精力集中在解决关键问题上。
libnet 库提供的接口函数包含 15 种数据包生成器和两种数据包发送器(IP 层和数据链路层)。
提供的接口函数包括:
1)内存管理(分配和释放)函数
2)地址解析函数
3)各种协议类型的数据包构造函数
4)数据包发送函数(IP层和链路层)
5)一些辅助函数,如产生随机数、错误报告、端口列表管理等
详情请看官方使用手册。
libnet 的安装
流程
利用libnet函数库开发应用程序的基本步骤:
1)数据包内存初始化
2)构造数据包
3)发送数据
4)释放资源
以发送 UDP 数据包为例,流程图如下:
这里需要注意的是组包的顺序,由上层再到底层,这里为 udp -> ip -> mac,不能反过来。
常用函数介绍
以下函数的使用需要包含头文件: libnet.h
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
void libnet_destroy(libnet_t *l);
功能:
释放资源
参数:
l:libnet_init() 返回的 libnet * 指针
返回值:
无
char* libnet_addr2name4(u_int32_t in, u_int8_t use_name);
功能:
将网络字节序转换成点分十进制数串
参数:
in:网络字节序的 ip 地址
use_name:
LIBNET_RESOLVE, 对应主机名
LIBNET_DONT_RESOLVE,对应点分十进制 IPv4 地址
返回值:
成功:点分十进制 ip 地址
失败:NULL
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
u_int32_t libnet_get_ipaddr4(libnet_t *l);
功能:
获取接口设备 ip 地址
参数:
l:libnet_init() 返回的 libnet * 指针
返回值:
成功:网络字节序的 ip 地址
失败:-1
struct libnet_ether_addr* libnet_get_hwaddr(libnet_t *l);
功能:
获取接口设备硬件地址
参数:
l:libnet_init() 返回的 libnet * 指针
返回值:
成功:指向 MAC 地址的指针
失败:NULL
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
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 win
u_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
libnet_ptag_t libnet_build_tcp_options(
u_int8_t *options,
u_int32_t options_s,
libnet_t *l,
libnet_ptag_t ptag );
功能:
构造 tcp 选项数据包
参数:
options:tcp 选项字符串
options_s:选项长度
l:libnet 句柄,libnet_init() 返回的 libnet * 指针
ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
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
libnet_ptag_t libnet_build_ipv4_options(
u_int8_t*options, u_int32_t options,
libnet_t*l, libnet_ptag_t ptag);
功能:
构造 IPv4 选项数据包
参数:
options:tcp 选项字符串
options_s:选项长度
l:libnet 句柄,libnet_init() 返回的 libnet * 指针
ptag:协议标记,若为 0,建立一个新的协议
返回值:
成功:协议标记
失败:-1
libnet_ptag_t libnet_build_arp(
u_int16_t hrd, u_int16_t pro,
u_int8_t hln, u_int8_t pln,
u_int16_t op, u_int8_t *sha,
u_int8_t *spa, u_int8_t *tha,
u_int8_t *tpa, u_int8_t *payload,
u_int32_t payload_s, libnet_t *l,
libnet_ptag_t ptag );
功能:
构造 arp 数据包
参数:
hrd:硬件地址格式,ARPHRD_ETHER(以太网)
pro:协议地址格式,ETHERTYPE_IP( IP协议)
hln:硬件地址长度
pln:协议地址长度
op:ARP协议操作类型(1:ARP请求,2:ARP回应,3:RARP请求,4:RARP回应)
sha:发送者硬件地址
spa:发送者协议地址
tha:目标硬件地址
tpa:目标协议地址
payload:负载,可设置为 NULL(这里通常写 NULL)
payload_s:负载长度,或为 0(这里通常写 0 )
l:libnet 句柄,libnet_init() 返回的 libnet * 指针
ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。
返回值:
成功:协议标记
失败:-1
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
int libnet_write(libnet_t * l);
功能:
发送数据包
参数:
l:libnet 句柄,libnet_init() 返回的 libnet * 指针
返回值:
成功:发送数据包的长度
失败:返回 -1
使用实例
这里是在 ubuntu 下通过原始套接字组一个 udp 数据包,给 PC 机的网络调试助手发送信息(对比:《原始套接字实例:发送 UDP 数据包》):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libnet.h>
int main(int argc, char *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, "eth0", 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, // 或者,ETHERTYPE_IP
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:
---------------------
作者:Mike__Jiang
来源:CSDN
原文:https://blog.csdn.net/tennysonsky/article/details/44944849
版权声明:本文为博主原创文章,转载请附上博文链接!
Linux 网络编程—— libnet库详细使用指南相关推荐
- libnet编译linux,Linux 网络编程—— libnet 使用指南
使用实例 这里是在 Ubuntu 下通过原始套接字组一个 udp 数据包,给 PC 机的网络调试助手发送信息(对比:<原始套接字实例:发送 UDP 数据包>): #include #inc ...
- linux网络编程之广播详细代码及文档说明 -,Linux网络编程之广播
1.概念 前面介绍的TCP,UDP都是单播方式,即一对一.而广播是一台主机向局域网内的所有主机发送数据.这时,同一网段的所有主机都能接收到数据.发送广播包的步骤大致如下: (1)确定一个发送广播的接口 ...
- Linux网络编程——千峰物联网笔记
B站视频:千峰物联网学科linux网络编程 网址:https://www.bilibili.com/video/BV1RJ411B761?p=1 目录 第一章:计算机网络概述 1.1计算机网络发展简史 ...
- Linux网络编程必看书籍推荐
首先要说讲述计算机网络和TCP/IP的书很多. 先要学习网络知识才谈得上编程 讲述计算机网络的最经典的当属Andrew S.Tanenbaum的<计算机网络>第五版,这本书难易适中. &l ...
- 【Linux】Linux网络编程(含常见服务器模型,上篇)
基本数据结构介绍 Linux系统是通过提供嵌套字(socket)来进行网络编程的.网络程序通过socket和其他几个函数的调用,会返回一个通用的文件描述符,用户可以将这个描述符看成普通的文件的描述符来 ...
- Linux网络编程之套接字基础
Linux网络编程之套接字基础 1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_ ...
- linux网络编程常用函数详解与实例(socket--bind--listen--accept)
常用的网络命令: netstat 命令netstat是用来显示网络的连接,路由表和接口统计等网络的信息.netstat有许多的选项我们常用的选项是 -an 用来显示详细的网络状态.至于其它的选项我们可 ...
- 【Linux】一步一步学Linux网络编程教程汇总(更新中......)
00. 目录 文章目录 00. 目录 01. 基础理论知识 02. 初级编程 03. 高级编程 04. LibEvent库 05. 06. 07. 01. 基础理论知识 [Linux网络编程]网络协议 ...
- 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系
00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...
最新文章
- 用了 10 年 Windows 后,我最终转向 Linux
- Windows下电脑硬盘的日常维护细则
- 容易忽视的十大SQL优化方案!
- Render errors:One or more layouts are missing the layout_width or layout_height attributes
- Navicat 连接MariaDB 失败: Host '*' is not allowed to connect to this MariaDB server
- IDEA解决sun.misc.BASE64Encoder找不到jar包的解决方法
- python列表操作程序_Python入门篇(三)之列表
- Qt工作笔记-3D效果唤出QWidgets界面(QGraphicsProxyWidget与QTimeLine)
- html图片滚动浏览,ul结合CSS制作网页相册滑动浏览效果
- maven私服搭建nexus
- 解决intellij idea卡顿的方法
- 京瓷Kyocera TASKalfa 3051ci 一体机驱动
- C语言----整钱换零钱问题。把1元兑换成1分,2分,5分的硬币,共有多少种不同的兑换方法?(同理n元硬币的兑换方法呢?n元由用户输入)
- ADS129X芯片中文资料(二)——模拟功能部分介绍
- kindle上查看微信读书的书籍
- dubbo官网最新版导航
- 利用免编程应用平台快速制作App
- (转)马邑之战与项目管理
- 2022/12/4 胡言乱语
- spring源码深度解析 第2版 pdf_吹爆!阿里爆款Spring源码高级笔记,原来看懂源码如此简单...