tcp/ip协议栈知识

来源:互联网 发布:linux查询用户密码命令 编辑:程序博客网 时间:2024/05/22 12:46

http://www.eefocus.com/article/09-06/75257s.html: 

http://blog.tianya.cn/blogger/post_show.asp?BlogID=643342&PostID=13157890: 两个都是,  linux内核网络协议栈的介绍分析,后面这一个写得挺好的。

struct dst_entry:最终生成的IP数据报的路由称为目的入口(dst_entry),目的入口反映了相邻的外部主机在主机内部的一种“映象。

http://www.makefile.net/2013/03/detail-of-dst_entry-struct.html:介绍struct dst_entry 和struct rtable结构体的成员以及一些意义

通过在一张路由表(struct fib_table)中,根据查询路由的目的IP地址(key)在其路由哈希表(struct fn_hash)中找到一个路由域(struct fn_zone),并在路由域中匹配到一个key相等的路由节点(struct fib_node),取其路由别名(struct fib_alias)和路由信息(struct fib_info),生成一个路由查询结果(struct fib_result)。

查询结果生成struct dst_entry目的入口,再由目的入口生成struct rtable

路由缓存表rt_hash_table是一个很大的数组(依据系统的内存大小而定),每一项都是一个struct rtable

路由可以分为两部分:路由缓存(rt_hash_table)和路由表()。路由表又分为两项RT_TABLE_LOCAL和RT_TABLE_MAIN

   RT_TABLE_LOCAL存储目的地址是本机的路由表项,这些目的地址就是为各个网卡配置的IP地址;

        RT_TABLE_MAIN存储到其它主机的路由表项;

ip_route_input_slow() -> fib_lookup()是在路由表里查询,ip_route_input里包含一个判断,看是到缓存里查找还是到路由表里查询。如果支持转发,则无论在路由表中找到与否,都会生成这次查询的一个缓存,包括源IP、目的IP、接收的网卡,插入路由缓存中

neighbour_table{ }是一个包含和本机所连接的所有邻元素的信息的数据结构,该结构中有个元素是neighbour结构的数组,数组的每一个元素都是一个对应于邻机的neighbour结构。在neighbour结构中,包含有与该邻居相连的网络接口设备net_device的指针,网络接口的硬件地址,邻居的硬件地址,包含有neigh_ops{}指针,这些函数指针是直接用来连接传输数据的,包含有queue_xmit(struct * sk_buff)函数入口地址,这个函数可能会调用硬件驱动程序的发送函数。

接收不是发给自己的数据包的处理如下:

