Raw Socket 之网络层数据获取

news2025/1/4 19:49:38

目录

  • 简介
  • 正常使用
  • tcpdump
  • 程序与分析
  • 报文
  • 理解
  • 参考

简介

针对网络包,我们一般的发送接收直接使用的是应用层,此时无法分辨接收为广播包还是单播包,为了能够分辨出接收到的是否为广播包,需要接收数据链路层的数据或者网络层的数据。

原理啥的就不复制了,可看参考链接,也可自行搜索。。。------ 》》》

直接上正题

正常使用

我们正常的 udp 通信如下:

	int fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
	struct sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = INADDR_ANY;
	bind(fd, (struct sockaddr *) &sin, sizeof(sin));

tcpdump

需要获取网络层数据,需要使用抓包过滤规则

setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));

bpf 为定义在 <linux/filter.h>

struct sock_filter {    /* Filter block */
        __u16   code;   /* Actual filter code */
        __u8    jt;     /* Jump true */
        __u8    jf;     /* Jump false */
        __u32   k;      /* Generic multiuse field */
};

struct sock_fprog {                     /* Required for SO_ATTACH_FILTER. */
        unsigned short             len; /* Number of filter blocks */
        struct sock_filter __user *filter;
};

生成过滤规则可使用 tcpdump 命令,如下:

$ sudo tcpdump udp and dst  port 8888  -d
(000) ldh      [12]                             //接收包的第12字节处,16位
(001) jeq      #0x86dd          jt 2	jf 6    //判断第12字节处是否等于0x86dd,满足跳到(002),不满足跳到(006)行
(002) ldb      [20]
(003) jeq      #0x11            jt 4	jf 15
(004) ldh      [56]
(005) jeq      #0x22b8          jt 14	jf 15
(006) jeq      #0x800           jt 7	jf 15  //判断第12字节处是否等于0x800,满足跳到(007),不满足跳到(015)行
(007) ldb      [23]							//接收包的第23字节处,8位
(008) jeq      #0x11            jt 9	jf 15 //判断第23字节处是否等于0x11,满足跳到(009),不满足跳到(015)行
(009) ldh      [20]							//接收包的第20字节处,16位
(010) jset     #0x1fff          jt 15	jf 11
(011) ldxb     4*([14]&0xf)	       //接收包的第14字节处,8位,取低四位,然后乘4,抓包看此处一般为 0x45,计算得 20
(012) ldh      [x + 16]           //接收包的第x+16字节处,16位,x在上面计算得20,故为第36字节处
(013) jeq      #0x22b8          jt 14	jf 15  //判断第36字节处是否等于0x22b8,满足跳到(014),不满足跳到(015)行
(014) ret      #262144            //满足,显示
(015) ret      #0                 //不满足,过滤

在这里插入图片描述

在这里插入图片描述

程序与分析

上面生成的主要是数据链路层起始数据,而区分广播包,获取网络层数据即可,上面生成过于复杂,可简化判断接收数据端口号,再加上网络层第一二字节判断

{ 0x28, 0, 0, 0x00000000 },
{ 0x15, 0, 3, 0x00004500 },
{ 0x28, 0, 0, 0x0000016 },
{ 0x15, 0, 1, 0x000022B8 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },

在使用的过程中,发现:若只使用 SOCK_RAW 获取网络层包,则会出现端口不可达
ICMP Destination unreachable (Port unreachable)

在这里插入图片描述

故而推测 socket(AF_INET, SOCK_RAW|SOCK_NONBLOCK, IPPROTO_UDP ) 使用时,不会打开端口,但数据收发也都正常,就是多了一条 ICMP,故而本文做法,打开所在端口,就不会出现端口不可达了,SOCK_RAW主要用于报文的抓取,在内核获取到报文后,会复制一份给 SOCK_RAW 套接字,另一个打开所在端口也可正常接收,例子如下:

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include<memory.h>
#include<stdlib.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h> // sockaddr_ll
#include<arpa/inet.h>
#include<netinet/if_ether.h>
#include<errno.h>
#include <linux/filter.h>

