原始套接字编程(AF_PACKET+SOCK_RAW)模拟一个PING

news2024/10/6 0:26:49

1. 背景

最近看一个客户的代码片段,发现他在用原始套接字编程,一般学习套接字都是流式套接字和数据报套接字,本来也不是搞网络的,原始套接字了解得很少,借着这次机会,自己来学习一下原始套接字编程。

2. 原始套接字是什么

我去翻看<Linux_Unix系统编程手册>第58章(TCP/IP网络基础)中只有一个泛泛的介绍:

 

 但是这个描述并不完全,于是我参考: 信息安全课程9:raw socket编程 - 知乎

另外还了解到原始套接字在socket的创建上有不同的组合,例如: AF_INET+SOCK_RAW最多只能允许用户层与IP层直接通信,而AF_PACKET+SOCK_RAW就可以允许用户层与数据链路层直接通信了(这一点也是Linux_Unix系统编程手册说得不准确的地方)

 

 另外,关于AF_PACKET+SOCK_RAW可以参考man packet:

3. 封装与PING包格式

同样参考 <Linux_Unix系统编程手册>中的封装基本概念:

 我自己用wireshark抓了一个ping包的格式如下:

上图是执行ping 192.168.0.103 -c 1抓的包,可以看到一个ping操作实际分为两个包,一个是由本机发出的包(echo ping request),另一个是收到对端发来的ack包(echo ping reply),不论怎样,这两个包组成都是相同的: 

98byte = EthernetII(以太网头部14byte)+Internet Protocol Version4(IP包20byte)+ICMP(64byte)

4. 实验代码

实验代码实际上是受到下面博客的启发:Linux 网络编程——原始套接字实例:MAC 地址扫描器_siocgifhwaddr_Mike江的博客-CSDN博客

在这篇博客中,作者用AF_PACKET+SOCK_RAW的原始套接字在数据链路层模拟了一个地址解析协议的操作(Address Resolution Protocol),其中作者没有使用繁杂的包数据结构去构造发送数据,转而使用了直接赋值的方式,非常直观与暴力,可以对着wireshark的数据来构造自己的数据包,非常便于理解与学习,所以我自己模仿了一个PING操作,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <net/if.h>           //struct ifreq
#include <sys/ioctl.h>        //ioctl、SIOCGIFADDR
#include <sys/socket.h>
#include <netinet/ether.h>    //ETH_P_ALL
#include <netpacket/packet.h> //struct sockaddr_ll
#include <netinet/in.h>
#include <netinet/ip_icmp.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFFSIZE (1024)
#define IPPACKETLEN (20)
#define ICMPHEADERLEN (16)
#define ICMPDATALEN (48)
#define PINGPACKETLEN (98)

//#define DEBUG

static void test(void)
{
    unsigned short a = 0x3623; /* in memory should be 0x23 0x36 */
    unsigned short b = htons(a);
    printf("network order b = %#x\n", b); /* in memory should be 0x36 0x23 */
    printf("local order b' = %#x\n", ntohs(b));
    return;
}

/* 2 bytes was a group
 * totally lens group
 */
static unsigned short calc_checksum(unsigned short *addr, int len)
{
    int i;
    unsigned int sum = 0;
    unsigned short tmp = 0;

    /* in this algorithm,
     * we use ntohs to convert the already constructed big endian data
     */
    for (i = 0; i < len; i++) {
        tmp = ntohs(*addr);
        sum += tmp;
        addr++;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >>16);
    tmp = ~sum;

    return tmp;
}

