天天看點

大師的錯誤?UNP1中raw socket不能接收TCP和UDP的錯誤

本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結,嚴禁用于任何商業用途。

作者:[email protected]

部落格:linuxfocus.blog.chinaunix.net     

在UNP1中的28.4 Raw Socket Input中,大師是這樣說的。

中文版是這樣說的。

1. 接收到UDP分組和TCP分組絕不傳遞到任何原始套接口。如果一個程序想要讀取含有UDP分組或TCP分組的IP資料報,它就必須在資料鍊路層讀取這些分組。

首先,要嚴重鄙視一下中文版的翻譯!什麼叫UDP分組和TCP分組?!

反正我在看到中文版這個部分的時候,就很疑惑?分組,是分片的筆誤還是多點傳播?然後對照了原文,唉,為啥不翻譯成封包呢。

看來我以後全看英文版的決定還是無比正确的。

今天跟同僚談到這個問題,跟他說明linux選擇socket機制時,對于raw socket,隻要該包符合了raw socket的過濾條件,該raw socket就可以獲得一個該包的拷貝。他想讓我找出理論依據,我就找出UNP1來作說明。這才發現UNP1中雖然說了raw socket可以獲得資料包拷貝的事情,但是也說了前提條件。其中UDP和TCP不會交給raw socket是首要條件。這讓我很困惑啊。

如何解決?代碼說明一切。讓我們直接看linux kernel的代碼。

我的kernel代碼是2.6.36.2

static struct sock *__raw_v4_lookup(struct net *net, struct sock *sk,

        unsigned short num, __be32 raddr, __be32 laddr, int dif)

{

    struct hlist_node *node;

    sk_for_each_from(sk, node) {

        struct inet_sock *inet = inet_sk(sk);

        if (net_eq(sock_net(sk), net) && inet->inet_num == num &&

            !(inet->inet_daddr && inet->inet_daddr != raddr) &&

            !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&

            !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))

            goto found; /* gotcha */

    }

    sk = NULL;

found:

    return sk;

}

static int raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash)

    struct sock *sk;

    struct hlist_head *head;

    int delivered = 0;

    struct net *net;

    read_lock(&raw_v4_hashinfo.lock);

    head = &raw_v4_hashinfo.ht[hash];

    if (hlist_empty(head))

        goto out;

    net = dev_net(skb->dev);

    sk = __raw_v4_lookup(net, __sk_head(head), iph->protocol,

                 iph->saddr, iph->daddr,

                 skb->dev->ifindex);

    while (sk) {

        delivered = 1;

        if (iph->protocol != IPPROTO_ICMP || !icmp_filter(sk, skb)) {

            struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);

            /* Not releasing hash */

            if (clone)

                raw_rcv(sk, clone);

        }

        sk = __raw_v4_lookup(net, sk_next(sk), iph->protocol,

                     iph->saddr, iph->daddr,

                     skb->dev->ifindex);

out:

    read_unlock(&raw_v4_hashinfo.lock);

    return delivered;

在__raw_v4_lookup中,隻是去比較位址,端口等過濾條件,則這個raw socket就是比對的。然後在raw_v4_input中,隻要不是ICMP或者不是需要過濾的ICMP type,這個raw socket就可以獲得一個資料包的拷貝。

kernel的代碼無疑已經說明了raw socket完全可以接受TCP或者UDP的資料包。我又檢查了linux2.4的處理,同樣沒有這個限制。

也許大師寫的這個條件不是針對linux的吧——因為對其他unix系統沒有了解,是以不敢枉然說這是大師的錯誤。

不過從這件事可以看出,盡信書不如無書。對于linux程式員,既然kernel是開源的,還是代碼說明了一切!

繼續閱讀