Linux使用原始套接字从链路层开始封包组包(IP数据包)

2023-05-16

UDP校验方式:

  1. 在对UDP校验的时候需要在UDP报文之间加上伪头部。
  2. IP校验的时候不需要伪头部
    伪头部中的源ip、目的IP必须和 IP报文中源ip、目的IP 一致。
    注意:伪头部中的源ip、目的IP必须和 IP报文中源ip、目的IP 一致。
#include <netinet/ip.h>
#include <netinet/udp.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>
#include <iostream>
using namespace std;

typedef struct{
    u_int32_t saddr;//源IP
    u_int32_t daddr;//目的IP
    u_int8_t flag;//标记(0)
    u_int8_t protocol;//udp协议 17
    u_int16_t len;//长度
}FAKEHDR;
//校验函数,通信两边的校验方法需要保持一致
unsigned short checksum(unsigned short *buf, int len)
{
    int nword = len/2;
    unsigned long sum;

    if(len%2 == 1)
        nword++;
    for(sum = 0; nword > 0; nword--)
    {
        sum += *buf;
        buf++;
    }
    sum = (sum>>16) + (sum&0xffff);
    sum += (sum>>16);
    return ~sum;
}

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

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

    cout<< "请输入要发送的内容:" << endl;
    char data[128] = "";
    fgets(data,sizeof(data),stdin);
    data[strlen(data) - 1 ] = 0;//去掉回车
    int dataLen = strlen(data);
    //udp需要数据长度为偶数个字节,不是偶数补0
    if(dataLen %2 != 0)
        dataLen++;

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

    unsigned char dstMac[6] = {0x00,0xFF,0xB4,0x31,0xD1,0xE2};
    unsigned char srcMac[6] = {0x00,0x0c,29,0xee,0x18,0x53};
    unsigned char dstIp[4] = {192,168,123,249};
    unsigned char srcIp[4] = {192,168,243,128};
    unsigned char msg[1024] = "";

    //1.ether
    struct ether_header *macHeader = (struct ether_header*)msg;
    memcpy(macHeader->ether_dhost,dstMac,6);
    memcpy(macHeader->ether_shost,srcMac,6);
    macHeader->ether_type  = htons(0x0800);

    //2.IP
    struct iphdr *ipHeader = (struct iphdr*)(msg+14);
    ipHeader->version = 4;//ipv4版本
    ipHeader->ihl = 5;//IP头部长度 单位4B 所以赋值5其实就是5*4=20B
    ipHeader->tos = 0;//服务类型
    ipHeader->tot_len = htons(20+8+dataLen);//总长度=IP首部长度+UDP首部长度+IP数据长度
    ipHeader->id = htons(0);//标识
    ipHeader->frag_off = htons(0);//标志 + 片偏移
    ipHeader->ttl = 128;//64或128都可以 生命周期
    ipHeader->protocol = 17;//协议类型:UDP-17;TCP-6
    memcpy(&ipHeader->daddr,dstIp,4);
    memcpy(&ipHeader->saddr,srcIp,4);
    //ip报文头部校验
    ipHeader->check = checksum((unsigned short *)ipHeader,20);

    //3.UDP
    struct udphdr *udpHeader = (struct udphdr*)(msg+14+20);
    udpHeader->source = htons(8000);//源端口
    udpHeader->dest = htons(9000);//目的端口
    udpHeader->len = htons(8+dataLen);//udp总长度=udp报文头+数据长度
    //UDP校验
    unsigned char fakeHdr[256] = "";
    FAKEHDR* fakeHeader = (FAKEHDR*)fakeHdr;
    memcpy(&fakeHeader->saddr,srcIp,4);
    memcpy(&fakeHeader->daddr,dstIp,4);
    fakeHeader->protocol = 17;
    fakeHeader->flag = 0;
    fakeHeader->len = htons(8+dataLen);//udp的总长度
    //将msg中的udp头部信息以及data数据 拷贝到伪头部后方
    memcpy(fakeHdr+12,udpHeader,8+dataLen);
    //校验udp: 伪头部+udp头部+data部分
    udpHeader->check = checksum((unsigned short *)fakeHdr,12+8+dataLen);

    //4.要发送的数据
    memcpy(msg+14+20+8,data,dataLen);

    sendto(sockfd,msg,14+20+8+dataLen,0,(struct sockaddr*)&sll,sizeof(sll));

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

Linux使用原始套接字从链路层开始封包组包(IP数据包) 的相关文章

随机推荐

  • 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
  • Linux使用原始套接字从链路层开始封包组包(IP数据包)

    UDP校验方式 xff1a 在对UDP校验的时候需要在UDP报文之间加上伪头部 IP校验的时候不需要伪头部 注意 xff1a 伪头部中的源ip 目的IP必须和 IP报文中源ip 目的IP 一致 span class token macro