菜鳥的三年成長史

NVGRE loses DHCP OFFER

從前我一直認為,寫application的人不需要去懂kernel到底幹了啥,只要知道他能夠使命必達,而programmer就專心的把上層軟體架構想清楚即可。但事實證明我錯了,而我這次解的這問題也著實打了我好大一巴掌,所以之後我會更認真去看kernel的細節,至少網路相關的要很熟。

以下是張示意圖,左邊是wireless controller,右邊是AP;而controller會與AP建立一條tunnel,以便將所有連上AP的STA traffic都導回controller。

問題

Controller和AP用192.168.1.x的網段建tunnel,但tunnel卻是被bridge在br3上,而controller的br3上面有個DHCP server,負責發IP給br3上面的STA。

封包流向理當是:

DHCP DISCOVER→wlan-1-1.3→gretap1→br0→eth0→→→→→vlan2.1→br0→gretap1→br3
DHCP OFFER→br3→gretap1→br0→vlan2.1→→→→→eth0→br0→gretap1→wlan-1-1.3

但DHCP OFFER卻送到一半就不見了,接著我用tcpdump去追封包掉在哪,發現br0上就已經抓不到了。但是在iptables和ebtables的hook點上確實有看到東西流過去,百思不得其解下我直接插printk在kernel的br_dev_xmitvlan_hard_start_xmitdev_queue_xmit (熟kernel的人應該知道我這根本把printk插在整個packet flow上...),然後我發現,同一個skb的pointer很順利的從br3一路通到vlan2.1出去。

(/‵′)/~ ╧╧

那tcpdump在搞啥鬼,所以我做了一個很痛的決定,我在原本插printk的點全部改成printk_hex (我自己寫的,用來把封包從頭到尾dump出來),最後總算發現是掉在gretap1內,也就是ipgre_tunnel_xmit

深入去了解後發現到,skb指向IP header的指針根本指錯了,導致IP header說不需要fragmentation但GRE看到的卻是IP header內的total length大到連他媽都不認識,遠超過IP的MTU。如示意圖:

但是我在ubuntu上面玩同樣的topology卻一點事也沒有。最後找到原因,一般的application如果要丟IP包或L2包必須要用PF_PACKET(或AF_PACKET,一樣的東西,kernel會轉)去丟;而PF_PACKET又分成兩socket type,SOCK_PACKETSOCK_RAW。功能很像,但做的事不太一樣,這篇不去探討詳細差別,最主要是它們進kernel的點不一樣,一個是packet_sendmsg_spkt另一個是packet_sendmsg

關鍵在於packet_sendmsg_spkt會去調整skb的data指針指向正確的network header,如圖黃色區塊:

packet_sendmsg則是user space丟下來是甚麼就是甚麼,controller上的DHCP server用的是SOCK_RAW,但在user space卻有將ethernet header填起來,導致應該指向IP header的指針指的其實是Ethernet header,最後,修正controller上的DHCP server的行為就一切正常了。