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

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抓包:

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nevron Vision for .NET Crack

Nevron Vision for .NET Crack NET Vision是一个用于创建具有数据可视化功能的强大数据表示应用程序的套件。该套件具有用于.NET的Nevron Chart、用于.NET的Nevron Diagram和用于.NET的Nevron User Interface。精心设计的对象模型、众多功能和高质量的演示使复杂数据的可视化变…

IntelliJ IDEA如何重新弹出git身份验证窗口

1、点击File菜单—>点击Settings—>点击Appearance & Behavior—>点击System Settings—>点击Passwords—>选中Do not save, forget passwords after restart—>点击Apply—>点击OK&#xff0c;如下所示&#xff1a; 2、重启IntelliJ IDEA—>通过g…

若依管理系统后端将 Mybatis 升级为 Mybatis-Plus

文章目录 说明流程增加依赖修改配置文件注释掉MybatisConfig里面的Bean 代码生成使用IDEA生成代码注意 Controller文件 说明 若依管理系统是一个非常完善的管理系统模板&#xff0c;里面含有代码生成的方法&#xff0c;可以帮助用户快速进行开发&#xff0c;但是项目使用的是m…

Linux 编译CEF源码详细记录

Linux CEF&#xff08;Chromium Embedded Framework&#xff09;源码下载编译 背景 由于CEF默认的二进制分发包不支持音视频播放&#xff0c;需要自行编译源码&#xff0c;将ffmpeg开关打开才能支持。这里介绍的是Linux平台下的CEF源码下载编译过程。 Windows平台参考&#…

包装类+初识泛型

目录 1 .包装类 1.1 基本数据类型对应的包装类 1.2.1装箱 ​1.2.2拆箱 2.初识泛型 2.1什么是泛型 2.2泛型类 2.3裸类型 2.4泛型的上界 2.5泛型方法 1 .包装类 基本数据类型所对应的类类型 在 Java 中&#xff0c;由于基本类型不是继承自 Object &#xff0c;为了在泛型…

腾讯云服务器地域有什么区别怎么选择?

腾讯云服务器地域有什么区别&#xff1f;怎么选择比较好&#xff1f;地域选择就近原则&#xff0c;距离地域越近网络延迟越低&#xff0c;速度越快。关于地域的选择还有很多因素&#xff0c;地域节点选择还要考虑到网络延迟速度方面、内网连接、是否需要备案、不同地域价格因素…

C++11语法笔记

文章目录 一.类中新增的默认成员函数:移动赋值和移动构造二.lambda表达式三.包装器bind函数 一.类中新增的默认成员函数:移动赋值和移动构造 二.lambda表达式 三.包装器 bind函数

vue3 table动态合并,自定义参数合并单元格

<template><div><el-table :data"tableData" :span-method"objectSpanMethod" border:header-cell-style"{ textAlign: center}"><el-table-column prop"area" label"区域" align"center"&g…

红帽8.5 ansible 安装和部署 |(简单版)

安装 配置yum仓库&#xff1a; vim /etc/yun.repo.d/aliyun.repo [AppStream] nameApp baseurlhttps://mirrors.aliyun.com/centos/8-stream/AppStream/x86_64/os gpgcheck0[BaseOS] namebase baseurlhttps://mirrors.aliyun.com/centos/8-stream/BaseOS/x86_64/os gpgcheck…

Vue+SpringBoot项目开发:登录页面美化,登录功能实现(三)

写在开始:一个搬砖程序员的随缘记录上一章写了从零开始VueSpringBoot后台管理系统&#xff1a;Vue3TypeScript项目搭建 VueTypeScript的前端项目已经搭建完成了 这一章的内容是引入element-plus和axios实现页面的布局和前后端数据的串联&#xff0c;实现一个登陆的功能&#x…

无涯教程-Perl - opendir函数

