zoukankan      html  css  js  c++  java
  • 数据包接收系列 — IP协议处理流程(二)

    本文主要内容:在接收数据包时,IP协议的处理流程。

    内核版本:2.6.37

    Author:zhangskd @ csdn blog 

    我们接着来看数据包如何发往本地的四层协议。

    ip_local_deliver

    在ip_local_deliver()中,如果发现数据报有被分片,则进行组装。

    然后调用NF_INET_LOCAL_IN处的钩子函数,如果数据包被钩子函数放行,

    则调用ip_local_deliver_finish()继续处理。

    /* Deliver IP Packets to the higher protocol layers. */
    
    int ip_local_deliver(struct sk_buff *skb)
    {
        /* 
         * Reassemble IP fragments. 
         * 如果IP数据报有被分片,则在这里进行组装还原。
         */
        if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
            if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
                return 0;
        }
    
        /* 调用netfilter的NF_INET_LOCAL_IN的钩子函数,如果此数据包被钩子函数放行,则调用
         * ip_local_deliver_finish()继续处理。
         */
        return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
            ip_local_deliver_finish);
    }

    ip_local_deliver_finish

    ip_local_deliver_finish()主要做了:

    处理RAW IP,如果有配置安全策略,则进行IPsec安全检查。

    根据IP报头的protocol字段,找到对应的L4协议(net_protocol),调用该协议的接收函数net_protocol->handler()。

    对于TCP协议,net_protocol实例为tcp_protocol,协议处理函数为tcp_v4_rcv()。

    接下来就进入四层协议的处理流程了,TCP协议的入口函数为tcp_v4_rcv()。

    static int ip_local_deliver_finish(struct sk_buff *skb)
    {
        struct net *net = dev_net(skb->dev);
    
        /* 把skb->data指向L4协议头,更新skb->len */
        __skb_pull(skb, ip_hdrlen(skb));
    
        /* 赋值skb->transport_header */
        skb_reset_transport_header(skb);
    
        rcu_read_lock();
        {
            int protocol = ip_hdr(skb)->protocol; /* L4协议号 */
            int hash, raw;
            const struct net_protocol *ipprot;
    
    resubmit:
            /* 处理RAW IP */
            raw = raw_local_deliver(skb, protocol);
    
            hash = protocol & (MAX_INET_PROTOS - 1); /* 作为数组索引 */
    
            /* 从inet_protos数组中取出对应的net_protocol元素,TCP的为tcp_protocol */
            ipprot = rcu_dereference(inet_protos[hash]);
    
            if (ipprot != NULL) {
                int ret;
    
                if (! net_eq(net, &init_net) && ! ipprot->netns_ok) {
                    if (net_ratelimit())
                        printk("%s: proto %d isn't netns-ready
    ", __func__, protocol);
                        kfree_skb(skb);
                        goto out;
                }
    
                /* 如果需要检查IPsec安全策略 */
                if (! ipprot->no_policy) {
                    if (! xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                        kfree_skb(skb);
                        goto out;
                    }
                    nf_reset(skb);
                }
    
                /* 调用L4协议的处理函数,对于TCP,调用tcp_protocol->handler,为tcp_v4_rcv() */
                ret = ipprot->handler(skb);
                if (ret < 0) {
                    protocol = - ret;
                    goto resubmit;
                }
                IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
    
            } else {
                if (! raw) {
                    if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                        IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);
                        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
                    } else
                        IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
    
                    kfree_skb(skb);
                }
            } 
    
    out:
        rcu_read_unlock();
        return 0;
    }
    

    L4协议处理函数

    数据包从L2传递到L3时,通过packet_type结构来找到L3协议的处理函数。

    同理,数据包从L3传递到L4时,通过net_protocol结构来找到L4协议的处理函数。

    /* This is used to register protocols. */
    struct net_protocol {
        int (*handler) (struct sk_buff *skb); /* L4协议的接收函数 */
        void (*error_handler) (struct sk_buff *skb, u32 info); /* ICMP用的 */
        ...
        unsigned int no_policy:1, /* 不需要检查IPsec策略 */
                     netns_ok:1;
    };
    
    #define MAX_INET_PROTOS 256 /* MUST be a power of 2 */

    把协议号作为索引,可以从数组找到对应协议的net_protocol元素。

    const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
    
    enum {
        ...
        IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
        ...
        IPPROTO_TCP = 6, /* Transmission Control Protocol */
        ...
        IPPROTO_UDP = 17, /* User Datagram Protocol */
        ...
        IPPROTO_RAW = 255, /* Raw IP packets */
        IPPROTO_MAX
    }; 
    
    /* TCP协议的net_protocol */
    static const struct net_protocol tcp_protocol = {
        .handler = tcp_v4_rcv,
        .err_handler = tcp_v4_err,
        ...
        .no_policy = 1,
        .netns_ok = 1,
    };
    
    /* UDP协议的net_protocol */
    static const struct net_protocol udp_protocol = {
        .handler = udp_rcv,
        .err_handler = upd_err,
        ...
        .no_policy = 1,
        .netns_ok = 1,
    };
    
    /* ICMP协议的net_protocl */
    static const struct net_protocol icmp_protocol = {
        .handler = icmp_rcv,
        .no_policy = 1,
        .netns_ok = 1,
    };
    

    初始化

    static int __init inet_init(void)
    {
        ...
        /* Add all the base protocols */
        if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
            printk(KERN_CRIT "inet_init: Cannot add ICMP protocol
    ");
    
        if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
            printk(KERN_CRIT "inet_init: Cannot add UDP protocol
    ");
    
        if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
            printk(KERN_CRIT "inet_init: Cannot add TCP protocol
    ");
        ...
    }
    

    RAW IP

    struct raw_hashinfo {
        rwlock_t lock;
        struct hlist_head ht[RAW_HTABLE_SIZE];
    };
    
    static struct raw_hashinfo raw_v4_hashinfo = {
        .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock);
    };
    #define RAW_HTABLE_SIZE MAX_INET_PROTOS
    

    RAW IP报的处理函数为raw_v4_input()。

    int raw_local_deliver(struct sk_buff *skb, int protocol)
    {
        int hash;
        struct sock *raw_sk;
    
        hash = protocol & (RAW_HTABLE_SIZE - 1); /* L4协议号作为key */
        raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); /* 取出哈希桶的第一个sock */
    
        /* If there maybe a raw socket we must check - if not we don't care less.
         * raw_v4_input(): IP input processing comes here for RAW socket delivery.
         * 如果该协议上有连接,那么调用raw_v4_input()来处理。
         */
        if (raw_sk && ! raw_v4_input(skb, ip_hdr(skb), hash))
            raw_sk = NULL;
    
        return raw_sk != NULL;
    }
    
  • 相关阅读:
    【 DCOS 】织云 CMDB 管理引擎技术详解
    腾讯云无服务器云函数架构精解
    深度学习在 CTR 中应用
    一文教你迅速解决分布式事务 XA 一致性问题
    小程序开发工具全新上线
    秦俊:开放 DevOps 敏捷开发套件,助力开发者驰骋云端
    张兴华:云端架构助力企业快速成长
    王磊:AI 时代物流行业的 OCR 应用
    陈杰:无服务器架构,让云端开发更纯粹
    腾讯织云:DevOps 流水线应用平台践行之路
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333287.html
Copyright © 2011-2022 走看看