通过前面的分析,可以发现,网络协议栈中的数据处理,都是基于各类结构体,所有有关于网络栈中数据包的状态,地址,端口等信息都封装在对应的结构中,可以说,了解这些数据结构是理解网络栈源码的基础,这里我们就来了解下网络协议栈中的各类数据结构.Linux 1.2.13
1,socket
(include\linux\Socket.h)该结构体 socket 主要使用在 BSD socket 层,是最上层的结构,在 INET socket 层也会有涉及,但很少.
/*
* Internal representation of a socket. not all the fields are used by
* all configurations:
*
* server client
* conn client connected to server connected to
* iconn list of clients -unused-
* awaiting connections
* wait sleep for clients, sleep for connection,
* sleep for i/o sleep for i/o
*/
//该结构表示一个网络套接字
struct socket {
short type;
/* 套接字所用的流类型*/
socket_state state; //套接字所处状态
long flags; //标识字段,目前尚无明确作用
struct proto_ops * ops;
/* 操作函数集指针 */
/* data保存指向'私有'数据结构指针,在不同的域指向不同的数据结构 */
//在INET域,指向sock结构,UNIX域指向unix_proto_data结构
void * data;
//下面两个字段只用于UNIX域
struct socket * conn;
/* 指向连接的对端套接字 */
struct socket * iconn;
/* 指向正等待连接的客户端(服务器端) */
struct socket * next; //链表
struct wait_queue * *wait;
/* 等待队列 */
struct inode * inode; //inode结构指针
struct fasync_struct * fasync_list;
/* 异步唤醒链表结构 */
};
2,
sock
(include\linux\Net.h) sock 的使用范围比 socket 要大得多,sock 结构的使用基本贯穿硬件层,设备接口层,ip 层,INET socket 层,而且是作为各层之间的一个联系,主要是因为无论是发送还是接收的数据包都要被缓存到 sock 结构中的缓冲队列中.
sock 结构与其对应的 socket 会相互绑定.
/*
* This structure really needs to be cleaned up.
* Most of it is for TCP, and not used by any of
* the other protocols.
* 大部分功能是为TCP准备的
*/
struct sock {
struct options * opt; //IP选项缓冲于此处
volatile unsigned long wmem_alloc; //发送缓冲队列中存放的数据的大小,这两个与后面的rcvbuf和sndbuf一起使用
volatile unsigned long rmem_alloc; //接收缓冲队列中存放的数据的大小
/* 下面三个seq用于TCP协议中为保证可靠数据传输而使用的序列号 */
unsigned long write_seq; //
unsigned long sent_seq; //
unsigned long acked_seq; //
unsigned long copied_seq; //应用程序有待读取(但尚未读取)数据的第一个序列号
unsigned long rcv_ack_seq; //目前本地接收到的对本地发送数据的应答序列号
unsigned long window_seq; //窗口大小
unsigned long fin_seq; //应答序列号
//下面两个字段用于紧急数据处理
unsigned long urg_seq; //紧急数据最大序列号
unsigned long urg_data; //标志位,1表示收到紧急数据
/*
* Not all are volatile, but some are, so we
* might as well say they all are.
*/
volatile char inuse,
//表示其他进程正在使用该sock结构,本进程需等待
dead,
//表示该sock结构已处于释放状态
urginline,
//=1,表示紧急数据将被当做普通数据处理
intr,
//
blog,
done,
reuse,
keepopen,
//=1,使用保活定时器
linger,
//=1,表示在关闭套接字时需要等待一段时间以确认其已关闭
delay_acks,
//=1,表示延迟应答
destroy,
//=1,表示该sock结构等待销毁
ack_timed,
no_check,
zapped,
/* In ax25 & ipx means not linked */
broadcast,
nonagle; //=1,表示不使用NAGLE算法
//NAGLE算法:在前一个发送的数据包被应答之前,不可再继续发送其它数据包
unsigned long lingertime; //等待关闭操作的时间
int proc; //该sock结构所属的进程的进程号
struct sock * next;
struct sock * prev;
/* Doubly linked chain.. */
struct sock * pair;
//下面两个字段用于TCP协议重发队列
struct sk_buff * volatile send_head; //这个队列中的数据均已经发送出去,但尚未接收到应答
struct sk_buff * volatile send_tail;
struct sk_buff_head back_log; //接收的数据包缓存队列,当套接字正忙时,数据包暂存在这里
struct sk_buff * partial; //用于创建最大长度的待发送数据包
struct timer_list partial_timer; //定时器,用于按时发送partial指针指向的数据包
long retransmits; //重发次数
struct sk_buff_head write_queue,
//指向待发送数据包
receive_queue; //读队列,表示数据报已被正式接收,该队列中的数据可被应用程序读取?
struct proto * prot; //传输层处理函数集
struct wait_queue * *sleep;
unsigned long daddr; //sock结构所代表套接字的远端地址
unsigned long saddr; //本地地址
unsigned short max_unacked; //最大未处理请求连接数
unsigned short window; //远端窗口大小
unsigned short bytes_rcv; //已接收字节总数
/* mss is min(mtu, max_window) */
unsigned short mtu; //和链路层协议密切相关 /* 最大传输单元 */
volatile unsigned short mss; //最大报文长度 =mtu-ip首部长度-tcp首部长度,也就是tcp数据包每次能够传输的最大数据分段
volatile unsigned short user_mss;
/* mss requested by user in ioctl */
volatile unsigned short max_window; //最大窗口大小
unsigned long window_clamp; //窗口大小钳制值
unsigned short num; //本地端口号
//下面三个字段用于拥塞算法
volatile unsigned short cong_window;
volatile unsigned short cong_count;
volatile unsigned short ssthresh;
volatile unsigned short packets_out; //本地已发送出去但尚未得到应答的数据包数目
volatile unsigned short shutdown; //本地关闭标志位,用于半关闭操作
volatile unsigned long rtt; //往返时间估计值
volatile unsigned long mdev; //绝对偏差
volatile unsigned long rto; //用rtt和mdev 用算法计算出的延迟时间值
/* currently backoff isn't used, but I'm maintaining it in case
* we want to go back to a backoff formula that needs it
*/
volatile unsigned short backoff; //退避算法度量值
volatile short err; //错误标志值
unsigned char protocol; //传输层协议值
volatile unsigned char state; //套接字状态值
volatile unsigned char ack_backlog; //缓存的未应答数据包个数
unsigned char max_ack_backlog; //最大缓存的未应答数据包个数
unsigned char priority; //该套接字优先级
unsigned char debug;
unsigned short rcvbuf; //最大接收缓冲区大小
unsigned short sndbuf; //最大发送缓冲区大小
unsigned short type; //类型值如 SOCK_STREAM
unsigned char localroute; //=1,表示只使用本地路由 /* Route locally only */
#ifdef CONFIG_IPX ipx_address ipx_dest_addr;
ipx_interface * ipx_intrfc;
unsigned short ipx_port;
unsigned short ipx_type;#endif#ifdef CONFIG_AX25
/* Really we want to add a per protocol private area */
ax25_address ax25_source_addr,
ax25_dest_addr;
struct sk_buff * volatile ax25_retxq[8];
char ax25_state,
ax25_vs,
ax25_vr,
ax25_lastrxnr,
ax25_lasttxnr;
char ax25_condition;
char ax25_retxcnt;
char ax25_xx;
char ax25_retxqi;
char ax25_rrtimer;
char ax25_timer;
unsigned char ax25_n2;
unsigned short ax25_t1,
ax25_t2,
ax25_t3;
ax25_digi * ax25_digipeat;#endif#ifdef CONFIG_ATALK struct atalk_sock at;#endif
/* IP 'private area' or will be eventually */
int ip_ttl; //ip首部ttl字段值,实际上表示路由器跳数 /* TTL setting */
int ip_tos; //ip首部tos字段值,服务类型值 /* TOS */
struct tcphdr dummy_th; //缓存的tcp首部,在tcp协议中创建一个发送数据包时可以利用此字段快速创建tcp首部
struct timer_list keepalive_timer; //保活定时器,用于探测对方窗口大小,防止对方通报窗口大小的数据包丢弃 /* TCP keepalive hack */
struct timer_list retransmit_timer; //重发定时器,用于数据包超时重发 /* TCP retransmit timer */
struct timer_list ack_timer; //延迟应答定时器 /* TCP delayed ack timer */
int ip_xmit_timeout; //表示定时器超时原因 /* Why the timeout is running */
//用于ip多播
#ifdef CONFIG_IP_MULTICAST int ip_mc_ttl;
/* Multicasting TTL */
int ip_mc_loop;
/* Loopback (not implemented yet) */
char ip_mc_name[MAX_ADDR_LEN];
/* Multicast device name */
struct ip_mc_socklist * ip_mc_list;
/* Group array */
#endif
/* This part is used for the timeout functions (timer.c). */
int timeout;
/* What are we waiting for? */
struct timer_list timer;
/* This is the TIME_WAIT/receive timer when we are doing IP */
struct timeval stamp;
/* identd */
//一个套接在在不同的层次上分别由socket结构和sock结构表示
struct socket * socket;
/* Callbacks */
//回调函数
void( * state_change)(struct sock * sk);
void( * data_ready)(struct sock * sk, int bytes);
void( * write_space)(struct sock * sk);
void( * error_report)(struct sock * sk);
};
3,
sk_buff
(include\linux\Skbuff.h) sk_buff 是网络数据报在内核中的表现形式,通过源码可以看出,数据包在内核协议栈中是通过这个数据结构来变现的.
从其中的 union 字段可以看出,该结构是贯穿在各个层的,可以说这个结构是用来为网络数据包服务的.其中的字段表明了数据包隶属的套接字,当前所处的协议层,所搭载的数据负载长度(data 指针指向),源端,目的端地址以及相关字段等.
主要重要的一个字段是 data[0],这是一个指针,它指向对应层的数据报(首部 + 数据负载)内容的首地址.怎么解释呢?
如果在传输层,那么 data 指向的数据部分的首地址,其数据部分为 TCP 首部 + 有效数据负载.
如果在网络层,data 指向的数据部分的首地址,其数据部分为 IP 首部 + TCP 首部 + 有效数据负载.
如果在链路层,data 指向的首地址,其数据布局为 MAC 首部 + IP 首部 + TCP 首部 + 有效数据负载.
所以在该 skb_buff 结构传递时,获取某一层的首部,都是通过拷贝 data 指向地址对应首部大小的数据.
//sk_buff 结构用来封装网络数据
//网络栈代码对数据的处理都是以sk_buff 结构为单元进行的
struct sk_buff {
struct sk_buff * volatile next;
struct sk_buff * volatile prev; //构成队列
#
if CONFIG_SKB_CHECK int magic_debug_cookie; //调试用
#endif struct sk_buff * volatile link3; //构成数据包重发队列
struct sock * sk; //数据包所属的套接字
volatile unsigned long when; //数据包的发送时间,用于计算往返时间RTT/* used to compute rtt's */
struct timeval stamp; //记录时间
struct device * dev; //接收该数据包的接口设备
struct sk_buff * mem_addr; //该sk_buff在内存中的基地址,用于释放该sk_buff结构
//联合类型,表示数据报在不同处理层次上所到达的处理位置
union {
struct tcphdr * th; //传输层tcp,指向首部第一个字节位置
struct ethhdr * eth; //链路层上,指向以太网首部第一个字节位置
struct iphdr * iph; //网络层上,指向ip首部第一个字节位置
struct udphdr * uh; //传输层udp协议,
unsigned char * raw; //随层次变化而变化,链路层=eth,网络层=iph
unsigned long seq; //针对tcp协议的待发送数据包而言,表示该数据包的ACK值
}
h;
struct iphdr * ip_hdr; //指向ip首部的指针 /* For IPPROTO_RAW */
unsigned long mem_len; //表示sk_buff结构大小加上数据部分的总长度
unsigned long len; //只表示数据部分长度,len = mem_len - sizeof(sk_buff)
unsigned long fraglen; //分片数据包个数
struct sk_buff * fraglist;
/* Fragment list */
unsigned long truesize; //同men_len
unsigned long saddr; //源端ip地址
unsigned long daddr; //目的端ip地址
unsigned long raddr; //数据包下一站ip地址 /* next hop addr */
//标识字段
volatile char acked,
//=1,表示数据报已得到确认,可以从重发队列中删除
used,
//=1,表示该数据包的数据已被应用程序读完,可以进行释放
free,
//用于数据包发送,=1表示再进行发送操作后立即释放,无需缓存
arp; //用于待发送数据包,=1表示已完成MAC首部的建立,=0表示还不知道目的端MAC地址
//已进行tries试发送,该数据包正在被其余部分使用,路由类型,数据包类型
unsigned char tries,
lock,
localroute,
pkt_type;
//下面是数据包的类型,即pkt_type的取值
#define PACKET_HOST 0 //发往本机 /* To us */
#define PACKET_BROADCAST 1 //广播
#define PACKET_MULTICAST 2 //多播
#define PACKET_OTHERHOST 3 //其他机器 /* Unmatched promiscuous */
unsigned short users; //使用该数据包的模块数 /* User count - see datagram.c (and soon seqpacket.c/stream.c) */
unsigned short pkt_class;
/* For drivers that need to cache the packet type with the skbuff (new PPP) */
#ifdef CONFIG_SLAVE_BALANCING unsigned short in_dev_queue; //该字段是否正在缓存于设备缓存队列中
#endif unsigned long padding[0]; //填充字节
unsigned char data[0]; //指向该层数据部分
//data指向的数据负载首地址,在各个层对应不同的数据部分
//从侧面看出sk_buff结构基本上是贯穿整个网络栈的非常重要的一个数据结构
};
4,
device
(include\linux\Netdevice.h)该结构表明了一个网络设备需要的字段信息.
/*
* The DEVICE structure.
* Actually, this whole structure is a big mistake. It mixes I/O
* data with strictly "high-level" data, and it has to know about
* almost every data structure used in the INET module.
*/
//网络设备结构
struct device {
/*
* This is the first field of the "visible" part of this structure
* (i.e. as seen by users in the "Space.c" file). It is the name
* the interface.
*/
char * name; //设备名称
/* I/O specific fields - FIXME: Merge these and struct ifmap into one */
unsigned long rmem_end; //设备读缓冲区空间 /* shmem "recv" end */
unsigned long rmem_start;
/* shmem "recv" start */
unsigned long mem_end; //设备总缓冲区首地址和尾地址 /* sahared mem end */
unsigned long mem_start;
/* shared mem start */
unsigned long base_addr; //设备寄存器读写IO基地址 /* device I/O address */
unsigned char irq; //设备所使用中断号 /* device IRQ number */
/* Low-level status flags. */
volatile unsigned char start,
//=1,表示设备已处于工作状态 /* start an operation */
tbusy,
//=1,表示设备正忙于数据包发送 /* transmitter busy */
interrupt; //=1,软件正在进行设备中断处理 /* interrupt arrived */
struct device * next; //构成设备队列
/* The device initialization function. Called only once. */
int( * init)(struct device * dev); //设备初始化指针(函数指针)
/* Some hardware also needs these fields, but they are not part of the
usual set specified in Space.c. */
unsigned char if_port; //指定使用的设备端口号 /* Selectable AUI, TP,..*/
unsigned char dma; //设备所用的dma通道号 /* DMA channel */
struct enet_statistics * ( * get_stats)(struct device * dev); //设备信息获取函数指针
/*
* This marks the end of the "visible" part of the structure. All
* fields hereafter are internal to the system, and may change at
* will (read: may be cleaned up at will).
*/
/* These may be needed for future network-power-down code. */
unsigned long trans_start; //用于传输超时计算 /* Time (in jiffies) of last Tx */
unsigned long last_rx; //上次接收一个数据包的时间 /* Time of last Rx */
unsigned short flags; //标志位 /* interface flags (a la BSD) */
unsigned short family; //设备所属的域协议 /* address family ID (AF_INET) */
unsigned short metric;
/* routing metric (not used) */
unsigned short mtu; //该接口设备的最大传输单元,ip首部+tcp首部+有效数据负载,去掉了以太网帧的帧头 /* interface MTU value*/
unsigned short type; //该设备所属硬件类型 /* interface hardware type */
unsigned short hard_header_len; //硬件首部长度 /* hardware hdr length */
void * priv; //私有数据指针 /* pointer to private data */
/* Interface address info. */
unsigned char broadcast[MAX_ADDR_LEN]; //链路层硬件广播地址 /* hw bcast add */
unsigned char dev_addr[MAX_ADDR_LEN]; //本设备硬件地址 /* hw address */
unsigned char addr_len; //硬件地址长度 /* hardware address length */
unsigned long pa_addr; //本地ip地址 /* protocol address */
unsigned long pa_brdaddr; //网络层广播ip地址 /* protocol broadcast addr */
unsigned long pa_dstaddr; //点对点网络中对点的ip地址 /* protocol P-P other side addr */
unsigned long pa_mask; //ip地址网络掩码 /* protocol netmask */
unsigned short pa_alen; //ip地址长度 /* protocol address length */
struct dev_mc_list * mc_list; //多播地址链表 /* Multicast mac addresses */
int mc_count; //多播地址数目 /* Number of installed mcasts */
struct ip_mc_list * ip_mc_list; //网络层ip多播地址链表 /* IP multicast filter chain */
/* For load balancing driver pair support */
unsigned long pkt_queue; //该设备缓存的待发送的数据包个数 /* Packets queued */
struct device * slave; //从设备 /* Slave device */
/* Pointer to the interface buffers. */
struct sk_buff_head buffs[DEV_NUMBUFFS]; //设备缓存的待发送的数据包
//函数指针
/* Pointers to interface service routines. */
int( * open)(struct device * dev);
int( * stop)(struct device * dev);
int( * hard_start_xmit)(struct sk_buff * skb, struct device * dev);
int( * hard_header)(unsigned char * buff, struct device * dev, unsigned short type, void * daddr, void * saddr, unsigned len, struct sk_buff * skb);
int( * rebuild_header)(void * eth, struct device * dev, unsigned long raddr, struct sk_buff * skb);
//用于从接收到的数据包提取MAC首部中类型字符值,从而将数据包传送给适当的协议处理函数进行处理
unsigned short( * type_trans)(struct sk_buff * skb, struct device * dev);#define HAVE_MULTICAST void( * set_multicast_list)(struct device * dev, int num_addrs, void * addrs);#define HAVE_SET_MAC_ADDR int( * set_mac_address)(struct device * dev, void * addr);#define HAVE_PRIVATE_IOCTL int( * do_ioctl)(struct device * dev, struct ifreq * ifr, int cmd);#define HAVE_SET_CONFIG int( * set_config)(struct device * dev, struct ifmap * map);
};
5,tcp 首部格式
//tcp首部格式
//http://blog.csdn.net/wenqian1991/article/details/44598537
struct tcphdr {
__u16 source; //源端口号
__u16 dest; //目的端口号
__u32 seq; //32位序列号
__u32 ack_seq; //32位确认号
#
if defined(LITTLE_ENDIAN_BITFIELD) __u16 res1: 4,
//4位首部长度
doff: 4,
//保留
//下面为各个控制位
fin: 1,
//最后控制位,表示数据已全部传输完成
syn: 1,
//同步控制位
rst: 1,
//重置控制位
psh: 1,
//推控制位
ack: 1,
//确认控制位
urg: 1,
//紧急控制位
res2: 2; //
#elif defined(BIG_ENDIAN_BITFIELD) __u16 doff: 4,
res1: 4,
res2: 2,
urg: 1,
ack: 1,
psh: 1,
rst: 1,
syn: 1,
fin: 1;#
else#error "Adjust your <asm/byteorder.h> defines"#endif __u16 window; //16位窗口大小
__u16 check; //16位校验和
__u16 urg_ptr; //16位紧急指针
};
6,ip 首部格式
//ip数据报,首部格式
struct iphdr {#
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 __u8 tos; //区分服务,用语表示数据报的优先级和服务类型
__u16 tot_len; //总长度,标识整个ip数据报的总长度 = 报头+数据部分
__u16 id; //表示ip数据报的标识符
__u16 frag_off; //片偏移
__u8 ttl; //生存时间,即ip数据报在网络中传输的有效期
__u8 protocol; //协议,标识此ip数据报在传输层所采用的协议类型
__u16 check; //首部校验和
__u32 saddr; //源地址
__u32 daddr; //目的地址
/*The options start here. */
};
7,以太网帧帧头格式
/* This is an Ethernet frame header. */
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; //目的地址 /* destination eth addr */
unsigned char h_source[ETH_ALEN]; //源地址 /* source ether addr */
unsigned short h_proto; //类型 /* packet type ID field */
};
8,ARP 报文报头
/*
* This structure defines an ethernet arp header.
*/
//ARP报文格式(arp报头)
struct arphdr {
unsigned short ar_hrd; //硬件类型 /* format of hardware address */
unsigned short ar_pro; //上层协议类型 /* format of protocol address */
unsigned char ar_hln; //MAC地址长度 /* length of hardware address */
unsigned char ar_pln; //协议地址长度 /* length of protocol address */
unsigned short ar_op; //操作类型 /* ARP opcode (command) */
#
if 0
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; //源MAC地址 /* sender hardware address */
unsigned char ar_sip[4]; //源IP地址 /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; //目的MAC地址 /* target hardware address */
unsigned char ar_tip[4]; //目的IP地址 /* target IP address */
#endif
};
来源: http://lib.csdn.net/article/linux/36369