数据包接收系列 — 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\n", __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\n");

    if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");

    if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
        printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
    ...
}

 

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;
}

 

 

posted on 2014-04-02 16:44  张大大123  阅读(499)  评论(0编辑  收藏  举报

导航