int main(int argc, char *argv[])
{
	//内核会把数据给每一个socket拷贝一份
	uint8_t buf[1024] = { 0 };
	uint8_t buf2[1024] = { 0 };
	int len = 0,len2 = 0;
	int fd, fd2;
	struct sockaddr_in client_addr, client_addr2;
	socklen_t addrlen = sizeof(client_addr);
	//fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));

	fd = socket(AF_INET, SOCK_RAW|SOCK_NONBLOCK, IPPROTO_UDP );
    struct sock_filter bpf_code[] = {
        { 0x28, 0, 0, 0x00000000 },
        { 0x15, 0, 3, 0x00004500 },
        { 0x28, 0, 0, 0x0000016 },
        { 0x15, 0, 1, 0x000022B8 },
        { 0x6, 0, 0, 0x00040000 },
        { 0x6, 0, 0, 0x00000000 },
    };
    struct sock_fprog bpf;
    memset(&bpf,0x00,sizeof(bpf));

    bpf.len = sizeof(bpf_code) / sizeof(struct sock_filter);

    bpf.filter = bpf_code;

    int ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));

    if (ret < 0) {
		printf("setsockopt:SO_ATTACH_FILTER>>>>error:%s\n",strerror(errno));
    }
	/*
	如果IP_HDRINCL未开启,由进程让内核发送的数据是从IP首部之后的第一个字节开始的,内核会自动构造合适的IP
	如果IP_HDRINGL开启,进行需要自行构造IP包
	*/
	/*
	int  one = 1;
	const int *val = &one;
	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int))) {
		perror("setsockopt() error");
		exit(-1);
	}
	*/
	
	fd2 = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_UDP);
	struct sockaddr_in addrServ;
	addrServ.sin_addr.s_addr= htonl(0);//指定0.0.0.0地址,表示任意地址0xC0A8012e
	addrServ.sin_family = AF_INET;//表示IPv4的套接字类型
	addrServ.sin_port = htons(8888);
	bind(fd2, (struct sockaddr*)&addrServ, sizeof(addrServ));
	
	while (1) {
		if ((len = recvfrom(fd, buf, sizeof(buf), 0,(struct sockaddr *)&client_addr, &addrlen)) == -1){
			//printf("recvfrom failed ! error message : %s\n", strerror(errno));
			continue;
		}

		if ((len2 = recvfrom(fd2, buf2, sizeof(buf2), 0, (struct sockaddr *)&client_addr2, &addrlen)) == -1){
			printf("recvfrom failed ! error message : %d %s\n", errno, strerror(errno));
		}
		if(len){
			printf("%s port: %d, len: %d, data: %x %x %x %x \n",inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port),  len, buf[0], buf[28], buf[29], buf[30]);
		}
		if(len2){
			printf("%s port: %d, len: %d, data: %x %x %x %x \n",inet_ntoa(client_addr2.sin_addr), htons(client_addr2.sin_port), len2, buf2[0], buf2[1], buf2[2], buf2[3]);
		}
		if(len){
			buf[0]=0x22;
			buf[1]=0xB8;
			buf[2]=0xaf;
			buf[3]=0x13;
			buf[4]=0x0;
			buf[5]=0x22;
			sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, addrlen);
		}
	}
	return 0;
}

上面程序的接收和发送,都是使用的 Raw Socket 的方式进行,其下为数据收发情况

  • 发送->接收
    在这里插入图片描述
    发送了 20个字节,收到 40个字节

  • 接收->发送
    在这里插入图片描述

其上打印,Raw Socket 的方式接收了 48 字节,首字节从0x45开始,其后包含了源 IP、目的 IP 和端口号等,正常数据位于第28字节处;
而正常的 UDP 模式下接收了 20 字节,开始即是 0x68

报文

在这里插入图片描述

Raw Socket 的方式发送从端口号开始, 可自行组包,源端口,目的端口,数据长度等,其正常数据从上面的调试助手看,从第 8 字节开始
在这里插入图片描述

理解

