linux网络源代码,Linux网络协议源代码分析 之 主要数据结构体

Linux对网络的看法可以分为如下几层:

网络层: IP协议栈的实现,完成路由的查找过程(主要处理skb)

Inet Socket层: 对IP包进行分组排序,实现QoS,传输层协议TCP/UDP协议栈的实现

使用sock{}类型数据来管理会话,数据主要放在sk_buff结构中

BSD Socket: 对于BSD Socket相关调用的实现,主要使用socket{}结构来存放连接

数据主要是放在msghdr{}结构中

msghdr结构

struct msghdr {

void * msg_name;

int msg_namelen;

struct iovec * msg_iov;

__kernel_size_t msg_iovlen;

void  *msg_control;

__kernel_size_t msg_controllen;

unsigned msg_flags;

};

msg_name: socket的名字,通常用NULL初始化

msg_iov: 发送或接收缓冲区地址数据

msg_iovlen: msg_iov中保存的数据包的个数

msg_flags: 保存数据接收时的控制标志,可以由应用

层控制,如果设置为MSG_DONTWAIT,则表示使用非阻塞方式收取数据包

struct iovec

{

void __user *iov_base;

__kernel_size_t iov_len;

};

实际上,msghdr中存储的iovec是一个数组,每个iovec都有自己单独的数据起始地址和

数据长度,以下的代码将skb->data(kdata)用copy_to_user传到用户态,用户态的BSD

Socket接口将分段的数据进行合并,组成一段连续的内存buffer.

int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)

{

while (len > 0) {

if (iov->iov_len) {

int copy = min_t(unsigned int, iov->iov_len, len);

if (copy_to_user(iov->iov_base, kdata, copy))

return -EFAULT;

kdata += copy;

len -= copy;

iov->iov_len -= copy;

iov->iov_base += copy;

}

iov++;

}

return 0;

}

关于SKB

SKB结构中最大的变化莫过于将union舍弃,也就是说,由

union {

struct tcphdr *th;

struct udphdr *uh;

struct icmphdr *icmph;

struct igmphdr *igmph;

struct iphdr *ipiph;

struct ipv6hdr *ipv6h;

unsigned char *raw;

} h;

union {

struct iphdr *iph;

struct ipv6hdr *ipv6h;

struct arphdr *arph;

unsigned char *raw;

} nh;

union {

unsigned char *raw;

} mac;

变成了

sk_buff_data_t transport_header;

sk_buff_data_t network_header;

sk_buff_data_t mac_header;

结构变得更加的简单了,对于获取IP头的操作,发生了如下

变化

在linux 2.4~linux 2.6.20下:

struct iphdr *ip = skb->nh.iph;

到了linux 2.6.22下,就变成了:

struct iphdr *ip = ip_hdr(skb);

这里是函数ip_hdr的定义,调用了skb_network_header

函数用以获取IP头:

static inline struct iphdr *ip_hdr(const struct sk_buff *skb)

{

return (struct iphdr *)skb_network_header(skb);

}

但是函数skb_network_header又有不同的实现,依赖于宏NET_SKBUFF_DATA_USES_OFFSET是否进行了定义

#if BITS_PER_LONG > 32

#define NET_SKBUFF_DATA_USES_OFFSET 1

#endif

这里完成了NET_SKBUFF_DATA_USES_OFFSET宏的定义,其实很简单,只是看目前

的系统是32位系统还是64位系统

#ifdef NET_SKBUFF_DATA_USES_OFFSET

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

return skb->head + skb->transport_header;

}

#else

static inline unsigned char *skb_network_header(const struct sk_buff *skb)

{

return skb->network_header;

}

以上是*skb_network_header函数的实现,可以看到,对于32位系统和64位系统,skb_network_header

函数有不同的实现,所以在编程中,直接调用ip_hdr函数是好的,而不要直接通过skb->network_header

获取IP Header

对于传输层协议,也应该调用tcp_hdr或者udp_hdr来进行解析,而不要调用skb中的transport_header

static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)

{

return (struct tcphdr *)skb_transport_header(skb);

}

static inline struct udphdr *udp_hdr(const struct sk_buff *skb)

{

return (struct udphdr *)skb_transport_header(skb);

}

#ifdef NET_SKBUFF_DATA_USES_OFFSET

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

return skb->head + skb->transport_header;

}

#else

static inline unsigned char *skb_transport_header(const struct sk_buff *skb)

{

return skb->transport_header;

}

如果tcph没有解析出来的话,依然可以用 ip_hdr(skb)->ihl*4找到tcp header的偏移

skb常用的操作

实际上在head和data,tail和end之间,存在着空洞,所以SKB可以向上或者向下进行扩展