描述 此函数使用readdir函数打开目录EXPR,并将其与DIRHANDLE关联以进行处理。 语法 以下是此函数的简单语法- opendir DIRHANDLE, EXPR返回值 如果成功,此函数将返回true。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -w$dirname"/tmp";opendir ( …

Maven的安装与配置(包含所有细节)

一、idea版本和maven配对 这里是很多新手都会遇到的大坑&#xff0c;一定要先将自己的idea版本和maven进行版本配配对。 Maven3.6.3版本兼容问题 注意&#xff1a;针对一些老项目 还是尽量采用 3.6.3版本&#xff0c;针对idea各个版本的兼容性就很兼容 IDEA 2022 兼容maven 3.8…

【2023 华数杯全国大学生数学建模竞赛】 C题 母亲身心健康对婴儿成长的影响 45页论文及python代码

【2023 华数杯全国大学生数学建模竞赛】 C题 母亲身心健康对婴儿成长的影响 45页论文及python代码 1 题目 母亲是婴儿生命中最重要的人之一&#xff0c;她不仅为婴儿提供营养物质和身体保护&#xff0c; 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c…

【Docker】性能测试监控平台搭建:InfluxDB+Grafana+Jmeter+cAdvisor

前言 在做性能测试时&#xff0c;如果有一个性能测试结果实时展示的页面&#xff0c;可以极大的提高我们对系统性能表现的掌握程度&#xff0c;进而提高我们的测试效率。但是我们每次打开Jmeter都会有几个硕大的字提示别用GUI模式进行负载测试&#xff0c;而且它自带的监视器效…

HCIP STP(生成树)

目录 一、STP概述 二、生成树协议原理 三、802.1D生成树 四、STP的配置BPDU 1、配置BPDU的报文格式 2、配置BPDU的工作过程 3、TCN BPDU 4、TCN BPDU的工作过程 五、STP角色选举 1、根网桥选举 2、根端口选举 3、指定端口选举 4、非指定端口选举 六、STP的接口状…

MD-MTSP:光谱优化算法LSO求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、光谱优化算法LSO 光谱优化算法&#xff08;Light Spectrum Optimizer&#xff0c;LSO&#xff09;由Mohamed Abdel-Basset等人于2022年提出。 参考文献&#xff1a; [1]Abdel-Basset M, Mohamed R, Sallam KM, Chakrabortty RK. Light Spectrum Optimizer: A Novel Physi…

使用Python和wxPython将图片转换为草图

导语: 将照片转换为艺术风格的草图是一种有趣的方式&#xff0c;可以为您的图像添加独特的效果。在本文中&#xff0c;我们将介绍如何使用Python编程语言和wxPython图形用户界面库来实现这一目标。我们将探讨如何使用OpenCV库将图像转换为草图&#xff0c;并使用wxPython创建一…

2023年8月份华为H12-811更新了

801、[单选题]178/832、在系统视图下键入什么命令可以切换到用户视图? A quit B souter C system-view D user-view 试题答案&#xff1a;A 试题解析&#xff1a;在系统视图下键入quit命令退出到用户视图。因此答案选A。 802、[单选题]“网络管理员在三层交换机上创建了V…

10个AI绘图生成器让绘画更简单

AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中发挥着越来越大的作用&#xff0c;开创了AI绘画生成器新时代。在绘画领域当然也是如此&#xff0c;与传统的绘画工具不同&#xff0c;AI人工智能时代的绘画工具是全自动的、智能的&#xff0c;甚至可以说是“傻瓜式”…

网页爬虫中常用代理IP主要有哪几种?

各位爬虫探索者&#xff0c;你是否有想过在网页爬虫中使用代理IP来规避限制实现数据自由&#xff1f;在这篇文章中&#xff0c;作为一名IP代理产品供应商&#xff0c;我将为你揭示常见的网页爬虫代理IP类型&#xff0c;让你在爬虫的世界中游刃有余&#xff01; 一、免费公开代理…