Raw Socket 比较适用于抓包分析,由于打开套接字不会打开对应端口,故使用时为去除 ICMP 包,需打开正常的端口号,发送可使用正常数据包,无需自行组包。

使用 Raw Socket 方式可解决:区分接收到的是广播数据还是单播数据 这一简单应用。

参考

https://github.com/xgfone/snippet/blob/master/snippet/docs/linux/program/raw-socket.md
http://qiusuoge.com/17205.html
https://www.kernel.org/doc/html/latest/networking/filter.html#networking-filter

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/717528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ct.js笔记-加载字体时字体名不能包含中文

这个和Qt很像&#xff0c;在开发过程中尽量避免使用中文。如下&#xff1a; 这个AaJH中文&#xff0c;在加载时&#xff1a; this.nickNameLabel new PIXI.Text(Your text here, ct.styles.get(AaJH中文)); this.addChild(this.nickNameLabel);this.nickNameLabel.x 30; thi…

UE5接入在线直播视频源,如hls(m3u8)格式

文章目录 1.实现目标2.实现过程2.1 VlcMedia插件重编译2.2 UE5接入在线直播2.3 创建材质3.参考资料1.实现目标 通过重编译VlcMedia插件,以支持在线直播视频在UE5中的播放,GIF动图如下: 2.实现过程 本文主要包括插件的重编译、在线直播视频的接入,以及材质的创建三个部分。…

基于Java办公用品管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

atomic原子操作

当一个程序中的两个线程同时向这个程序里的全局变量增加数时会发生什么&#xff1f; 理论上来说&#xff0c;应该是200000&#xff0c;但是最后结果不是200000。 这是多个线程同时操作一个全局变量引起的错误。 要解决这个错误&#xff0c;用互斥锁即可&#xff1a; 这样就可以…

【计算机视觉】DETR 系列的最新综述!

论文地址&#xff1a; https://arxiv.org/pdf/2306.04670.pdf项目地址&#xff1a; https://github.com/mindgarage-shan/trans_object_detection_surveyTransformer在自然语言处理(NLP)中的惊人表现&#xff0c;让研究人员很兴奋地探索它们在计算机视觉任务中的应用。与其他…

Spark | 性能调优原理与步骤 | 案例讲解

&#x1f525; 跟着梁哥打卡一波spark的性能调优&#xff5e; 文章目录 1、Spark调优原理与步骤2、Spark任务UI监控3、Spark调优案例3.1、资源配置优化3.2、利用缓存减少重复计算3.3、数据倾斜调优3.4、broadcastmap代替join3.5、reduceByKey/aggregateByKey代替groupByKey 1、…

为什么门店需要店务管理系统?店务管理系统的重要性有哪些?

随着经济市场的回暖&#xff0c;实体行业特别是门店都需要更好的营销手段以留住客户、转化客户&#xff0c;因此需要一个好的工具来完成对于店铺经营数据的实时了解和管理&#xff0c;这就需要用到店务经营管理系统。如蚓链数字化店务管理系统具备股东营销系统、收银系统及进销…

Linux5.12 rsync 远程同步

文章目录 计算机系统5G云计算第四章 LINUX rsync 远程同步一、rsync概述二、配置 rsync 服务1.配置rsync源服务器2.发起端 三、发起端配置 rsyncinotify1.修改rsync源服务器配置文件2.调整 inotify 内核参数3.安装 inotify-tools4.在另外一个终端编写触发式同步脚本 计算机系统…

7.4.2 【Linux】特殊设备 loop 挂载 (镜像文件不烧录就挂载使用)

挂载光盘/DVD镜像文件 如此一来我们不需要将这个文件烧录成为光盘或者是 DVD 就能够读取内部的数据了。 创建大文件以制作 loop 设备文件&#xff01; 创建大型文件 假设我要创建一个空的文件在 /srv/loopdev &#xff0c;那可以这样做&#xff1a; 将 512 块&#xff0c;每…

爱普生L358打印机墨仓有墨水但墨水指示灯常亮

