Linux 网络编程—— libnet库详细使用指南

2023-05-16

概述
通过《原始套接字实例:发送 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 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 网络编程—— libnet库详细使用指南 的相关文章

  • ESP32控制TDC-GP22测量超声传播时间(超声流量计)

    TDC GP22控制的资料 xff0c 网上的资源都是基于STM32或者MSP430主控的 xff0c 但现在这两款芯片都太贵了 xff0c 因此就想用便宜点的ESP32 xff0c 折腾了快一周 xff0c 终于弄好了 工程源码和参考资料
  • WIN10远程连接WIN10报错:你的凭据不工作

    很多用户在升级到win10系统之后 xff0c 连接远程桌面功能的时候遇到这样一个错误 xff0c 就是远程桌面提示 你的凭据不工作 xff0c 导致无法连接远程桌面 xff0c 该怎么解决呢 xff0c 下面为大家讲解一下Win10无法连
  • 一个分号引发的血案

    最近在看 Data Structures and Algorithms 这本书 xff0c 书里对数据结构和算法进行了简单的描述 xff0c 并且用伪码进行了实现 伪码毕竟和代码还是有很大不同的 xff0c 书的说明里说有C 写的源码 xf
  • Iphone客户端程序员半年工作总结

    来公司四个半月了 xff0c 从对客户端游戏编程的小白慢慢的也能写一些东西了 xff0c 当然了这里最感谢的人就是九天了 xff0c 对于九天其它的好我就不说了 xff0c 就是感觉九天为了团队 xff0c 为了项目 xff0c 他在很用心
  • Unity Android Activity控制

    前言 开发游戏 xff0c 在国内发行 xff0c 接入各个渠道SDK是一件绕不开的事情 并且这件事非常复杂琐碎 xff0c 原因如下 xff1a a 发行平台多 xff0c Android平台有30 xff0c 40家主流发行平台 b 每
  • Unity Android 加载动态库

    前言 在接入360 Android SDK时遇到在有些机型的Android机器上报错 xff0c 具体错误提示为 xff1a Failure to initialize Your hardware does not support this
  • 德鲁克谈《自我管理》笔记摘要

    一 我的长处是什么 多数人都以为他们知道自己擅长什么 xff0c 其实不然 要发现自己的长处 xff0c 唯一的途径就是分析回馈法 写下自己预期的结果 9 12个月后 xff0c 将实际结果和预期比较 总结改善自己的预期和行动 我们要以持之
  • ubuntu下搭建cocos2dx编程环境-上

    这大半年一直在开发flash游戏 xff0c 用到的编程语言是actionscript和c 43 43 所以这次公司决定开发手游端的话 xff0c C 43 43 不是很生疏 xff0c 这是个好消息 坏消息是由于现在网页游戏还没有上线 x
  • ubuntu 系统上安装python3.6

    今天再ubuntu系统上安装python3 6 xff0c 之前版本是python 2 7版本 具体安装步骤如下 xff1a 1 sudo apt get update 2 sudo apt get install software pro
  • 用matlab实现图片的缩放

    i 61 imread 39 qiegray jpg 39 j 61 imresize i 0 5 imshow i figure imshow j
  • VR系统的组成与交互技术汇总

    今天给大家介绍一下VR系统的组成与当前一些VR交互技术 xff0c 希望大家对VR有更深的理解 一个典型的虚拟现实系统主要由计算机 输入输出设备 虚拟现实设计 浏览软件等组成 用户以计算机为核心 xff0c 通过输入输出设备与应用软件的虚拟
  • 百度云python客户端bypy的上传命令(upload)

    命令 xff1a bypy upload localfile cloudfile 说明 xff1a 1 xff09 localfile为本地文件路径 2 xff09 cloudfile为云端文件路径 3 xff09 由于百度的限制 xff0
  • 【Github】git bash无法复制粘贴

    参考教程https www cnblogs com slw0113 p 15602715 html 使用git bash时只能使用手工敲击所有命令 xff0c 非常不方便 1 在屏幕右键打开git bash命令窗口 2 在左上角右键 xff
  • python编码与反编码 decode('unicode-escape')

    反编码 我自己起的名字 xff0c 大概意思就是我得到一串字符 xff0c 是unicode码 xff0c 如 xff1a u53eb u6211 xff0c 进行反编码后得到其对应的汉字 f 61 39 u53eb u6211 39 pr
  • (11)jsp+servlet编程实战《猜数字游戏》

    使用JSP技术实现猜数字游戏 设计一个web app xff0c 每次产生一个30以内的数字 xff0c 给5次机会让客户猜测这个数字 xff1a 1 xff09 如果客户猜的数字比产生的数字值大 xff0c 则提示 可惜 xff0c 大了
  • Spark2.0 shell使用python3

    spark2 0 shell默认使用的是python2 xff0c 如果想用python3作为默认shell xff0c 那么只需要修改 spark home bin pyspark文件下的配置即可 span class token com
  • 五分钟教你搭建 Typecho个人博客

    五分钟教你搭建 Typecho 个人博客 简介 本教程将会教会你如何使用阿里云服务器添加一条A记录 xff0c 部署博客程序源码 xff0c 最后搭建一个属于自己的博客 xff0c 新手小白必备 xff01 关于 Typecho 仅仅 7
  • Typora 设置上传图片功能

    Typora 设置上传图片功能 准备工作 安装 Picgo安装 Typora配置好自己的图床 以上步骤可参考我以前的文章 如何使用 picgo 搭建个人图床 Typora 设置自动上传插入图片功能 xff0c 实现 61 61 截屏 xff
  • 为什么要用到二进制?

    进制及其转换 为什么要用到二进制 xff1f 数字电路非常广泛 xff0c 小到逻辑门电路 xff0c 大到超大规模集成电路 xff0c 手机 xff0c 电脑 xff0c 智能设备 数字信号即可以表示数字 xff0c 也可以表示非数值的信
  • Github student package 申请过程

    Github学生包优惠 验证教育邮箱 进入官网 填写资料 填写材料时 xff0c 务必如实填写 最好添加一个教育邮箱 xff0c 也就是将你的教育邮箱绑定giehub账号 xff0c 如第一张图所示 填写资料的中途可能要上传附件 xff0c

随机推荐