Linux使用原始套接字从链路层开始封包组包(ARP请求与应答)

2023-05-16

数组方式封包

#include <iostream>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <cstring>
#include <sys/ioctl.h>
#include <pthread.h>

/*
 * 使用原始套接字,从链路层开始组以太网帧数据进行发送
*/

using namespace std;
void *recv_from(void* arg)
{
    int sockfd = *(int*)arg;

    while(1)
    {
        unsigned char buf[1500] = "";
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        //如果帧类型是ARP
        if(ntohs(*((unsigned short*)(buf+12))) == 0x0806)
        {
            //如果是ARP应答包
            if(ntohs(*(unsigned short*)(buf+20)) == 0x02)
            {
                unsigned char temp_ip[] = {192,168,243,1};
                //验证是否是想获取IP的RAP应答包
                if(memcmp((buf+28),temp_ip,4) == 0){
                    char dest_MAC[18] = "";
                    sprintf(dest_MAC,"%02x:%02x:%02x:%02x:%02x:%02x",buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);
                    char dest_IP[16] = "";
                    sprintf(dest_IP,"%d.%d.%d.%d",buf[28],buf[29],buf[30],buf[31]);
                    cout << dest_MAC << endl;
                    cout << dest_IP << endl;
                    break;
                }
            }
        }
    }
    return  NULL;
}

int main()
{
    int sockFd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    if(sockFd < 0)
    {
        perror("socket error");
        return  -1;
    }

    //要组的包
    unsigned char msg[] = {
        /*MAC报文头部*/
        0xff,0xff,0xff,0xff,0xff,0xff,//目标MAC地址
        0x00,0x0c,0x29,0xee,0x18,0x53,//源MAC地址
        0x08,0x06,//帧类型
        /*ARP报文头部*/
        0x00,0x01,//硬件类型,1代表以太网地址
        0x08,0x00,//0x0800为IP
        0x06,//硬件地址长度
        0x04,//协议地址长度
        0x00,0x01,//1:ARP请求;2:ARP应答;3:RARP请求;4:RARP应答
        0x00,0x0c,0x29,0xee,0x18,0x53,//源MAC地址
        192,168,243,128,//源IP地址
        0x00,0x00,0x00,0x00,0x00,0x00,//目标MAC地址
        192,168,243,1//目标IP地址
    };

    //设置网卡接口
    struct ifreq ethreq;
    strncpy(ethreq.ifr_ifrn.ifrn_name,"ens33",IFNAMSIZ);
    if(-1 == ioctl(sockFd,SIOCGIFINDEX,&ethreq))
    {
        perror("ioctl error");
        return  -2;
    }

    //设置帧数据从哪块网卡出去
    struct sockaddr_ll sll;
    bzero(&sll,sizeof(sll));
    sll.sll_ifindex = ethreq.ifr_ifru.ifru_ivalue;
    //接收ARP应答,如果放在发送下方,有可能会因为代码没有执行到下一行,ARP应答包就回来了,导致丢包
    //因为recvform会阻塞,所以这里要开启线程函数处理
    pthread_t tid;
    pthread_create(&tid,NULL,recv_from,(void*)&sockFd);
    //发送ARP请求
    sendto(sockFd,msg,sizeof(msg),0,(struct sockaddr*)&sll,sizeof(sll));
    pthread_join(tid,NULL);
    close(sockFd);
    return 0;
}

结构体方式封包

struct arphdr所在位置: /usr/include/net/if_arp.h #include <net/if_arp.h>

#include <iostream>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <cstring>
#include <sys/ioctl.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <unistd.h>
#include <pthread.h>

using namespace std;