int main(int argc,char *argv[])
{
    int i = 1;

#ifdef DEBUG
    test();
    return 0;
#endif

    unsigned char send_msg[BUFFSIZE] = {
        //--------------Ethernet II------------------------14--------------------------
        0xf8, 0x94, 0xc2, 0xdb, 0x72, 0x43, //dst_mac: F8-94-C2-DB-72-43
        0x00, 0x0c, 0x29, 0x7e, 0x2f, 0x86, //src_mac: 00-0C-29-7E-2F-86
        0x08, 0x00,                         //type: 0x0800 IPV4

        //--------------Internet Protocol Version 4--------20--------------------------
        0x45,                               //0100-version4, 0101-5*4byte=20byte
        0x00,                               //DSCP:0, ECN-Not-ECT
        0x00, 0x54,                         //total length: 0x54==84==20(IP)+64(ICMP)
        0x36, 0x23,                         //identification: 0x36 0x23
        0x40, 0x00,                         //flags: 0x4000, do not fragment
        0x40,                               //time to live(TTL): 64
        0x01,                               //protocol: ICMP(1)
        0x00, 0x00,                         //checksum(need to calc later)
        192, 168, 0, 104,                   //src ip addr
        192, 168, 0, 103,                   //dst ip addr
        //--------------ICMP-Header------------------------16--------------------------
        0x08,                               //type: 8(echo ping request)
        0x00,                               //code:0
        0x00, 0x00,                         //checksum(need to calc later)
        0x20, 0x09,                         //identifier: 0x20 0x09
        0x00, 0x00,                         //sequence number
        0x12, 0xfc, 0xd4, 0x64,
        0x00, 0x00, 0x00, 0x00,             //timestamp from icmp data
        //--------------ICMP-Data--------------------------48--------------------------
        0x12, 0x34, 0x56, 0x78,
        0x12, 0x34, 0x56, 0x78,
        0x12, 0x34, 0x56, 0x78,
        0x12, 0x34, 0x56, 0x78,
    };

    unsigned short checksum = 0;
    unsigned short checksum_be = 0;

    /* handle checksum of IP packet */
    checksum = calc_checksum((unsigned short *)(&send_msg[14]), IPPACKETLEN/2);
    printf("IP packet checksum = %#x\n", checksum);

    checksum_be = htons(checksum);
    memcpy(&send_msg[24], &checksum_be, sizeof(checksum_be));

    /* handle checksum of ICMP packet */
    checksum = calc_checksum((unsigned short *)(&send_msg[34]), (ICMPHEADERLEN + ICMPDATALEN)/2);
    printf("ICMP packet checksum = %#x\n", checksum);

    checksum_be = htons(checksum);
    memcpy(&send_msg[36], &checksum_be, sizeof(checksum_be));

    /* socket create failed, why: Operation not permitted
     * should have root permission to create the socket raw
     */
    int sock_raw_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock_raw_fd < 0) {
        printf("socket create failed, why: %s\n", strerror(errno));
        return -1;
    }
    printf("socket create success.\n");

    struct sockaddr_ll sll;
    struct ifreq req;
    strncpy(req.ifr_name, "ens33", IFNAMSIZ);

    ioctl(sock_raw_fd, SIOCGIFINDEX, &req);
    bzero(&sll, sizeof(sll));
    sll.sll_ifindex = req.ifr_ifindex;

    int len = sendto(sock_raw_fd, send_msg, PINGPACKETLEN, 0 , (struct sockaddr *)&sll, sizeof(sll));
    if (len == -1) {
        printf("sendto failed, why: %s\n", strerror(errno));
        close(sock_raw_fd);
        return -1;
    }
    printf("\n%d bytes sended.\n", len);

    unsigned char recv_msg[BUFFSIZE] = {0};
    len = recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);
    if (len < 0) {
        printf("recvfrom failed, why: %s\n", strerror(errno));
        close(sock_raw_fd);
        return -1;
    }
    printf("\n%d bytes received, check the wireshark.\n", len);

    close(sock_raw_fd);

    return 0;
}

