第12章 网络 (5)

目录

12.8 网络层

12.8.1 IPv4

12.8.2 接受分组

12.8.3 交付到本地传输层


本专栏文章将有70篇左右,欢迎+关注,查看后续文章。

12.8 网络层

12.8.1 IPv4

IPv4首部:

version:         值为4。

IHL:            IP header length。至少20字节。

Codepoint或Type of Service:ToS

Length:          首部 + 数据的总长度。

Fragment ID: 分片标识,一个分组的所有分片都相同,结合Fragment Offset来合并分片。

标志:            不同位代表不同特性。如:

        DF位:           不可分片。

        MF位:           有更多分片。

Fragment Offset:     该分片在分组的偏移。

TTL:                        最大跳数。

Protocol:                 传输层协议,如TCP or UDP。

IP头结构体

struct    iphdr    {

        __u8             version:4,

        __u8             ihl:4;

        __u8             tos;

        __be16         tot_len;

        __be16         id;

        __be16         frag_off;

        __u8             ttl;

        __u8             protocol;

        __sum16      check;

        __be32         saddr;

        __be32         daddr;

};

注册IP协议

inet_init()

        -> dev_add_pack( &ip_packet_type );

                将IP协议注册到ptype_base[ ] 数组中。

struct   packet_type     ip_packet_type    =    {

        .type        =       cpu_to_be16( ETH_P_IP ),

        .func        =       ip_rcv,         // 网络层入口

}

问:ip_rcv () 如何被调用?

答:1. 收包时,__netif_receive_skb_core () 中调用 deliver_ptype_list_skb ()

        2. deliver_ptype_list_skb:

                1. 遍历所有注册的网络层协议(packet_type)。

                2. 比较 packet_type->type与 skb->protocol 是否相等。

                3. 若相等,调用 packet_type->func(),即 ip_rcv。

12.8.2 接受分组

ip_rcv 收到分组后,流程:

struct    rtable   {         // IPv4 路由决策信息。

        struct    dst_entry    dst;

        unsigned int            rt_flags;

                //值有RTCF_LOCAL、RTCF_MULTICAST

        __u16                   rt_type;

                //表示路由类型。

                举例:

                        RTN_UNICAST:单播路由。

                        RTN_LOCAL:本地路由(直接交付)。

        int                         rt_genid;

                // 路由表变化时,rt_genid会递增。

        u32                      rt_pmtu;

                // pmtu值,即路径MTU。

        __be32                 rt_gateway;

                // 下一跳

        ........

};

IPv4

struct    rtable     *rt;

本地接收单播:

        rth->dst.input    =    ip_local_deliver;

转发单播:

        rth->dst.input    =    ip_forward;

接收组播

        rth->dst.input    =    ip_mr_input;

IPv6

struct    rt6_info    *rt;

本地接收:

        rt->dst.input    =    ip6_input;

转发:

        rt->dst.input    =    ip6_forward;

ip_rcv () 内容:

        1. 检测报文首部校验,其他字段检查。

        2. 调用 netfilter的 PRE_ROUTING钩子函数。

int    ip_rcv(struct   sk_buff    *skb,    struct net_device    *dev,    struct packet_type    *pt,    struct net_device    *orig_dev)

{

        // 1. 各种错误检查

        // 2. 执行netfilter的pre_route钩子函数

        NF_HOOK(NFPROTO_IPV4,    NF_INET_PRE_ROUTING,    skb,    dev,    NULL,

   ip_rcv_finish);

}

ip_rcv_finish:

        1. 根据 sk_buff 计算得到struct dst_entry *dst

        2. 执行dst->input() ,即:

                ip_local_deliver:本地接收。传给传输层协议。

                        或

                ip_forward:转发。

12.8.3 交付到本地传输层

对应函数:

        ip_local_deliver

int    ip_local_deliver(struct  sk_buff    *skb)

{

        if ( ip_is_fragment( ip_hdr(skb) ) ) {

                if (ip_defrag(skb,    IP_DEFRAG_LOCAL_DELIVER ))

                        return 0;         // 分片合并失败,如分片还没全部收到。

        }

        return    NF_HOOK(NFPROTO_IPV4,    NF_INET_LOCAL_IN,    skb,    skb->dev,    NULL,

   ip_local_deliver_finish);

}

1. 分片重组合并

ip_defrag

分片缓存:

        同一分组的各个分片缓存在一个独立的等待队列,直到所有分片到达。

ip_find ():

        对分片ID,源目IP,协议类型等进行hash,以检查是否为该分组创建了等待队列。

        1. 若没有,则创建新队列。

        2. 否则,通过 ip_frag_queue () 将分组置于队列中。

ip_frag_reasm ():

        实现合并分组动作。

若一个分组的分片没有全部到达,ip_defrag () 返回NULL,终止处理,等待分片到达。

2. 交付到传输层

ip_local_deliver () 分片合并后,调用:

        NF_HOOK(NFPROTO_IPV4,    NF_INET_LOCAL_IN,    skb, skb->dev,    NULL,    ip_local_deliver_finish);

每个传输层协议,定义一个 struct    net_protocol 实例

struct    net_protocol {

        void         (*early_demux)(struct sk_buff    *skb);

        int            (*handler)(struct sk_buff    *skb);

        void         (*err_handler)(struct sk_buff    *skb,    u32    info);

        unsigned int         no_policy:1,

        unsigned int         icmp_strict_tag_validation:1;

};

struct    net_protocol    tcp_protocol    =    {

        .handler    =    tcp_v4_rcv,

};

struct    net_protocol    udp_protocol    =    {

        .handler    =    udp_rcv,

};

struct    net_protocol    icmp_protocol    =    {

        .handler    =    icmp_rcv,

        .err_handler    =    icmp_err,         // 处理收到的ICMP差错消息。

}

注册传输层协议方法:

inet_add_protocol( &tcp_protocol,    IPPROTO_TCP);

        //添加到struct net_protocol *inet_protos[MAX_INET_PROTOS];

        //数组索引由hash得到。

所以最终由 udp_rcv () ,tcp_v4_rcv () 等函数接收分组。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值