计算UDP报文CRC校验的总结

概述

因公司项目需求,遇到需要发送带UDP/IP头数据包的功能,经过多次尝试顺利完成,博文记录以备忘。

环境信息

操作系统

ARM64平台的中标麒麟Kylin V10

工具

tcpdump、wireshark、vscode

原理

请查看大佬的博文

UDP伪包头定义(图片来源于参考链接)
在这里插入图片描述

调试过程

对着大佬的博文说明,用上白嫖党专业技能C++【Copy++】,欠缺的结构体和头文件运用专业技能补全。

//头文件
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <string.h>

//结构体
struct ipovly {
	caddr_t	ih_next, ih_prev;	/* for protocol sequence q's */
	u_char	ih_x1;			/* (unused) */
	u_char	ih_pr;			        //协议域
	short	ih_len;			        //这个相当于IP头部,len = data Len + udp HeaderLen + ip header
	struct	in_addr ih_src;		//源地址
	struct	in_addr ih_dst;		//目标地址
};

补全后的代码,经过测试,发现死活不对,后面通过打印结构体大小,发现了端倪,罪魁祸首就是 caddr_t 这个数据类型,IP报文头只有20个字节,但是这个结构体直接32个字节,将UDP的数据都覆盖了部分。这就导致生成的CRC总是不对。

经过层层头文件穿透,caddr_t 定义如下

//sys/types.h
......省略部分

typedef char *__caddr_t;

......省略部分

#ifdef	__USE_MISC
# ifndef __daddr_t_defined
typedef __daddr_t daddr_t;
typedef __caddr_t caddr_t;
#  define __daddr_t_defined
# endif
#endif

发现char * 数据类型在64位操作系统下是占用8个字节;在32位操作系统下是占用4个字节,故而导致数据被覆盖。因为struct ipovly这个结构不是同一个博文定义的,不排除是复制有差异。

后面将结构体调整如下

struct ipovly {
	unsigned int ih_next, ih_prev;	/* for protocol sequence q's */
	u_char	ih_x1;			/* (unused) */
	u_char	ih_pr;			        //协议域
	short	ih_len;			        //这个相当于IP头部,len = data Len + udp HeaderLen + ip header
	struct	in_addr ih_src;		//源地址
	struct	in_addr ih_dst;		//目标地址
};

因为ih_next、ih_prev这两个字段并没有使用,所以只要保证每个字段大小是4个字节,并且值为0即可。

经过一番测试,对比成功。

完整代码

为了方便项目使用,做了部分调整,请自行针对性修改

#include <netinet/udp.h>
#include <netinet/ip.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#ifndef IPHL
#define IPHL sizeof(struct ip)
#endif

#ifndef UDPHL
#define UDPHL sizeof(struct udphdr)
#endif

struct ipovly
{
  unsigned int ih_next, ih_prev; /* for protocol sequence q's */
  u_char ih_x1;                  /* (unused) */
  u_char ih_pr;                  // 协议域
  short ih_len;                  // 这个相当于IP头部,len = data Len + udp HeaderLen + ip header
  struct in_addr ih_src, ih_dst; /* source and dest address */
};

