本文共 3456 字,大约阅读时间需要 11 分钟。
这里在head.h的头文件中定义了以太网,IP, PPPOE, ARP, TCP, UDP 等数据包头部的结构体,以便于对数据包进行解析,其实在库中有已经定义好的各个数据包头部,像ethhdr,iphdr,tcphdr等,这里自己写一遍可以加深对网络数据包的理解。
/****************************************************************************** 文 件 名 : head.h 版 本 号 : V1.1 负 责 人 : Sophisticated 生成日期 : 2018年9月27日 最近修改 : 文件描述 : 报文头部结构体 函数列表 : 修改历史 : 1.日 期 : 2018年9月27日 作 者 : Sophisticated 修改内容 : 创建文件******************************************************************************/#ifndef _HEAD_H_#define _HEAD_H_#include#include /*最大抓包长度 :Ethernet 1500字节 + 以太网帧头部14字节 + 以太网帧尾部4字节*/#define SNAP_LEN 1518/*ethernet head are exactly 14 bytes*/#define ETHERNET_HEAD_SIZE 14/*ip头部字节数宏 取hlv低四位即头部长度*单位4bytes 然后强转为ip结构体*///#define IP_HEAD_SIZE(ipheader) ((ipheader->ip_hlv & 0x0f) * 4)#define IP_HEAD_SIZE(packet) ((((struct ip *)(packet + ETHERNET_HEAD_SIZE))->ip_hlv & 0x0f) * 4)/*ethernet address are 6 bytes*/#define ETHERNET_ADDR_LEN 6/*ip address are 4 bytes*/#define IP_ADDR_LEN 4#define ARP_REQUEST 1#define ARP_REPLY 2/*TCP flag*/#define TCP_FIN 0x01#define TCP_SYN 0x02#define TCP_RST 0x04#define TCP_PUSH 0x08#define TCP_ACK 0x10#define TCP_URG 0x20#define TCP_ECE 0x40#define TCP_CWR 0x80/*Ethernet HEADER*/struct ethernet{ u_char ether_dhost[ETHERNET_ADDR_LEN]; u_char ether_shost[ETHERNET_ADDR_LEN]; u_short ether_type; //IP?ARP?etc};/*IP HEADER*/struct ip{ //unsigned int ip_version:4; //unsigned int ip_hlen:4; u_char ip_hlv; /*version + headlength 如果分开定义会有大小端问题,会增加额外的判断*/ u_char ip_tos; u_short ip_len; u_short ip_id; u_short ip_off; u_char ip_ttl; u_char ip_protocol; u_short ip_sum; u_char ip_src[IP_ADDR_LEN]; u_char ip_dst[IP_ADDR_LEN];};/*TCP HEADER*/struct tcp{ u_short tcp_sport; u_short tcp_dport; u_int tcp_seqe; u_int tcp_ack; //u_char tcp_off:4; //u_char tcp_unused:4; //保留位 u_char tcp_hre; //header(4bits) + reserved(4bits) u_char tcp_flag; u_short tcp_win; u_short tcp_sum; u_short tcp_urp;};/*UDP HEADER*/struct udp{ u_short udp_sport; u_short udp_dport; u_short udp_len; u_short udp_sum;};/*ARP HEADER 8+6+4+6+4*/struct arp{ u_short arp_hrd; //hardware u_short arp_pro; //protocol u_char arp_hdlen; //hardware address length u_char arp_prolen; //protocol length u_short arp_op; //arp operations u_char arp_shost[ETHERNET_ADDR_LEN]; u_char arp_sip[IP_ADDR_LEN]; u_char arp_dhost[ETHERNET_ADDR_LEN]; u_char arp_dip[IP_ADDR_LEN];};/*ICMP HEADER*/struct icmp{ u_char icmp_type; u_char icmp_code; u_short icmp_sum; u_short icmp_id; u_short icmp_seq; u_int icmp_time;};/*PPPOE HEADER*/struct pppoe{ u_char pppoe_vtype; //version(0x1) + type(0x1) u_char pppoe_code; u_short pppoe_s_id; u_short pppoe_len;}#endif
在定义IP数据包头结构的第一个字节时,即IP版本号和数据报头长度,我和源码中的做法不太一样,源码中是这样:
#if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4;#elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4;#else#error "Please fix"#endif......
而我是把版本号和报文头长度一起定义在了一个字节里,这样不用区分大小端问题,只是解析起来会麻烦些。
解析方法:ipheader->ip_hlv & 0xf0) >> 4取这个字节高四位即为版本号
ipheader->ip_hlv & 0x0f取这个字节低四位即为报文头长度
在定义tcp数据包头结构体时也是如此,我把标志位flag定义到一个字节里了,源码中是这样的:
#if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;#elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1;#else#error "Adjust yourdefines"#endif
解析方法见后面的tcp报文头部解析函数,同样是使用的&运算
转载地址:http://yuhpi.baihongyu.com/