1

からデータを抽出しようとしてstruct sk_buffいますが、期待する出力が得られません。問題のフレームは 34 バイトです。8 バイト (実験的プロトコル) ヘッダーをラップする 14 バイトのイーサネット ヘッダー:

struct monitoring_hdr {
    u8      version;
    u8      type;
    u8      reserved;
    u8      haddr_len;

    u32     clock;
} __packed;

このヘッダーの後に、2 つの可変長ハードウェア アドレスがあります (それらの長さはhaddr_len上記のフィールドによって決まります)。この例では、両方とも 6 バイト長です。

次のコードは、ヘッダー (構造体) を正しく抽出しますが、後続の 2 つの MAC アドレスは抽出しません。

送信側:

    ...
    skb = alloc_skb(mtu, GFP_ATOMIC);
    if (unlikely(!skb))
            return;
    skb_reserve(skb, ll_hlen);
    skb_reset_network_header(skb);
    nwp = (struct monitoring_hdr *)skb_put(skb, hdr_len);
    /* ... Set up fields in struct monitoring_hdr ... */
    memcpy(skb_put(skb, dev->addr_len), src, dev->addr_len);
    memcpy(skb_put(skb, dev->addr_len), dst, dev->addr_len);
    ...

レシーバー側:

   ...
   skb_reset_network_header(skb);
   nwp = (struct monitoring_hdr *)skb_network_header(skb);

   src = skb_pull(skb, nwp->haddr_len);
   dst = skb_pull(skb, nwp->haddr_len);
   ...

期待される出力:

tcpdump を使用して問題のパケットをネットワーク上でキャプチャしたところ、次のことがわかりました (実際には、送信者の NIC によって 60 バイトにパディングされましたが、省略しました)。

0000 | 00 90 f5 c6 44 5b 00 0e  c6 89 04 2f c0 df 01 03
0010 | 00 06 d0 ba 8c 88 00 0e  c6 89 04 2f 00 90 f5 c6
0020 | 44 5b

最初の 14 バイトはイーサネット ヘッダーです。次の 8 バイト ( で始まり で01終わる) は、正しく実行88される に入れられるバイトです。struct monitoring_hdr次に、次の MAC アドレスが見つかることを期待しています。

src = 00 0e c6 89 04 2f
dst = 00 90 f5 c6 44 5b

実際の出力:

ただし、受信したデータは 2 バイト左にシフトされます。

src = 8c 88 00 0e c6 89
dst = 04 2f 00 90 f5 c6

上記のコードに論理的な欠陥が見られる人はいますか? または、これを行うより良い方法はありますか?受信側でもskb_pull代わりに試してみましたが、カーネルパニックが発生しました。skb_network_header

助けてくれてありがとう。

解決策:

のデータの最初のバイトへのポインタが、本来あるべきようsk_buffに によって指されていませんでしsrcた。私は最終的に以下を使用しました:

   ...
   skb_reset_network_header(skb);
   nwp = (struct monitoring_hdr *)skb_network_header(skb);
   skb_pull(skb, offsetof(struct monitoring_hdr, haddrs_begin));

   src = skb->data;
   dst = skb_pull(skb, nwp->haddr_len);
   ...
4

1 に答える 1

3

skbuff.h ヘッダーを見ると、使用している関数は次のようになります。

static inline void skb_reset_network_header(struct sk_buff *skb)
{
        skb->network_header = skb->data - skb->head;
}

static inline unsigned char *skb_network_header(const struct sk_buff *skb)
{
        return skb->head + skb->network_header;
}

extern unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);
static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
        skb->len -= len;
        BUG_ON(skb->len < skb->data_len);
        return skb->data += len;
}

そのため、最初に印刷してskb->dataskb->head期待するパケットの部分を参照していることを確認します。ここではカスタム プロトコルを使用しているため、ヘッダー処理コードにバグがあり、skb->data正しく設定されていない可能性があります。

また、 と の定義を見ると、sky_network_headerおそらくskb_pull間違った使い方をしていると思います。最初の 6 バイトの addr は、 の戻り値であることが示されている場所にあるべきではありskb_network_header()ませんか? その関数は、ヘッダー ブロックの長さをバッファーの先頭に追加するように見えます。これにより、最初のデータ値へのポインターが得られるはずです。

skb_pull()同様に、渡したフィールドの長さを追加し、次のバイトへのポインターを返すように見えます。したがって、おそらく次のようなものが必要になります。

src = skb_network_header(skb);
dst = skb_pull(skb, nwp->haddr_len);

それが役立つことを願っています。これが正確な答えでなくて申し訳ありません。

于 2012-12-02T20:02:33.543 に答える