netif_receive_skb->ip_rcv(如果不是发给自己的,(skb->pkt_type == PACKET_OTHERHOST)-->ip_rcv_finish()在完成包接收后进行路由处理。

ip_rcv_finish包含两个重要的函数:

     1、 ip_route_input:determine the route of a packet. The skb->dst pointer of the socket buffer is set to an entry in the routing cache

      2、dst_input():the procedure of the IP protocol reaches the junction between packets addressed to the local computer (局域网中的电脑)and packets to be forwarded.是发给局域网中的主机还是转发给外网中的主机。如果是发给局域网内的主机则调用ip_local_deliver,如果发给外面的电脑则调用ip_forward() ,具体调用哪一个是根据放在skb->dst->input里面的。下面是这个函数的具体实现,可以看出,只是调用了skb->dst->input里面保存的函数

static inline int dst_input(struct sk_buff *skb)
{
   int err;
    for (;;) {
       err = skb->dst->input(skb);
        if (likely(err == 0))
            return err;
        if (unlikely(err != NET_XMIT_BYPASS))
           return err;
    }
}

 

下面再看看ip_route_input的实现,到路由表里查询:

ip_route_input()==>ip_route_input_slow()
在ip_route_input_slow()中:
    if ((err = fib_lookup(&fl, &res)) != 0) {
        if (!IN_DEV_FORWARD(in_dev))
            goto e_hostunreach;
        goto no_route;
    }
调用fib_lookup()函数用来在fib中查询路由信息,将路由查询结果保存在fib_result结构的res中。
接下来:
    if (res.type == RTN_LOCAL) {
        int result;
        result = fib_validate_source(saddr, daddr, tos,
                         loopback_dev.ifindex,
                         dev, &spec_dst, &itag);
        if (result dst->input=ip_local_deliver*/
        goto local_input;
    }
如果查询的结果显示,路由类型RTN_LOCAL的话,跳转到local_input段,设置skb->dst->input = ip_local_deliver
接下来,路由类型是RTN_LOCAL的已经跳转到下面去了,剩下的就是非LCOAL的,也就是Forward的:
    if (!IN_DEV_FORWARD(in_dev))
        goto e_hostunreach;
    if (res.type != RTN_UNICAST)
        goto martian_destination;
/*设置skb->dst->input=ip_forward*/
    err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);
调用IN_DEV_FORWARD宏来判断网络设备是否处于FORWARD状态;调用ip_mkroute_input()函数来设置skb->dst->input=ip_forward

http://hi.baidu.com/zengzhaonong/item/e01607b9aafe16eb4ec7fd71:介绍ip_rcv_finish的函数

http://hi.baidu.com/zengzhaonong/item/dff0fc9971c880da7a7f0172:介绍ip_forward的函数

下面看ip_forward的处理


下面一部分转载于http://blog.csdn.net/yming0221/article/details/7979838

struct sock是传输层的套接字,里面包含了一个传输层协议相关的操作集合,如tcp、udp、icmp协议,各协议对应一个不同的协议操作集合,这些操作函数是传输层和网络层交互的接口如下。

struct proto udp_prot:UDP协议操作集

struct proto tcp_prot:tcp协议操作集

struct proto是一个协议操作函数集合,包含了很多的操作函数

BSD层的socket结构体如下:

  1. struct socket {  
  2.     socket_state        state;  
  3.   
  4.     kmemcheck_bitfield_begin(type);  
  5.     short           type;  
  6.     kmemcheck_bitfield_end(type);  
  7.   
  8.     unsigned long       flags;  
  9.   
  10.     struct socket_wq __rcu  *wq;  
  11.   
  12.     struct file     *file;  
  13.     struct sock     *sk;  
  14.     const struct proto_ops  *ops;  
  15. };

 struct proto_ops是inet层的协议操作集。到传输层的入口

UDP协议在INET层操作集是const struct proto_ops inet_dgram_ops 

TCP协议z在INET层操作集const struct proto_ops inet_stream_ops


上面结构体里的type包含几个值如下:

  1. enum sock_type {  
  2.     SOCK_DGRAM  = 1,  
  3.     SOCK_STREAM = 2,  
  4.     SOCK_RAW    = 3,  
  5.     SOCK_RDM    = 4,  
  6.     SOCK_SEQPACKET  = 5,  
  7.     SOCK_DCCP   = 6,  
  8.     SOCK_PACKET = 10,  
  9. };


下面的结构体就是在系统初始化时用来管理协议族初始化的结构体:

[cpp] view plaincopy
  1. struct net_proto_family {  
  2.     int     family;  
  3.     int     (*create)(struct net *net, struct socket *sock,  
  4.                   int protocol, int kern);  
  5.     struct module   *owner;  
  6. };  
第一个属性就是协议族的宏定义,如PF_INET;

第二个属性就是协议族对应的初始化函数指针;

INET协议族对应该结构的定义如下:

[cpp] view plaincopy
  1. static const struct net_proto_family inet_family_ops = {  
  2.     .family = PF_INET,  //协议族
  3.     .create = inet_create, //用来创建套接字的函数指针 
  4.     .owner  = THIS_MODULE,  
  5. }; 

创建套接字时,根据不同的协议族来调用不同的创建函数,内核里保存了一个struct net_proto_family结构的数组net_families包含了不同的协议族创建函数.这个数组是用下面的函数一个个初始化的。每个协议都要调用这个函数来注册到系统中。

int sock_register(const struct net_proto_family *ops)   {       int err;       spin_lock(&net_family_lock);   ///代码非常简单,就是根据类型,然后放到相应的位置.       if (net_families[ops->family])           err = -EEXIST;       else {           net_families[ops->family] = ops;           err = 0;       }    } 


原创粉丝点击