上一篇:DIY TCP/IP 网络设备模块6
6. pdbuf模块的实现
pdbuf是payload buffer的简称,该模块为DIY TCP/IP提供buffer管理功能,与Linux Kernel的struct sk_buff类似。pdbuf模块提供buffer的动态分配和释放,为有效载荷分配内存时预留协议头部所需内存空间,同时保证有效载荷加上协议头部(不包括14个字节的以太网头部)的大小不超过以太网的最大协议传输单元1500字节。pdbuf模块还提供buffer的push,pop,insert,rewind等函数接口,方便buffer流经DIY TCP/IP上层模块时添加协议头部。pdbuf操作buffer的函数接口也考虑了IP数据帧的分片和重组。
pdbuf模块为有效载荷分配内存时预留协议头部所需内存空间,例如TCP模块向pdbuf模块申请32个字节存放待发送的TCP数据,pbbuf模块申请32字节的内存同时,还会申请TCP头部,IP头部以及以太网头部所需内存空间。所以在实现pdbuf数据结构之前,先来添加ARP, ICMP, IP, TCP 和UDP层的头文件,在这些头文件中定义各层协议头部的数据结构,以便pdbuf模块使用。
6.1 协议头部数据结构
本节为pdbuf模块提供各层协议头部数据结构的定义,协议头部的详细解释在介绍DIY TCP各模块的协议实现时引入。本节只是根据wireshark抓包的结果来定义协议头部数据结构。相对物理媒介来说,第5章实现的网络设备模块是layer2,下面就按照layer3和layer4的顺序,介绍各层协议头部的数据结构。Layer3包括ARP,IP, layer4包括ICMP, TCP和UDP。
ARP
wireshark抓包结果可以看出,ARP数据在以太网头部的后面。上图右半边的raw byte显示,ARP数据一共是28个字节,添加arp.h,定义arphdr_t数据结构。
#ifndef _ARP_H_
#define _ARP_H_
#define ARP_HW_TYPE 1 /* Ethernet */
#define ARP_PROTO_TYPE 0x0800 /* IPv4 */
#define ARP_HW_SZ 6 /* Hardware Address Length */
#define ARP_PROTO_SZ 4 /* Protocol Address Length */
/* ARP operation code */
enum {
ARP_REQUEST = 1,
ARP_REPLY = 2,
} arp_op_code;
/* ARP Packet */
typedef struct _arphdr {
unsigned short hw_type;
unsigned short proto_type;
unsigned char hw_sz;
unsigned char proto_sz;
unsigned short op_code;
unsigned char sender_mac[ARP_HW_SZ];
unsigned char sender_ip[ARP_PROTO_SZ];
unsigned char target_mac[ARP_HW_SZ];
unsigned char target_ip[ARP_PROTO_SZ];
} __attribute__ ((packed)) arphdr_t;
#endif
ARP头部各个字段的详细解释,在介绍DIY TCP/IP ARP模块的实现时引入。
IP
IP是网络层协议,紧跟在以太网头部后面,可以认为ARP和IP是同处于layer3的协议。从IP头部的protocol字段可以看出IP头部后面是UDP头部和UDP的有效载荷,raw byte显示IP头部一共是20个字节。添加ip.h,定义iphdr_t数据结构。
#ifndef _IP_H_
#define _IP_H_
#define IP_VERSION 4
#define IP_HDR_LEN 5
#define IP_FRAG_OFFSET 0x1FFF
#define REASSEMBLE_BUF_SZ ((1 << 16) - 1 - 20)
#define IP_PROTO_ICMP 0x01
#define IP_PROTO_TCP 0x06
#define IP_PROTO_UDP 0x11
/* ipv4 header, 20 bytes */
typedef struct _iphdr {
/* version and header length */
unsigned char hdr_len:4;
unsigned char ver:4;
/* type of service */
unsigned char tos;
/* total length: hdr_len + payload len */
unsigned short total_len;
/* identification */
unsigned short id;
/* flags and fragment offset */
unsigned short flags_offset;
/* time to live */
unsigned char ttl;
/* payload protocol */
unsigned char proto;
/* header checksum */
unsigned short hdr_cksum;
/* source ip address */
unsigned char src_ip[4];
/* destination ip address */
unsigned char dst_ip[4];
} __attribute__((packed)) iphdr_t;
#endif
IP头部各个字段的详细解释,在介绍DIY TCP/IP IP模块的实现时引入。
ICMP
ICMP跟在IP头部的后面,raw byte显示一共是40字节,减去32个字节的数据,2个字节的identifier,2个字节的sequence number,icmp头部一共是4个字节,添加icmp.h,定义icmphdr_t数据结构。
#ifndef _ICMP_H_
#define _ICMP_H_
#define ICMP_TYPE_ECHO 0x8
#define ICMP_TYPE_ECHO_REPLY 0x0
#define ICMP_CODE 0x0
/* icmp header, 4 bytes */
typedef struct _icmphdr {
/* icmp type */
unsigned char type;
/* code */
unsigned char code;
/* checksum */
unsigned short cksum;
} __attribute__ ((packed)) icmphdr_t;
#endif
ICMP头部各个字段的详细解释,在介绍DIY TCP/IP ICMP模块的实现时引入。
TCP
上图是一个TCP ACK数据帧,TCP头部紧跟在IP头部后面,从TCP头部的flags和tcp samgent len的长度可以看出,该TCP数据帧是一个不包含任何数据的TCP ACK,raw byte显示该TCP帧一共20个字节,全部都是tcp头部,添加tcp.h,定义tcphdr_t数据结构。
#ifndef _TCP_H_
#define _TCP_H_
typedef union _tcp_hdr_flags {
struct _bitmap {
unsigned short flags:12;
unsigned short hdr_len:4;
} bitmap;
unsigned short word_val;
} tcp_hdr_flags_t;
/* tcp header, 20 bytes, options, 12 bytes */
typedef struct _tcphdr {
/* source port */
unsigned short src_port;
/* destination port */
unsigned short dst_port;
/* sequence number */
unsigned int seq_num;
/* acknowledge number */
unsigned int ack_num;
/* tcp header length and flags */
tcp_hdr_flags_t hdr_flags;
/* window size */
unsigned short wnd_sz;
/* checksum */
unsigned short cksum;
/* urgent point */
unsigned short ugtp;
/* options
* variable in length, may occupy space at the end
* of TCP header and are a multiple of 8 bits in
* length. All options are included in the checksum
*/
unsigned char options[0];
} __attribute__ ((packed)) tcphdr_t;
#endif
TCP头部各个字段的详细解释,在介绍DIY TCP/IP TCP模块的实现时引入。
UDP
与TCP头部相比,UDP头部相对简单,UDP头部中的Length 97字节是UDP头部和89个字节的UDP数据的和。97减去89字节的UDP数据,与raw byte显示一致,UDP头部一共是8个字节,添加udp.h,定义udphdr_t数据结构。
#ifndef _UDP_H_
#define _UDP_H_
/* udp header, 8 bytes */
typedef struct _udphdr {
/* source port */
unsigned short src_port;
/* destination port */
unsigned short dst_port;
/* length: udp header length + payload length */
unsigned short len;
/* checksum */
unsigned short cksum;
} __attribute__ ((packed)) udphdr_t;
#endif
UDP头部各个字段的详细解释,在介绍DIY TCP/IP UDP模块的实现时引入。下节介绍pdbuf数据结构的定义时,将会用到本节的ARP,IP,ICMP,TCP,UDP头部数据结构的定义。
下一篇:DIY TCP/IP Payload Buffer模块1