一.緣起
本人工作與網關產品有關,經常會接觸到pppoe協議,但對ppp和pppoe是如何實現的不甚瞭解。在網上查找相關的文章大多是描述ppp/pppoe協議的具體內容和數據流大的流程,而未過多的涉及內核中pppoe驅動和ppp驅動代碼實現的細節,正好這段時間在看毛德操和胡希明老師的《Linux內核源代碼情景分析》,看了內核中的終端驅動,遂決定仔細學習一下PPP/PPPOE驅動這一塊的內容並將這段時間的收穫記錄下來,以便以後翻閱同時也與大家分享。
二.關於本文
本文不會涉及ppp協議和pppoe協議本身,即不會討論如LCP,PAP,CHAP,IPCP, PADI等這些報文的格式等內容,如果您僅僅想了解的是ppp和pppoe協議本身,那這篇文章並不適合你。
如果你想了解ppp和pppoe驅動,想了解ppp程序是怎麼建立起ppp網絡接口的,以及ppp網絡接口如何將數據通過具體的網絡接口(比如eth0)發送的,具體的網絡接口(比如eth0)收到數據後是怎樣將數據給ppp網絡接口的那這篇文章正好適合你。
閱讀本文時建議將各個部分的流程圖先複製下來保存在一個文檔中,然後對照着流程圖來看代碼。
三.代碼鏈接
ppp代碼下載鏈接 http://mirrors.aliyun.com/ubuntu/pool/main/p/ppp/ppp_2.4.7.orig.tar.gz
Linux-2.4.0代碼下載鏈接https://mirrors.edge.kernel.org/pub/linux/kernel/v2.4/linux-2.4.0.tar.gz
四.代碼分析
1.內容預覽
pppd命令
ppp代碼創建ppp網絡接口
pppoe驅動建立channel
ppp驅動建立ppp網絡接口並與pppoe建立的channel關聯
ppp網絡接口和ppp程序收包過程
ppp程序和ppp網絡接口發包過程
2.pppd命令
pppd plugin /usr/lib/rp-pppoe.so nic-pon0.30 linkname internet nodefaultroute noipdefault noauth default-asyncmap hide-password nodetach usepeerdns mtu 1492 mru 1492 noaccomp nodeflate nopcomp novj novjccomp user aaa lcp-echo-interval 20 lcp-echo-failure 10 nopersist
3.ppp代碼創建ppp網絡接口
[ppp-2.4.7]main.c中
[ppp-2.4.7]options.c
[ppp-2.4.7]options.c 中general_options
4.pppoe驅動建立channel
the_channel->connect()爲PPPOEConnectDevice,這個函數比較長我們分段來看
Linux代碼
上圖中proto[protocol]->create即爲pppoe_create,因爲協議爲PX_PROTO_OE,在pppoe_init中註冊
回到PPPOEConnectDevice函數(續1)
在openInterface中會創建socket並綁定接口,如下兩圖所示
創建socket成功後回到PPPOEConnectDevice函數會調用discovery()函數進行pppoe協議的交互
接着看PPPOEConnectDevice函數(續2),會調用connect函數
connect函數會調用內核中的pppoe_connect,這個函數包含在pppoe_ops中,在pppoe_create中設置到sock->ops,即與我們conn->sessionSocket相關的參數中,這一過程可回看上面。
ppp_register_channel函數會生成struct channel *pch,把pch->chan 設成 chan,並把pch放到all_channels中
5.ppp驅動建立ppp網絡接口並與pppoe建立的channel關聯
回到ppp的auth.c的函數start_link中the_channel->establish_ppp()爲generic_establish_ppp
需要注意函數中的fd有改變開始時fd爲PPPOEConnectDevice函數的返回值conn->sessionSocket,對它調用ioctl 會走到linux的
pppox_ioctl函數,該函數返回PPPOEConnectDevice[ppp代碼中的plugin.c]函數調用connect創建的channel的index
接着來看 generic_establish_ppp中對fd爲"/dev/ppp"調用PPPIOCATTCHAN,make_ppp_unit和PPPIOCCONNECT的動作
調用ioctl會調用ppp_ioctl函數
PPPIOCATTCHAN時回去找channel,很顯然就是找到之前pppoe在connect創建的那個channel
ppp中make_ppp_unit會調用PPPIOCNEWUNIT
通過網絡接口ppp0(假設爲這個名字)發送數據時就會調用ppp_start_xmit,後面再看這個函數。
generic_establish_ppp函數中接着會調用PPPIOCCONNECT,通過ppp_connect_channel就將pppoe創建的channel和
ppp網絡設備聯繫在一起了
6.ppp網絡接口和ppp程序收包過程
回到ppp的main函數中看ppp程序是如何接收ppp協議的數據的
內核接收數據過程
通過ppp_receive_nonmp_frame函數大家可以看到根據收到的數據包的協議是ppp控制包還是普通網絡包比如ipv4包,走不同的分支,如果是ppp協議控制包就將其放到ppp->file.rq中,我們的ppp程序會收到這個包,如何是普通的網絡包就通過netif_rx進入網絡協議棧。
7.ppp程序和ppp網絡接口發包過程
這個pch->chan->ops->start_xmit(pch->chan, skb)調用的是pppoe_xmit,接下來我們看爲什麼是pppoe_xmit
這個channel是通過pppoe套接字然後調用connect方法創建的,創建時po->chan.ops = &pppoe_chan_ops;所以start_xmit就是pppoe_xmit