skb_put() 将数据添加到现有数据的尾部

skb_push() 将数据添加到现有数据的头部

skb_pull() 从数据头部开始缩减

skb_trim() 从数据尾部开始缩减

socket结构

socket结构主要用于BSD Socket层存储连接信息,应用程序需要一个文件描述符来和socket结构

进行对应

struct socket {

socket_state state;

unsigned long flags;

const struct proto_ops *ops;

struct fasync_struct *fasync_list;

struct file *file;

struct sock *sk;

wait_queue_head_t wait;

short type;

};

这个结构在2.6.14和2.6.22中没有变化

socket_state描述了连接的状态,可以是以下枚举类型

typedef enum {

SS_FREE = 0,

SS_UNCONNECTED,

SS_CONNECTING,

SS_CONNECTED,

SS_DISCONNECTING

} socket_state;

flags:用户层的一些控制信息

在2.4内核中,使用一个inode结构来进行文件描述符和一个socket之间的对应,所以在socket

结构中有一个inode成员,2.6内核中舍弃了这一成员,在socket创建函数sock_alloc中,使用

SOCKET_I函数根据新生成的inode结构返回相对应的socket结构,以后inode结构就再也用不着了.

file:用于垃圾收集

sk:指定了Inet socket层中sock结构的指针,同时在sock结构中也包含了socket函数的指针,

所以sock和socket结构是一一对应的关系

sock结构

struct sock {

struct sock_common __sk_common;

#define sk_family __sk_common.skc_family

#define sk_state __sk_common.skc_state

#define sk_reuse __sk_common.skc_reuse

#define sk_bound_dev_if __sk_common.skc_bound_dev_if

#define sk_node __sk_common.skc_node

#define sk_bind_node __sk_common.skc_bind_node

#define sk_refcnt __sk_common.skc_refcnt

#define sk_hash __sk_common.skc_hash

#define sk_prot __sk_common.skc_prot

unsigned char sk_shutdown : 2,

sk_no_check : 2,

sk_userlocks : 4;

unsigned char sk_protocol;

unsigned short sk_type;

int sk_rcvbuf;

socket_lock_t sk_lock;

struct {

struct sk_buff *head;

struct sk_buff *tail;

} sk_backlog;

wait_queue_head_t *sk_sleep;

struct dst_entry *sk_dst_cache;

struct xfrm_policy *sk_policy[2];

rwlock_t sk_dst_lock;

atomic_t sk_rmem_alloc;

atomic_t sk_wmem_alloc;

atomic_t sk_omem_alloc;

int sk_sndbuf;

struct sk_buff_head sk_receive_queue;

struct sk_buff_head sk_write_queue;

struct sk_buff_head sk_async_wait_queue;

int sk_wmem_queued;

int sk_forward_alloc;

gfp_t sk_allocation;

int sk_route_caps;

int sk_gso_type;

int sk_rcvlowat;

unsigned long  sk_flags;

unsigned long sk_lingertime;

struct sk_buff_head sk_error_queue;

struct proto *sk_prot_creator;

rwlock_t sk_callback_lock;

int sk_err,

sk_err_soft;

unsigned short sk_ack_backlog;

unsigned short sk_max_ack_backlog;

__u32 sk_priority;

struct ucred sk_peercred;

long sk_rcvtimeo;

long sk_sndtimeo;

struct sk_filter *sk_filter;

void *sk_protinfo;

struct timer_list sk_timer;

ktime_t sk_stamp;

struct socket *sk_socket;

void *sk_user_data;

struct page *sk_sndmsg_page;

struct sk_buff *sk_send_head;

__u32 sk_sndmsg_off;

int sk_write_pending;

void *sk_security;

void (*sk_state_change)(struct sock *sk);

void (*sk_data_ready)(struct sock *sk, int bytes);

void (*sk_write_space)(struct sock *sk);

void (*sk_error_report)(struct sock *sk);

int (*sk_backlog_rcv)(struct sock *sk,

struct sk_buff *skb);

void (*sk_destruct)(struct sock *sk);

};

sk_rcvbuf:接收缓冲区的长度限制

sk_sndbuf:发送缓冲区的长度限制

sk_rmem_alloc:已经申请的接收缓冲区长度

sk_wmem_alloc:已经申请的发送缓冲区长度

sk_receive_queue:等待接收的数据包队列头

sk_write_queue:等待发送的数据包队列头

sk_forward_alloc:为这个sock结构提前申请的数据区大小

sk_omem_alloc:当前申请的sock选项的空间大小

sk_dst_cache:dst_entry结构的链表,作为路由地址链表

sk_filter:这个结构比较有趣,关联到内核中BPF的实现,BPF规则将通过这一结构进行

下发

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值