unsigned short my_cksum(unsigned short *addr, int len)
{
  register int sum = 0;
  register unsigned short *w = addr;
  register int nleft = len;
  int c = 0;
  while (nleft > 1)
  {
    sum += *w++;
    nleft -= 2;
  }
  if (nleft == 1)
  {
    unsigned char a = 0;
    memcpy(&a, w, 1);
    sum += a;
  }
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

short udpipgen(
    unsigned char *frame,
    const char *saddr,
    const char *daddr,
    unsigned short sport,
    unsigned short dport,
    unsigned short msglen)
{
  struct ip *ip = (struct ip *)(frame);
  struct udphdr *udp = (struct udphdr *)(frame + IPHL);
  struct ipovly *io = (struct ipovly *)frame; /*Only for UDP crc*/
  struct in_addr s_addr, d_addr;
  memset(&s_addr, 0, sizeof(struct in_addr));
  memset(&d_addr, 0, sizeof(struct in_addr));
  inet_aton(saddr, &s_addr);
  inet_aton(daddr, &d_addr);

  if (ip == NULL || udp == NULL || io == NULL || saddr == NULL || daddr == NULL ||
      s_addr.s_addr == 0 || d_addr.s_addr == 0)
  {
    return 0;
  }

  io->ih_next = io->ih_prev = 0;
  io->ih_dst.s_addr = d_addr.s_addr;
  io->ih_src.s_addr = s_addr.s_addr;
  io->ih_x1 = 0;
  io->ih_pr = IPPROTO_UDP;
  io->ih_len = htons(UDPHL + msglen);
  udp->uh_sport = htons(sport);
  udp->uh_dport = htons(dport);
  udp->uh_ulen = io->ih_len;
  udp->uh_sum = my_cksum((unsigned short *)ip, UDPHL + IPHL + msglen);
  memset(io, 0x00, sizeof(struct ipovly));
  ip->ip_v = IPVERSION;
  ip->ip_hl = IPHL >> 2;
  ip->ip_tos = 0;
  ip->ip_len = htons(UDPHL + IPHL + msglen);
  ip->ip_id = htons(0x78D6); //反向验证的时候,注意该字段是否和Wireshark的数据包一致
  ip->ip_off = 0; //反向验证的时候,注意该字段是否和Wireshark的数据包一致
  ip->ip_ttl = 1; //反向验证的时候,注意该字段是否和Wireshark的数据包一致
  ip->ip_p = IPPROTO_UDP;
  ip->ip_src.s_addr = s_addr.s_addr;
  ip->ip_dst.s_addr = d_addr.s_addr;
  ip->ip_sum = my_cksum((unsigned short *)ip, IPHL);
  return 1;
}

/**
* 正向使用程序(通过数据生成对应的IP/UDP包数据)样例
* int main(...)
* {
*    //需要发送的内层数据
* 	char data[100] = {...};
* 	//包含IP头、UDP头、内层数据的数组(以下称帧缓冲区)
* 	char frame[20+8+sizeof(data)] = {0};
* 	memset(frame,0,sizeof(frame));
* 	//内层数据复制到帧缓冲区,因为UDP计算CRC的时候,需要带上数据。IP包头计算CRC只需要包头数据
* 	memcpy(frame+28,data,sizeof(data)); //28 = IP包头20个字节 + UDP包头8个字节
* 	//udpipgen函数会自动填充对应的结构体字段
* 	short ret = udpipgen(frame, "192.168.10.1", "192.168.10.119", 10254, 10252, sizeof(data));
* 	if (ret == 1) { //TODO 生成成功的后续操作 }
* }
*
*/

/**
* 因为是反向验证程序(从Wireshark捕捉的UDP数据验证CRC),所以该程度的具体功能如下
* 1. 读取一包Wireshark/tcpdump捕捉到的UDP数据包
* 2. 根据IP/UDP解释对应的数据包
* 3. 然后清除对应结构的CRC校验字段
* 4. 调用udpipgen函数,生成CRC
* 5. 控制台打印然后比对原来的CRC和新生成CRC,是否一致
*/
int main(int argc, char *argv[])
{
  FILE *fp;
  //注意这个缓冲区大小,如果解释大于1024字节的数据会出问题,请自行修改
  unsigned char buff[1024] = {0};

  if (argc < 2)
  {
    printf("Usage: %s package\n", argv[0]);
    exit(0);
  }
	
	//argv[1]: 表示Wireshark抓的数据包,只解释一包,批量请自行修改
  fp = fopen(argv[1], "rb");
  if (fp == NULL)
  {
    perror("open file failure!!!");
    exit(-1);
  }
  else
  {
    // fseek(fp,18,SEEK_SET);
    size_t nbytes = fread(buff, 1, 1024, fp);
    if (nbytes < 0)
    {
      perror("read file error!!");
      fclose(fp);
      fp = NULL;
      exit(-1);
    }
    else
    {
      unsigned char dup[nbytes] = {0};
      //复制数据帧
      memcpy(dup, buff, nbytes);

      struct ip *ip = (struct ip *)(buff);
      struct udphdr *udp = (struct udphdr *)(buff + IPHL);
      unsigned short ip_sum = ip->ip_sum;
      unsigned short udp_sum = udp->uh_sum;
      printf("ip sum: %x, udp sum: %x\n", ip_sum, udp_sum);

      struct ip *ip2 = (struct ip *)(dup);
      struct udphdr *udp2 = (struct udphdr *)(dup + IPHL);
      printf("ip2 sum: %x, udp2 sum: %x\n", ip2->ip_sum, udp2->uh_sum);
      //重置复制帧的CRC字段
      ip2->ip_sum = 0;
      udp2->uh_sum = 0;

      char saddr[256] = {0};
      char daddr[256] = {0};
      inet_ntop(AF_INET, (void *)&ip->ip_src.s_addr, saddr, 256);
      printf("ip_src.s_addr : %s ,%d ", saddr, ntohs(udp->uh_sport));
      memset(daddr, 0, 256);
      inet_ntop(AF_INET, (void *)&ip->ip_dst.s_addr, daddr, 256);
      printf("ip_dst.s_addr : %s ,%d\n", daddr, ntohs(udp->uh_dport));
      // udp->len = UDP Header长度(8个字节) + 数据长度,因为Wireshark捕捉的数据包都是网络字节序的,所以需要转换一下
      printf("data length : %d\n", ntohs(udp->len) - 8);
      memset((void *)ip2, 0, sizeof(struct ip));
      // 调用函数,计算IP结构、UDP结构的CRC字段
      udpipgen(dup, saddr, daddr, ntohs(udp->uh_sport), ntohs(udp->uh_dport), ntohs(udp->uh_ulen) - 8);
      printf("Calc ip sum: %x, udp sum: %x\n", ip2->ip_sum, udp2->uh_sum);

      fclose(fp);
      fp = NULL;
    }
  }
}

验证方法

将生成的数据通过UDP发送出去,利用Wireshark/tcpdump抓包,然后通过Wireshark打开
右键呼出菜单,设置自动校验,通过Wireshark的自动校验来验证CRC生成是否正确
在这里插入图片描述

参考链接

手动组UDP/TCP包时计算 CRC 校验碰到的问题。
C语言数据类型在不同位数平台下的字节长度
TCP/IP详解V2(三)之TCP协议
UDP的伪首部是什么?

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

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

相关文章

西工大计算机学院计算机系统基础实验一(函数编写15~17)

还是那句话&#xff0c;稳住心态&#xff0c;稳住心态&#xff0c;稳住心态。心里别慌&#xff0c;心里别慌&#xff0c;心里别慌。 第15题&#xff0c;howManyBits&#xff0c;返回用二进制补码形式表示x所需的最小二进制位数。比如howManyBits(12) 5&#xff0c;12可以被表…

轨迹分析:Palantir评估细胞分化潜能 类似于monocle2

轨迹分析是单细胞测序分析中重要的组成部分&#xff0c;它基于细胞谱系之间“具有中间态细胞”的理论基础&#xff0c;通过结合先验知识&#xff08;细胞注释、markers&#xff09;、细胞基因表达改变等&#xff0c;为在单细胞测序数据赋予了“假时间”&#xff08;pseudotime&…

vscode eide arm-gcc 编译环境搭建调试

安装cube&#xff0c;vscode 1.安装vscode插件 C/C Extension Pack Chinese (Simplified) (简体中文) Language Pack Cortex-Debug Embedded IDE 工具链设置 2.软件工程生成 调试 3.生成工程&#xff0c;导入工程 4. 配置工程 编译完毕

【EI会议征稿】第五届大数据与信息化教育国际学术会议(ICBDIE 2024)

【往届检索】第五届大数据与信息化教育国际学术会议&#xff08;ICBDIE 2024&#xff09; 2023 5th International Conference on Big Data and Informatization Education 第五届大数据与信息化教育国际学术会议&#xff08;ICBDIE 2024&#xff09;定于2024年01月19-21日在…

小型洗衣机哪个牌子质量好?内衣洗衣机便宜好用的牌子

近些年来&#xff0c;由于人们对生活和健康的追求越来越高&#xff0c;所以内衣洗衣机也逐渐走进了人们的视线&#xff0c;许多研究显示&#xff0c;单纯地用手洗内衣是并不能彻底消除内衣物上所残留的细菌&#xff0c;而内衣洗衣机拥有着高温蒸煮的除菌功能&#xff0c;因此可…

【C语言】用户空间使用非缓存内存

在用户空间使用非缓存内存通常不是标准做法&#xff0c;因为非缓存内存的操作与硬件平台紧密相关&#xff0c;并且通常被保留给内核模块或设备驱动程序使用。 一、方法 用户空间程序一般不直接处理非缓存内存问题&#xff0c;因为它们依赖于操作系统来管理内存缓存一致性。尽…

智慧景区(园区)数字孪生可视化GIS解决方案

随着技术的日新月异&#xff0c;景区日常管理及运营中使用到的智慧化工具越来越丰富&#xff0c;智慧化硬件设备也越来越多&#xff0c;而其中各个管理系统往往又是相互独立&#xff0c;形成一个个数据孤岛。智慧景区管理平台就是将各个孤岛中的数据及功能汇集起来&#xff0c;…

叮!速来get宏基因组元素循环耦合分析!

微生物通过一系列氧化还原反应驱动生物地球化学循环&#xff0c;有的微生物可以耦合不同元素的生物地球化学循环&#xff0c;例如碳、氮、磷、硫等&#xff0c;存在复杂的耦合关系。 图 升高(A)和气候变暖(B)对氮库和转化过程影响的概念图 红树林生态系统被认为是生物地球化学…

Elasticsearch:什么是大语言模型(LLM)?

大语言模型定义 大语言模型 (LLM) 是一种深度学习算法&#xff0c;可以执行各种自然语言处理 (natural language processing - NLP) 任务。 大型语言模型使用 Transformer 模型&#xff0c;并使用大量数据集进行训练 —— 因此规模很大。 这使他们能够识别、翻译、预测或生成文…

HASH 哈希算法之MD5 算法

1. 哈希算法&#xff0c;用C 写的 #include <iostream> #include <iomanip> #include <cstring> #include <openssl/md5.h> #include <stdio.h>using namespace std;int main() {string str "hello world";unsigned char digest[MD5…

网络安全(一)--网络环境构成,系统的安全

2. 网络攻防环境 目标 了解攻防环境构成了解入侵检测系统&#xff08;平台&#xff09;的部署位置 2.1. 环境构成 2.1.1. 环境框图 一个基本的网络攻防实验环境包括&#xff1a;靶机、攻击机、入侵检测分析系统、网络连接四部分组成。 一个基础的网络攻防实验环境需要如下…

【c语言指针详解】指针的高级应用

目录 一、指针和字符串 1.1 字符串和字符数组的关系 1.2 字符串常量和字符指针的使用 1.3 字符串函数库的指针参数 1.3.1 strlen 函数 1.3.2 strcpy 函数 1.3.3 strcat 函数 1.3.4 strcmp 函数 二、指针的高级应用 2.1 函数指针的定义和使用 2.2 回调函数和函数指针数组的应用 …

2022年第十一届数学建模国际赛小美赛D题野生动物贸易是否应长期禁止解题全过程文档及程序

2022年第十一届数学建模国际赛小美赛 D题 野生动物贸易是否应长期禁止 原题再现&#xff1a; 野生动物市场被怀疑是此次疫情和2002年SARS疫情的源头&#xff0c;食用野生肉类被认为是非洲埃博拉病毒的一个来源。在冠状病毒爆发后&#xff0c;中国最高立法机构永久性地加强了野…

基于SpringBoot的大学活动平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着互联网技术的不断…

如何统计12.5米高程覆盖率?

无论是卫星影像还是高程DEM数据&#xff0c;覆盖率都是大家非常关心的一个重要参数。 我们曾基于WGS84坐标进行过简单的覆盖率计算&#xff0c;而且面积还包括了海洋区域。 因此&#xff0c;最后得出了一个非常不靠谱&#xff0c;看起来也很不漂亮的数据&#xff1a;12%。 为…

【算法】对二分搜索的理解

二分搜索大家都很熟悉&#xff0c;首先我们先来看看基本框架 func binarySearch(nums []int, target int) int {left, right : 0, ...for ... {mid : left (right-left)/2if nums[mid] target {...} else if nums[mid] < target {left ...} else if nums[mid] > targ…

快速安装Axure RP Extension for Chrome插件

打开原型文件的html&#xff0c;会跳转到这个页面&#xff0c;怎么破&#xff1f; 我们点开产品设计的原型图如果没有下载Axure插件是打不开&#xff0c;而我们国内网通常又不能再google商店搜索对应插件&#xff0c;下面教大家如何快速安装 1、打开原型文件->resources-&g…

星钻图形输出

答案&#xff1a; #include <stdio.h> int a 0, b 0; void printLine(int a , int b) //输出一行包含&#xff1a;若干个空格 若干个*&#xff0c;第一&#xff0c;二个参数为空格数和*数&#xff1b; (定义一个星钻输出函数) {while (a--) //打印a个空格{printf(…

内衣洗衣机和手洗哪个干净?高性价比内衣洗衣机推荐

通常来说&#xff0c;我们的内衣裤对卫生要求比较高&#xff0c;毕竟是贴身穿的&#xff0c;所以如果和一般的衣物一起洗&#xff0c;就怕会有细菌互相感染。所以很多用户为了内衣裤的卫生都会选择自己手动洗&#xff0c;但手洗一方面很费时间和人力&#xff0c;另一方面又很伤…

el-menu标题过长显示不全问题处理

项目基于vue-element-admin 问题 期望 处理方式 \src\layout\components\Sidebar\index.vue 文件后添加CSS <style scped> /* 侧栏导航菜单经典 文字超长溢出问题 CSS折行 */ .el-submenu__title {display: flex;align-items: center; } .el-submenu__title span {white-…