原始套接字编程(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 DEBUGstatic 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 DEBUGtest();return 0;
#endifunsigned char send_msg[BUFFSIZE] = {//--------------Ethernet II------------------------14--------------------------0xf8, 0x94, 0xc2, 0xdb, 0x72, 0x43, //dst_mac: F8-94-C2-DB-72-430x00, 0x0c, 0x29, 0x7e, 0x2f, 0x86, //src_mac: 00-0C-29-7E-2F-860x08, 0x00,                         //type: 0x0800 IPV4//--------------Internet Protocol Version 4--------20--------------------------0x45,                               //0100-version4, 0101-5*4byte=20byte0x00,                               //DSCP:0, ECN-Not-ECT0x00, 0x54,                         //total length: 0x54==84==20(IP)+64(ICMP)0x36, 0x23,                         //identification: 0x36 0x230x40, 0x00,                         //flags: 0x4000, do not fragment0x40,                               //time to live(TTL): 640x01,                               //protocol: ICMP(1)0x00, 0x00,                         //checksum(need to calc later)192, 168, 0, 104,                   //src ip addr192, 168, 0, 103,                   //dst ip addr//--------------ICMP-Header------------------------16--------------------------0x08,                               //type: 8(echo ping request)0x00,                               //code:00x00, 0x00,                         //checksum(need to calc later)0x20, 0x09,                         //identifier: 0x20 0x090x00, 0x00,                         //sequence number0x12, 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://xiahunao.cn/news/1379860.html

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

相关文章

STL初探

STL简介 STL(standard template libaray - 标准模板库)是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 STL的一些版本 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本&#xff0c;…

PHP最简单自定义自己的框架实现像TP链式sql语句(六)

1、实现效果&#xff0c;链式sql语句封装 order、where、group、limit等封装 2、数据表构造函数入参&#xff0c;ModelBase.php public $table NULL; public function __construct($table){$this->table$table;if(!$this->table){die("no table" );}$this-&…

AI一键生成数字人

AI一键生成数字人,不玩虚的 阅读时长&#xff1a;10分钟 本文内容&#xff1a; 结合开源AI&#xff0c;一键生成短视频发布到常见的某音&#xff0c;某手平台&#xff0c;狠狠赚一笔 前置知识&#xff1a; 基本的 python 编程知识Jupyter Notebook 使用过Linux 使用过 先上源码…

Wallpaper Engine使用视频壁纸黑屏解决方法(window10)

1.点击电脑左下角开始键>设置>搜索框搜索图形设置 2.浏览>找到wallpaper engine相关程序添加 3.将三个的选项改成节能 4.如果软件还在运行&#xff0c;重新启动

WebView2 通过 PuppeteerSharp 实现爬取 王者 壁纸 (案例版)

王者壁纸自动化获取逻辑分析 其实它的逻辑很简单&#xff0c; 就是王者的官网&#xff0c;打开后&#xff0c;在右下角就看到了皮肤页面部分。 这个时候&#xff0c;点击更多&#xff0c;就会打开全部英雄详情的页面。 这个时候&#xff0c;单点任意一个英雄&#xff0c;就会…

微信小程序短视频去水印,王者查战力,手持弹幕,获取头像壁纸,工具箱源码

短视频在线去水印,无任何广告 王者荣耀查荣耀战力已更新 新增加查看历史上的今天 新增手持弹幕&#xff0c;修复剪辑视频不能保存问题。 新增壁纸&#xff0c;头像&#xff0c;网址转二维码。 【微信小程序】&#xff1a;好野工具 【小程序版本】&#xff1a;5.1.1 【小…

王者壁纸批量爬取

语言&#xff1a;python json格式网站&#xff1a; 爬取网址&#xff1a;王者荣耀壁纸下载-王者荣耀官方网站-腾讯游戏 思路分析&#xff1a; 分析下一页&#xff0c;发现下一页之后是在当前页面的局部重新加载 当然排除可以直接爬取目标url获取当前页面的信息以及后面的所…

C语言案例 阶乘求和-12

题目&#xff1a;求1 2&#xff01;3&#xff01; … 20&#xff01;的和。 程序分析 阶乘相关原理&#xff1a;一个正整数的阶乘是所有小于及等于该数的正整数的积&#xff0c;并且0的阶乘为1。自然数n的阶乘写作n!&#xff0c;任何大于1的自然数n阶乘表示方法&#xff1a;…

5大电脑软件推荐!每一款都是精心挑选,良心推荐!

说起实用的电脑软件&#xff0c;我想你看对地方了&#xff0c;世上最棒最实用的电脑软件推荐&#xff01;每一款都十足良心&#xff01; 是时候表演真正的技术了&#xff01; 1.PotPlayer 超受欢迎的播放器之一。这款播放器可能会成为你的菜&#xff0c;内存占用低并且不卡&a…

Windows上有哪些推荐的笔记软件?

说实话&#xff0c;windows上的优秀的笔记软件没有ipad上多&#xff0c;在ipad上的notability、goodnotes、marginnote等优秀的笔记软件&#xff0c;在windows上很少有可替代的产品&#xff0c;不过在windows上也有一些比较优质的笔记软件&#xff0c;对于学生来说&#xff0c;…

3款强大到离谱的电脑软件,个个提效神器,从此远离加班

推荐3款让你偷懒&#xff0c;让你上头的提效电脑软件&#xff0c;个个功能强大&#xff0c;让你远离加班&#xff01; 很多几个小时才能做好的事情&#xff0c;用上它们&#xff0c;只需要5分钟就行&#xff01;&#xff01; 1、Quicker —— 桌面快速启动工具 Quicker鼠标效…

这几款好用的电脑软件推荐给你

软件一&#xff1a;potplayer PotPlayer是一款免费的媒体播放器&#xff0c;它支持多种格式的音频和视频文件。它的界面简洁而且易于使用&#xff0c;让用户可以轻松地播放各种媒体文件。以下是PotPlayer的一些主要功能&#xff1a; 支持多种格式&#xff1a;PotPlayer支持多…

5款超级无敌好用的电脑软件 建议收藏!

1.Quicker Quicker绝对要吹爆&#xff0c;Quicker可以给常用的软件和操作建立捷径&#xff0c;鼠标中键调出快捷面板使用&#xff0c;将常用操作放在手边&#xff0c;绝对的生产力工具&#xff0c;节省大量时间。 支持自定义动作&#xff0c;内置丰富的动作库&#xff0c;可…

电脑推荐软件

个人推荐电脑软件&#xff0c;如需自取。附下载链接&#xff1a; 电脑推荐软件: https://url46.ctfile.com/d/42812846-54181981-4b3214?p7872 (访问密码: 7872) 1、Everything 文件搜索器&#xff0c;快速找出电脑里面所有文件&#xff0c;支持关键字查找。 下载链接&#xf…

3.1 Spring MVC概述

1. MVC概念 MVC是一种编程思想&#xff0c;它将应用分为模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;、控制器&#xff08;Controller&#xff09;三个层次&#xff0c;这三部分以最低的耦合进行协同工作&#xff0c;从而提高应用的可扩展性及可维护…

Vue生命周期函数(详解)

目录 生命周期图 生命周期函数 beforeCreate和created的区别 beforeCreate创建前应用场景 created创建后应用场景 beforeMount和mounted的区别 beforeMount挂载前应用场景 mounted挂载后应用场景 beforeUpdate和updated的区别 beforeUpdate更新前应用场景 updated更新后应用…

数据通信——VRRP

引言 之前把实验做了&#xff0c;结果发现我好像没有写过VRRP的文章&#xff0c;连笔记都没记过。可能是因为对STP的记忆&#xff0c;导致现在都没忘太多。 一&#xff0c;什么是VRRP VRRP全名是虚拟路由冗余协议&#xff0c;虚拟路由&#xff0c;看名字就知道这是运行在三层接…

【前端 | CSS】滚动到底部加载,滚动监听、懒加载

背景 在日常开发过程中&#xff0c;我们会遇到图片懒加载的功能&#xff0c;基本原理是&#xff0c;滚动条滚动到底部后再次获取数据进行渲染。 那怎么判断滚动条是否滚动到底部呢&#xff1f;滚动条滚动到底部触发时间的时机和方法又该怎样定义&#xff1f; 针对以上问题我…

【瑞吉外卖】Linux学习

Linux常用命令 Linux命令初体验 Linux的命令都是由一个或几个单词的缩写构成的 命令对应英文作用lslist查看当前目录下的内容pwdprint work directory查看当前所在目录cd [目录名]change directory切换目录touch [文件名]touch如果文件不存在&#xff0c;新建文件mkdir [目录…

HCIA---动态路由---RIP协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 前言 一.动态路由 二.动态路由协议分类 IGP&#xff1a;内部网关协议 EGP:外部网关协议 三.RIP协议概述 RIP版本分类&#xff1a; RIP三要素&#xff1a; 思维…