Linux, 工作, 生活, 家人

Embedded, Linux, Programming

[Linux][Kernel][Driver] 老師有沒有說, 不要用 cb!

之前就苦口婆心的說, Network Driver 不要偷用 sk_buff 內的 cb .
就是有人不聽, 一定要用!


這二天在 Debug 某一家 Wireless 11n 的 Driver.
當 Wireless 在 LAN 端, 要連到 WAN 端時, 發現 NAT 會過不去,
而且會出現 “ip_forward(): Argh! Destination lost!\n”

在追 Code 之後發現, 該 Wireless 11n 的 Driver 會用到 sk_buff->cb 去做一些事情.
導致 sk_buff->cb 被蓋掉,
這部份在 net/ipv4/ip_opinion.c 內,
看到 function ip_foward_opinion

struct ip_options * opt = &(IPCB(skb)->opt);
unsigned char * optptr;
struct rtable *rt = (struct rtable*)skb->dst;
unsigned char *raw = skb->nh.raw;

if (opt->rr_needaddr) {
optptr = (unsigned char *)raw + opt->rr;
ip_rt_get_source(&optptr[optptr[2]-5], rt);
opt->is_changed = 1;
}
if (opt->srr_is_hit) {

而 IPCB 的功能是

#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))

如果這時候你不小心寫到 skb->cb 的前幾個 bytes, 這時取出來的 opt 就會是有問題的 opt.

我粗略的解決方案就是將該 Wireless 11n 的 Driver 會取用到 skb->cb 的部份再往後延展
8 個 Bytes , 而 skb->cb 共有 48 Bytes , 這樣就可以避開覆寫 cb 的慘劇!
不過因為 kernel 內用到 skb->cb 的地方還不少, 而且目前我沒有找到有文件是描述 cb 的使用狀況.
所以這個解決方只能說是暫時解決.

一勞永逸的方法大概就是以下幾個,
1. 修改 sk_buff 的結構, 多塞一個欄位
2. 修改 __dev_alloc_skb, 多塞一些 header room 進去.
目前來講所有的 11n driver 都會去動到這一塊

#if defined(CONFIG_PRE11N)
struct sk_buff *skb = alloc_skb(length + 64, gfp_mask);
if (likely(skb))
skb_reserve(skb, 64);
#else
struct sk_buff *skb = alloc_skb(length + 16, gfp_mask);
if (likely(skb))
skb_reserve(skb, 16);
#endif

*註: 新版的 Linux kernel (至少是 2.6.22 以後), 這個 16 己經變成一個 define 了,

#ifndef NET_SKB_PAD
#define NET_SKB_PAD 16
#endif

直接修改就可以了.

以個人觀點, 修改 __dev_alloc_skb 的方式相容性比較高,
如果跑去改 sk_buff, 難免會有一些 non open source 的 modules 會有問題.

[Tags] Linux, Kernel, Driver, Wireless, 11n, sk_buff [/Tags]

2 留言

  1. 匿名訪客

    請問一下:skb->magic的含義?

  2. 匿名訪客

    博主,你好!
    我是一名学生,想向你请教一下关于linux内核的知识。目前我想在IPV6报头的基础上修改报头,把原有的40字节固定首部改为36字节。去掉一些,修改一些字段。需要修改哪些部分?
    宗宁

發佈留言