需要注意的是send_msg的数据全部是依据wireshark包来构造的,自己机器的ip和mac,对端机器的ip和mac需要自行适配,另外,IP包和ICMP包是有 checksum需要计算的,一个偷懒的办法是先胡乱写一个checksum进去,发包的时候wireshark会识别到错误并告诉你正确的checksum是什么,再回填进去即可,通常,IP包的checksum wireshark不检查(显示validation disabled),但是如果构造不对的话是收不到echo ping reply包的,可以按照下图的方法打开wireshark IP包的checksum检查结果:

 不过话说回来,我们应该搞清楚checksum的计算原理,参考:

IP数据报首部checksum的计算_ipchecksum计算_Allen_Kao的博客-CSDN博客

另外注意的是由于我们暴力构造数据已经是网络字节序了,所以计算的时候有必要用htons和ntohs转换一下,参考代码中的calc_checksum()函数。

5. 实验结果

本机X86 ubuntu的ip是192.168.0.104,mac是00:0c:29:7e:2f:86

PC的ip是192.168.0.103,mac是f8:94:c2:db:72:43

因为原始套接字权限要求,必须给与权限运行:

wireshark抓包:

 

 

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

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

相关文章

函数的模拟实现

题一&#xff1a; 模拟实现strncpy #include <stdio.h>void my_strncpy(char* arr2, char* arr1, size_t num){int i 0;for (i 0; i < num; i){*(arr2 i) *(arr1 i);}}int main(){char arr1[] "hello liangzai";char arr2[10] { 0 };//strncpy(ar…

包管理工具详解npm 、 yarn 、 cnpm 、 npx 、 pnpm(2023)

1、包管理工具npm &#xff08;1&#xff09;包管理工具npm&#xff1a; Node Package Manager&#xff0c;也就是Node包管理器&#xff1b;但是目前已经不仅仅是Node包管理器了&#xff0c;在前端项目中我们也在使用它来管理依赖的包&#xff1b;比如vue、vue-router、vuex、…

3.6 Spring MVC文件上传

1. 文件上传到本地 实现方式 Spring MVC使用commons-fileupload实现文件上传&#xff0c;注意事项如下&#xff1a; l HTTP请求方法是POST。 l HTTP请求头的Content-Type是multipart/form-data。 SpringMVC配置 配置commons-fileupload插件的文件上传解析器CommonsMultip…

搭建servlet服务

目录 servlet的生命周期 配置tomcat环境 创建web后端项目 配置web.xml http请求 get和post 其他请求 http响应 Servlet是Server Applet的简称&#xff0c;意思为用Java编写的服务器端的程序&#xff0c;它运行在web服务器中&#xff0c;web服务器负责Servlet和客户的通…

Springboot开发常用注解

文章目录 1.RestController2.Data3.RequestMapping4.Builder 1.RestController RestController注解其实就是将 return 中的内容以 JSON字符串的形式返回客户端 controller的详解 2.Data Data详解 3.RequestMapping RequestMapping 4.Builder Builder

文件系统目录结构

1. 目录结构 linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构中的最上层是根目录/ &#xff0c;然后在此目录下再创建其他的目录。 在linux中&#xff0c;一切皆文件(Linux将所有的设备、文件、进程等都当做文件来处理) 2. 目录作用具体介绍 目录名解析/b…

访企聚力促创新:长安大学来访闪马智能

7月31日&#xff0c;长安大学运输工程学院院长葛颖恩教授、学院副书记李婷以及学办主任董彬一行来访闪马智能&#xff0c;闪马智能创始人兼CEO彭垚、城市交通行业总经理兼营销副总裁詹诚以及公共交通行业总经理熊天圣等出席了本次交流会。 长安大学运输工程学院院长葛颖恩教授…

软考考点之Mccabe度量计算及路径覆盖

软考考点之Mccabe度量计算及路径覆盖 [2023年上半年] 34、35、以下流程图中&#xff0c;至少需要(34)个测试用例才能覆盖所有路径。采用McCabe方法计算程序复杂度为(35) A&#xff0e;3B&#xff0e;4C&#xff0e;5D&#xff0e;6(35)A&#xff0e;2B&#xff0e;3C&#xf…

