【Linux 内核网络协议栈源码剖析】网络栈主要结构介绍(socket、sock、sk_buff,etc)
原文:http://blog.csdn.net/wenqian1991/article/details/46700177
通过前面的分析,可以发现,网络协议栈中的数据处理,都是基于各类结构体,所有有关于网络栈中数据包的状态,地址,端口等信息都封装在对应的结构中,可以说,了解这些数据结构是理解网络栈源码的基础,这里我们就来了解下网络协议栈中的各类数据结构。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
- };