void * recvThread(void * arg)
{
    int sockfd = *(int*)arg;
    while(true)
    {
        unsigned char buf[1500] = "";
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        if(ntohs(*((unsigned short*)(buf+12))) == 0x0806)
        {
            if(ntohs(*(unsigned short*)(buf+20)) == 0x02)
            {
                unsigned char temp_ip[] = {192,168,243,1};
                if(memcmp((buf+28),temp_ip,4) == 0)
                {
                    char dest_MAC[18] = "";
                    sprintf(dest_MAC,"%02x:%02x:%02x:%02x:%02x:%02x",buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);
                    char dest_IP[16] = "";
                    sprintf(dest_IP,"%d.%d.%d.%d",buf[28],buf[29],buf[30],buf[31]);
                    cout << dest_MAC << endl;
                    cout << dest_IP << endl;
                    break;
                }
            }
        }
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    int sockfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    if(sockfd < 0)
    {
        perror("sockfd error");
        return -1;
    }

    struct ifreq ethreq;
    strncpy(ethreq.ifr_name,"ens33",IFNAMSIZ);
    if(ioctl(sockfd,SIOCGIFINDEX,&ethreq) == -1)
    {
        perror("ioctl error");
        close(sockfd);
        return -2;
    }

    struct sockaddr_ll sll;
    bzero(&sll,sizeof(sll));
    sll.sll_ifindex = ethreq.ifr_ifindex;

    unsigned char destMac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
    unsigned char srcMac[6] = {0x00,0x0c,0x29,0xee,0x18,0x53};
    unsigned char destIp[4] = {192,168,243,1};
    unsigned char srcIp[4] = {192,168,243,128};
    unsigned char msg[1024] = "";

    struct ether_header *macHeader = (struct ether_header*)msg;
    memcpy(macHeader->ether_dhost,destMac,sizeof(destMac));
    memcpy(macHeader->ether_shost,srcMac,sizeof(srcMac));
    macHeader->ether_type = htons(0x0806);

    struct arphdr *arpHeader = (struct arphdr*)(msg+14);
    arpHeader->ar_hrd = htons(1);
    arpHeader->ar_pro = htons(0x0800);
    arpHeader->ar_hln = 6;
    arpHeader->ar_pln = 4;
    arpHeader->ar_op = htons(1);
    memcpy(arpHeader->__ar_sha,srcMac,6);
    memcpy(arpHeader->__ar_sip,srcIp,4);
    memcpy(arpHeader->__ar_tha,destMac,6);
    memcpy(arpHeader->__ar_tip,destIp,4);

    pthread_t tid;
    pthread_create(&tid,NULL,recvThread,(void*)&sockfd);
    sendto(sockfd,msg,sizeof(msg),0,(struct sockaddr*)&sll,sizeof(sll));

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

Linux使用原始套接字从链路层开始封包组包(ARP请求与应答) 的相关文章

  • 为 Qt 应用程序创建 Linux 安装

    我刚刚用 Qt Creator 制作了一个很棒的程序 我对自己很满意 如何将其从台式机移至笔记本电脑 那么 最好的方法是安装程序 对吗 对于 Ubuntu 这是一个 Debian 软件包 对吗 我怎么做 有人这样做过吗 他们可以分享 QT
  • Inotify linux 监视子目录

    是否可以以这种模式监视目录 storage data usernames Download gt storage data Download 我需要监视每个用户的下载文件夹中是否进行了更改 也许我需要创建所有路径的列表 将其放入数组中 并在
  • 如何以编程方式从Linux中的进程名称获取进程ID

    在我的项目中 我们使用 ACE 自适应通信环境 中间件来编写可在 Windows 和 Linux 上运行的独立于操作系统的代码 要求是从进程名称中获取进程 ID 由于 ACE 不支持这一点 因此我们必须使用特定于平台的宏来分离 Window
  • 每个进程每个线程的时间量

    我有一个关于 Windows 和 Linux 中进程和线程的时间量子的问题 我知道操作系统通常为每个线程提供固定的时间量 我知道时间量根据前台或后台线程而变化 也可能根据进程的优先级而变化 每个进程有固定的时间量吗 例如 如果操作系统为每个
  • 从 TypeScript 运行任何 Linux 终端命令?

    有没有办法直接从 TypeScript 类中执行 Linux 终端命令 这个想法是做类似的事情 let myTerminal new LinuxTerminal let terminalResult myTerminal run sudo
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 从多线程程序中调用 system()

    我们正在开发一个用 C 编写的多线程内存消耗应用程序 我们必须执行大量的 shellscript linux 命令 并获取返回码 读完之后article http www linuxprogrammingblog com threads a
  • 无需 cron 在后台发送邮件

    我想知道是否有一种方法可以运行 PHP 循环 以便在后台向订阅者发送几百封电子邮件 我的目标是格式化新闻通讯 单击发送 然后关闭浏览器或更改页面 当然 发送电子邮件的实际过程将在后台运行 不会因浏览器关闭而中断 我知道这可以通过 cron
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst
  • Linux shell 脚本中的 while 循环超时

    这工作正常 无限循环 while TRUE do printf done 我在尝试着timeout this while loop与timeout命令 所有这些都不起作用 timeout 5 while TRUE do printf don
  • 在 /dev/input/eventX 中写入事件需要哪些命令?

    我正在开发一个android需要将触摸事件发送到 dev input eventX 的应用程序 我知道C执行此类操作的代码结构如下 struct input event struct timeval time unsigned short
  • 在主目录中安装库

    在 Linux Ubuntu 中 我尝试运行一个工具 但它显示错误 库丢失 我无权在系统中安装任何内容 或者根本无法从我的用户帐户执行 sudo 是否可以在我的主目录 没有 sudo 中安装缺少的库 在我的例子中为 libstdc so 6
  • 如何更改 Ubuntu 14.04 上的 php-cli 版本?

    我是 Linux 新手 在篡改时破坏了一些 php 设置 如果我执行一个包含以下内容的 php 脚本 phpinfo 它显示 php 版本为 5 6 但通过命令行 如果我运行php v它返回 7 0 版本 我想让两个版本匹配 我怎样才能修复
  • 嵌入式Linux poll()不断返回

    我有一个特别的问题 当我知道没有什么可读时 民意调查不断返回 因此设置如下 我有 2 个文件描述符 它们构成fd设置民意调查监视 一种用于引脚从高到低的变化 GPIO 另一个用于代理输入 代理输入出现问题 处理的顺序是 启动main函数 然
  • 为什么我收到的数据包数据大小大于mss?

    我在两台 PC 上使用 ifconfig ethX mtu 300 修改了 MTU 并使用 netperf 测试网络 我用 WireShark 嗅探了 SYN 数据包中的 MSS 260 但我得到了一些大于 260 的数据包 为什么 嗅探器
  • 在 Mac OS X 上构建 Linux 内核

    我正在做一个修改Linux内核的项目 我有一台桌面 Linux 机器 在上面构建内核没有问题 不过 我要去旅行 我想在途中工作 我只有一台 MacBook 当我尝试构建 Linux 内核时 它抱怨说elf h was not found 我
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • Jenkins中找不到环境变量

    我想在詹金斯中设置很多变量 我试过把它们放进去 bashrc bash profile and profile of the jenkins用户 但 Jenkins 在构建发生时找不到它们 唯一有效的方法是将所有环境变量放入Jenkinsf
  • 如何在bash中使用jq从变量中包含的json中提取值

    我正在编写一个 bash 脚本 其中存储了一个 json 值 现在我想使用 Jq 提取该 json 中的值 使用的代码是 json val code lyz1To6ZTWClDHSiaeXyxg redirect to http examp
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c

随机推荐

  • Linux系统编程之readdir()函数判断目录是否为空的问题

    由于Linux目录中总是存在 两个隐藏目录 xff0c 所以在用readdir 函数判断是否为空时 xff0c 得到的结果总是目录不为空 但是现实中对于如果目录中没有自己的文件就应该被判为空 xff0c 所以在多次测试之后得到下面这段判断目
  • Linux系统编程之wait()函数回收子进程

    一次调用wait或waitpid函数只能回收一个子进程 xff0c 当有多个子进程时应使用循环 如果想回收指定进程 xff0c 使用waitpid函数 函数原型 pid t wait int status 返回值 成功 xff1a 清理掉的
  • 神秘的程序员

    神秘的程序员漫画集
  • 进程间通信方法一:管道:pipe()、fifo

    管道的实现原理是内核使用循环队列机制 xff0c 借助内核缓冲区 xff08 4K xff09 实现 管道的局限性 xff1a 由于管道采用半双工的通信方式 xff0c 所以数据只能在一个方向上流动 xff0c 要不读要不写 xff08 读
  • Linux系统编程之进程间通信二:内存映射区

    1 内存映射区 1 1mmap函数 函数功能 创建内存映射将磁盘文件的数据映射到内存中 xff0c 通过修改内存就能修改磁盘文件 函数原型 length xff1a 大小为4K的倍数 xff0c 且不能为0 一般文件多大 xff0c 映射区
  • Linux系统编程之多线程并发服务器简单实现

    服务端 当有客户端连接服务端后 xff0c 客户端发送小写字母 xff0c 服务端转换为大写字母后发送给客户端 span class token macro property span class token directive keywo
  • vim中配置C/C++环境

    在vim中配置C C 43 43 环境 xff1a 详见资源下载地址 xff1a VIM配置C C 43 43 配置开发环境 包括代码函数自动补全 括号自动补全与详细的配置步骤等 xff0c 基本是一个完整的代码编辑器 配置完后如果使用Xs
  • Linux系统调用函数read()返回值判断

    函数原型 read int fd void buf size t count 函数说明 将从文件描述符fd对应的文件中读到的数据存在buf缓冲区中 xff0c 每次读count字节 xff0c 同时文件指针会随着移动 函数返回值 当返回值大
  • Linux系统编程之多进程并发服务器简单实现

    服务端 span class token macro property span class token directive keyword include span span class token string lt stdio h g
  • 多路IO转接服务器实现方法一:select()函数

    采用多进程与多线程的方法来实现并发服务器时 xff0c 监听的工作由server应用程序自身通过accept函数不断去监听 当客户端连接较多时 xff0c 这种方法会大大降低程序执行效率 xff0c 消耗CPU资源 xff08 CPU需要在
  • 多路IO转接服务器实现方法二:poll()函数

    相较于多路IO转接服务器实现方法一 xff1a select 函数 xff0c 使用poll 函数的优点有 文件描述符的上限可以突破1024select 函数监听集合与返回的满足监听条件的集合为一个集合 xff0c 而poll函数将监听集合
  • 多路IO转接服务器实现方法三:epoll()函数

    epoll是Linux下多路复用接口select poll的增强版本 它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率 xff0c 因为它会复用文件描述符集合来传递结果而不用每次等待时间之前都必须重新准备要被监听的文件
  • epoll函数之边缘触发、水平触发、非阻塞IO模式

    1 边沿触发与水平触发 这两个实际是由模拟电路上的高低电频来的 xff0c 低电压 0 到高电压 1 为上升沿 xff0c 高电压 1 到低电压 0 为下降沿 xff0c 高电压 1 到高电压 1 为水平沿 水平沿对应水平触发 xff0c
  • linux getty命令参数及用法详解--linux开启及设置终端命令

    getty get teletypewriter 功能说明 xff1a 是Unix类操作系统启动时必须的三个步骤之一 xff0c 用来开启终端 xff0c 进行终端的初始化 xff0c 设置终端 语 法 xff1a getty h d lt
  • 进程间通信方法三:本地套接字domain

    代码中注释已写的很详细 xff0c 就不多言了 注意事项 保证套接字文件在同一个目录下网络套接字与本地套接字使用的结构体不同 网络套接字采用的结构体为struct sockaddr in本地套接字采用的结构体为struct sockaddr
  • itoa()、atoi()函数C++实现

    主要思想就是利用字符型的数字减去或加上字符型的0 xff0c 得到的就是刚好就是整型的数字 例如 0 的ASCII码为48 xff0c 4 的ASCII码为52 xff0c 4 0 61 52 47 61 4 xff0c 这样就得到数字4了
  • 自写简单的抓包程序

    需要清楚以太网帧头结构 xff0c tcp报头结构 udp报头结构 span class token macro property span class token directive keyword include span span c
  • 使用TFTP协议下载文件

    TFTP协议 最初用于引导无盘系统 xff0c 被设计用来传输小文件 TFTP基于UDP协议 TFTP的编程思想 和 UDP一样TFTP xff1a 简单文件传送协 特点 xff1a 基于UDP协议实现不进行用户有效性认证 数据传输模式 x
  • C++变参函数

    需要包含头文件 lt stdarg h gt 例1 span class token macro property span class token directive hash span span class token directiv
  • Linux使用原始套接字从链路层开始封包组包(ARP请求与应答)

    数组方式封包 span class token macro property span class token directive hash span span class token directive keyword include s