【框架类】—Vue3的生命周期

一、生命周期的相关函数 onBeforeMount 页面渲染之前 和 onMounted渲染之后 示例 <template><div class"test"><div ref"el">组件初始化</div></div> </template> <script> //按需引入所需方法 import { ref,…

sudo root命令不在sudoers文件中。此事将被报告。全网比较详细版本,解决报错问题步骤较多

我在使用个人用户zhang&#xff08;非root用户&#xff09;时&#xff0c;在执行其他命令时&#xff0c;使用sudo命令来执行的时候&#xff0c;需要验证当前用户的密码&#xff0c; 输入了之后&#xff0c;提示“zhang‘ 不在sudoers文件中&#xff0c;此事将被报告” 分析原…

JAVA设计模式----原型设计模式

文章目录 一、简介二、实现方式三、原型模式的注意事项浅拷贝与深拷贝浅拷贝深拷贝一、简介 定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 类型:创建类模式 类图: 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Protot…

eNSP:ibgp的破水平切割练习

实验要求&#xff1a; 拓扑展示&#xff1a; 命令操作&#xff1a; R1&#xff1a; <Huawei>sys [Huawei]sys r1 [r1]int g 0/0/1 [r1-GigabitEthernet0/0/1]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/1]int lo0 [r1-LoopBack0]ip add 1.1.1.1 24 [r1-LoopBack0]osp…

dingding机器人

“自定义机器人”只支持消息发送&#xff0c;自动回复需要“企业内部机器人” 消息发送 import requests import jsonres requests.post(https://oapi.dingtalk.com/robot/send?access_token036a339axxx,data json.dumps({"text": {"content":"h…

React UI组件库

1 流行的开源React UI组件库 1 material-ui(国外) 官网: Material UI: React components based on Material Design github: GitHub - mui/material-ui: MUI Core: Ready-to-use foundational React components, free forever. It includes Material UI, which implements Go…

【Hilog】鸿蒙系统日志源码分析

【Hilog】鸿蒙系统日志源码分析 Hilog采用C/S结构&#xff0c;Hilogd作为服务端提供日志功能。Client端通过API调用&#xff08;最终通过socket通讯&#xff09;与HiLogd打交道。简易Block图如下。 这里主要分析一下。Hilog的读、写、压缩落盘&#xff0c;以及higlog与android…

PIL报错:TypeError: Cannot handle this data type: (1, 1, 3), <f4

问题&#xff1a; mi_fgsm.py:101: DeprecationWarning: Starting with ImageIO v3 the behavior of this function will switch to that of iio.v3.imread. To keep the current behavior (and make this warning disappear) use import imageio.v2 as imageio or call image…

django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比

一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…

Stable Diffusion AI绘图教学

课程介绍下载 这门课程将教授学生使用Stable Diffusion AI绘图工具进行数据可视化和图形设计。学生将学习基本的绘图原理、数据分析技巧&#xff0c;以及如何使用Stable Diffusion AI创建高质量的图表和可视化作品。通过实践项目和案例研究&#xff0c;学生将提升绘图技能&…

基础篇-环境搭建

JDK安装 https://www.oracle.com/java/technologies/downloads/ 安装完成后&#xff0c;一直点下一步就行 打开控制面板输入cmd运行&#xff0c;控制台输入java -version。如果看到版本号就说明我们安装成功了 JDK的组成 1. JVM JAVA虚拟机&#xff0c;JAVA真正运行的地方 …

机器学习笔记 - 基于C++的​​深度学习 二、实现卷积运算

一、卷积 卷积是信号处理领域的老朋友。最初的定义如下 在机器学习术语中: I(…)通常称为输入 K(…)作为内核,并且 F(…)作为给定K的I(x)的特征图。 虑多维离散域,我们可以将积分转换为以下求和 对于二维数字图像,我们可以将其重写为: <