产品类型&#xff1a;墨仓式多功能一体机 最大处理幅面&#xff1a;A4 涵盖功能&#xff1a;打印/复印/扫描 黑白打印速度&#xff1a;9ipm 耗材类型&#xff1a;分体式墨盒 打印分辨率&#xff1a;57601440dpi 问题描述&#xff1a; 爱普生L358彩…

RK3399平台开发系列讲解(入门篇)为什么需要学习Linux驱动

🚀返回专栏总目录 文章目录 一、为什么需要学习Linux驱动二、如何学习Linux驱动开发三、Linux 驱动学习的难点四、学习资料沉淀、分享、成长,让自己和他人都能有所收获!😄 一、为什么需要学习Linux驱动 学习Linux驱动开发有以下几个重要的原因: 深入理解系统底层:学习L…

【CSS】定位

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;HTMLCSS &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 标准流&#xff08;Normal Flow&#xff09;元素定位position属性静态定位-static…

(免费分享)基于springboot,vue在线小说系统

本系统功能包括&#xff1a; 普通用户端登录注册&#xff0c;小说的分类&#xff0c;日榜&#xff0c;月榜&#xff0c;年榜&#xff0c; 小说的阅读&#xff0c;分章节&#xff0c;小说的评论&#xff0c;收藏&#xff0c;推荐等等&#xff0c;以 及后台小说的维护&#xff0c…

Linux服务器扩容VG时报错 Couldn‘t create temporary archive name

今天扩容磁盘遇到失败报错。 [rootmysql ~]# vgextend rhel /dev/sdc1 Couldnt create temporary archive name. 原因&#xff1a;磁盘使用100%&#xff0c;无法执行挂载&#xff0c;须预留部分空间出来。解决办法&#xff1a;删掉其中无用文件、log日志继续操作即可。释放空间…

SpringBoot+Vue实现文件上传功能

目录 1.后端代码部分&#xff1a; 2.前端代码部分 3.效果展示 1.后端代码部分&#xff1a; RestController RequestMapping("/file") public class FileController {private final String UPLOAD_PATH "D:/OBS/";//这里写上你需要上传的路径&#xff…

对于大连企业而言如何提升网站的曝光率

对于大连企业而言&#xff0c;提升网站的曝光率是非常重要的&#xff0c;可以通过以下几种方式来实现&#xff1a; 1. 优化网站结构和内容&#xff1a;确保网站的结构清晰&#xff0c;布局合理&#xff0c;并且内容丰富、有吸引力。网站的页面加载速度也要快&#xff0c;以提升…

leetcode 110. 平衡二叉树

2023.7.4 平衡二叉树 &#xff0c;经典题目 。需要利用到之前求二叉树高度的思路。 先定义一个求高度的函数&#xff0c;然后使用递归的方式判断左子树和右子树的高度差是否小于等于1&#xff0c;下面上代码&#xff1a; class Solution { public:int get_length(TreeNode* cu…

C语言学习(三十二)---动态内存(一)

在昨天的内容中&#xff0c;我们学习有关结构体和联合体在内存中存储的相关问题&#xff0c;由于粗心&#xff0c;有一个代码重复了&#xff0c;感谢这位朋友的提醒&#xff1a; 现在已经对内容进行了修改&#xff0c;感谢朋友们的支持。 今天我们将学习有关动态内存的相关问…

Android Studio 和 Android Gradle 插件的已知问题

Android Studio 的已知问题 渲染 Compose 预览时出错 从 Android Studio Chipmunk 开始&#xff0c;如果您在问题面板中看到 java.lang.NoSuchFieldError: view_tree_saved_state_registry_owner 或 java.lang.ClassNotFoundException: androidx.savedstate.R$id&#xff0c;…

大势速影:机载激光快速建模测绘装备

实景三维模型高逼真、高真实、高精度的展示地物的几何结构、纹理色彩、空间位置等信息&#xff0c;在当前测绘应急保障、规划等领域具备极大的应用价值。但是&#xff0c;激光雷达和倾斜摄影建模技术均无法较好的满足快速建模应用需求&#xff0c;具体表现在&#xff1a;激光点…