IP隧道基礎研究

static char banner[] __initdata = KERN_INFO "IPv4 over IPv4 tunneling driver\n";
static struct xfrm_tunnel ipip_handler = {
    .handler        =       ipip_rcv, //看下面接收處理函數實現
    .err_handler    =       ipip_err,
    .priority       =       1,
};
static int __init ipip_init(void) // net/ipv4/ipip.c
{
    int err;
    printk(banner); //打印信息

    //註冊ipip接收處理函數
    if (xfrm4_tunnel_register(&ipip_handler)) {
        printk(KERN_INFO "ipip init: can't register tunnel\n");
        return -EAGAIN;
    }
    //分配一個網絡設備,設備名tunl0
    ipip_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "tunl0", ipip_tunnel_setup);
    if (!ipip_fb_tunnel_dev) {
        err = -ENOMEM;
        goto err1;
    }
    ipip_fb_tunnel_dev->init = ipip_fb_tunnel_init; //初始化函數

    //註冊這個網絡設備,會調用上面的初始化函數
    if ((err = register_netdev(ipip_fb_tunnel_dev)))
        goto err2;
out:
    return err;
err2:
    free_netdev(ipip_fb_tunnel_dev);
err1:
    xfrm4_tunnel_deregister(&ipip_handler);
    goto out;
}
//註冊處理函數
static struct xfrm_tunnel *tunnel4_handlers;
int xfrm4_tunnel_register(struct xfrm_tunnel *handler)
{
    struct xfrm_tunnel **pprev;
    int ret = -EEXIST;
    int priority = handler->priority; //權限

    mutex_lock(&tunnel4_mutex);
    for (pprev = &tunnel4_handlers; *pprev; pprev = &(*pprev)->next) {
        if ((*pprev)->priority > priority) //找到位置,從小到大排列
            break;

        if ((*pprev)->priority == priority) //重複,出錯
            goto err;
    }
    handler->next = *pprev;
    *pprev = handler;
    ret = 0;
err:
    mutex_unlock(&tunnel4_mutex);
    return ret;
}

ipip協議
static struct net_protocol tunnel4_protocol = { //IPIP協議處理
    .handler        =       tunnel4_rcv,
    .err_handler    =       tunnel4_err,
    .no_policy      =       1,
};
    ipip協議註冊
static int __init tunnel4_init(void)
{
    //向核心註冊ipip協議
    if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) {
        printk(KERN_ERR "tunnel4 init: can't add protocol\n");
        return -EAGAIN;
    }
    return 0;
}
static int tunnel4_rcv(struct sk_buff *skb)
{
    struct xfrm_tunnel *handler;

    if (!pskb_may_pull(skb, sizeof(struct iphdr)))
        goto drop;

    //調用連表中的全部處理函數,已經安大小排過序了
    for (handler = tunnel4_handlers; handler; handler = handler->next)
        if (!handler->handler(skb)) //處理函數返回0,正確結束
            return 0;
    //有一個不對就發送icmp目的和端口不可達包
    icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
drop:
    kfree_skb(skb);
    return 0;
}
如今咱們看出只要是調用了xfrm4_tunnel_register函數向ipip協議註冊了處理函數那麼就會被ipip協議接收函數調用,權限字段值越小越先被調用.
在ip_local_deliver_finish函數中(能夠參考linux協議佔函數流程一文)會根據ip協議中的協議字段調用相應的協議處理函數。

......
int protocol = skb->nh.iph->protocol;
......
if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) {
    ......
    ret = ipprot->handler(skb); //調用ipip協議的處理函數tunnel4_rcv
    ......
}
[接收處理函數實現]
每個IP數據包均交由ip_rcv函數處理,在進行一些必要的判斷後,ip_rcv對於發送給本機的數據包將交給上層處理程序。
對於IPIP包來講,其處理函數是ipip_rcv(就如TCP包的處理函數是tcp_rcv同樣,IP層不加區分)。
也就是說,當一個目的地址爲本機的封包到達後,ip_rcv函數進行一些基本檢查併除去IP頭,而後交由ipip_rcv解封。
    ipip_rcv所作的工做就是去掉封包頭,還原數據包,而後把還原後的數據包放入相應的接收隊列netif_rx().
static int ipip_rcv(struct sk_buff *skb)
{
    struct iphdr *iph;
    struct ip_tunnel *tunnel;
    iph = skb->nh.iph;

    read_lock(&ipip_lock);
    //在hash表中查詢
    if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
        if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { //檢測IPSEC包策略
            read_unlock(&ipip_lock);
            kfree_skb(skb);
            return 0;
        }

        secpath_reset(skb); //釋放sec_path

        skb->mac.raw = skb->nh.raw;
        skb->nh.raw = skb->data; //指向這個封裝的ip頭
        skb->protocol = htons(ETH_P_IP);//協議變爲IP
        skb->pkt_type = PACKET_HOST;

        tunnel->stat.rx_packets++;
        tunnel->stat.rx_bytes += skb->len;
        skb->dev = tunnel->dev;//指向這個虛擬設備
        dst_release(skb->dst); //釋放路由緩存,須要重新查找
        skb->dst = NULL;
        nf_reset(skb); //釋放ip_conntrack結構
        ipip_ecn_decapsulate(iph, skb); //ECN解封,IPSEC相關
        netif_rx(skb);//從新遞交
        read_unlock(&ipip_lock);
        return 0;
    }
    read_unlock(&ipip_lock);
    return -1; //查詢不到出錯
}

static struct ip_tunnel *tunnels_r_l[HASH_SIZE]; //(remote,local)
static struct ip_tunnel *tunnels_r[HASH_SIZE];   //(remote,*)
static struct ip_tunnel *tunnels_l[HASH_SIZE];   //(*,local)
static struct ip_tunnel *tunnels_wc[1];           //(*,*)
static struct ip_tunnel **tunnels[4] = { tunnels_wc, tunnels_l, tunnels_r, tunnels_r_l };
static struct ip_tunnel * ipip_tunnel_lookup(u32 remote, u32 local)
{
    //計算出hash值
    unsigned h0 = HASH(remote);
    unsigned h1 = HASH(local);
    struct ip_tunnel *t;
    //在源和目的地址都在的hash表中尋找
    for (t = tunnels_r_l[h0^h1]; t; t = t->next) {
        if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
            return t;
    }
    //在只有目的地址hash表中尋找
    for (t = tunnels_r[h0]; t; t = t->next) {
        if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
            return t;
    }
    //在只有源地址hash表中尋找
    for (t = tunnels_l[h1]; t; t = t->next) {
        if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
            return t;
    }
    if ((t = tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP))
        return t;
    return NULL;
}
[\接收處理函數實現]
相關文章
相關標籤/搜索