版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Sophisticated_/article/details/83339048
这里在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<sys/types.h>
#include<netinet/in.h>
/*最大抓包长度 :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 <asm/byteorder.h>"
#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 your <asm/byteorder.h> defines"
#endif
解析方法见后面的tcp报文头部解析函数,同样是使用的&运算