姓名:朱小鵬 ? ?學(xué)號(hào):16010130023
轉(zhuǎn)載:
http://blog.sina.com.cn/s/blog_62a85b950101anvd.html
【嵌牛導(dǎo)讀】:這一節(jié)主要針對(duì)ARP講解ARP表的創(chuàng)建澳泵,更新生蚁,查詢等操作。這里我們先從幾個(gè)簡(jiǎn)單的函數(shù)入手講解ARP各個(gè)子模塊功能藏姐,然后再將各個(gè)模塊與上層協(xié)議結(jié)合起來呀酸,宏觀的講解ARP模塊凉蜂。
【嵌牛鼻子】:ARP表的創(chuàng)建,更新性誉,查詢等操作
【嵌牛提問】:LWIP是怎樣進(jìn)行ARP表的創(chuàng)建窿吩,更新,查詢等操作错览?
【嵌牛正文】:
ARP攻擊纫雁,是針對(duì)以太網(wǎng)地址解析協(xié)議(ARP)的一種攻擊技術(shù)。在局域網(wǎng)中倾哺,ARP病毒收到廣播的ARP請(qǐng)求包轧邪,能夠解析出其它節(jié)點(diǎn)的(IP, MAC)地址,然后病毒偽裝為目的主機(jī),告訴源主機(jī)一個(gè)假M(fèi)AC地址羞海,這樣就使得源主機(jī)發(fā)送給目的主機(jī)的所有數(shù)據(jù)包都被病毒軟件截取忌愚,而源主機(jī)和目的主機(jī)卻渾然不知。ARP攻擊通過偽造IP地址和MAC地址實(shí)現(xiàn)ARP欺騙却邓,能夠在網(wǎng)絡(luò)中產(chǎn)生大量的ARP通信量使網(wǎng)絡(luò)阻塞菜循,攻擊者只要持續(xù)不斷的發(fā)出偽造的ARP響應(yīng)包就能更改目標(biāo)主機(jī)ARP緩存中的IP-MAC條目。ARP協(xié)議在設(shè)計(jì)時(shí)未考慮網(wǎng)絡(luò)安全方面的特性申尤,這就注定了其很容易遭受ARP攻擊癌幕。黑客只要在局域網(wǎng)內(nèi)閱讀送上門來的廣播ARP請(qǐng)求數(shù)據(jù)包,就能偷聽到網(wǎng)內(nèi)所有的(IP, MAC)地址昧穿。而源節(jié)點(diǎn)收到ARP響應(yīng)時(shí)勺远,它也不會(huì)質(zhì)疑,這樣黑客很容易冒充他人时鸵。
這一節(jié)主要針對(duì)ARP講解ARP表的創(chuàng)建胶逢,更新,查詢等操作饰潜。這里我們先從幾個(gè)簡(jiǎn)單的函數(shù)入手講解ARP各個(gè)子模塊功能初坠,然后再將各個(gè)模塊與上層協(xié)議結(jié)合起來,宏觀的講解ARP模塊彭雾。
第一個(gè)需要迫不及待要說的函數(shù)是find_entry碟刺,該函數(shù)最重要的輸入是一個(gè)IP地址,返回值是該IP地址對(duì)應(yīng)的ARP緩存表項(xiàng)索引薯酝。函數(shù)聲明原型如下半沽,
static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
這里爽柒,很有必要翻譯一下源代碼中注釋的內(nèi)容:該函數(shù)主要功能是尋找一個(gè)匹配的ARP表項(xiàng)或者創(chuàng)建一個(gè)新的ARP表項(xiàng),并返回該表項(xiàng)的索引號(hào)者填。如果參數(shù)ipaddr為給定的非空的內(nèi)容浩村,則函數(shù)需要返回一個(gè)處于pending或stable的索引表項(xiàng),如果沒有匹配的表項(xiàng)占哟,則該函數(shù)需要返回一個(gè)empty的表項(xiàng)心墅,但該表項(xiàng)的IP字段的值要被設(shè)置為ipaddr的值,這種情況下榨乎,find_entry函數(shù)返回后怎燥,調(diào)用者需要將表項(xiàng)從狀態(tài)empty改為pending。還有一種情況谬哀,如果參數(shù)ipaddr為空值刺覆,同樣返回一個(gè)狀態(tài)為empty的表項(xiàng)严肪。
返回狀態(tài)為empty的表項(xiàng)史煎,首先從狀態(tài)標(biāo)示為empty的空閑ARP表項(xiàng)中選取,如果這樣的表項(xiàng)都用完了驳糯,同時(shí)參數(shù)flags的值被設(shè)置為ETHARP_TRY_HARD篇梭,則find_entry就回收最老的ARP表項(xiàng),將該表項(xiàng)設(shè)置為empty狀態(tài)返回酝枢。
這個(gè)函數(shù)比較大恬偷,有將近200行代碼,這里就不貼了帘睦,直接講講它的工作流程袍患。這部分的討論還是參考了網(wǎng)上某位大俠的博客,名字記不得了竣付,對(duì)不起啊啊啊啊肮钛印!網(wǎng)絡(luò)古胆,有時(shí)確實(shí)是個(gè)好東西肆良,越發(fā)的明白。好了逸绎,看看find_entry的工作流程惹恃。
首先,lwip有一個(gè)比較巧妙的地方棺牧,它并不是沖上去就是就把a(bǔ)rp緩存中所有的表項(xiàng)搜索一遍巫糙,而是做了一個(gè)假設(shè),假設(shè)這次的表項(xiàng)索引還是上一次的(在很多情況下就是這樣的)颊乘。所以曲秉,LWIP中有個(gè)全局的變量etharp_cached_entry采蚀,它始終保存著上次用到的索引號(hào),如果這個(gè)索引恰好就是我們要找的內(nèi)容承二,且索引的表項(xiàng)已經(jīng)處于stable狀態(tài)榆鼠,那就直接返回這個(gè)索引號(hào)就完成了,we're really fast亥鸠!
如果情況不夠理想妆够,就必須去檢索整個(gè)ARP表了,檢索的過程是從ARP表的第一個(gè)表項(xiàng)開始负蚊,依次往后檢索直至最后一個(gè)表項(xiàng)神妹,過程較復(fù)雜。對(duì)于每個(gè)表項(xiàng)首先判斷它是否為empty狀態(tài)家妆,find_entry只關(guān)心第一個(gè)狀態(tài)為empty的表項(xiàng)索引值鸵荠,對(duì)該索引值以后的empty表項(xiàng)不感興趣,忽略伤极。如果一個(gè)表項(xiàng)不是empty狀態(tài)蛹找,則判斷它是不是pending狀態(tài)。對(duì)于pending狀態(tài)的表項(xiàng)哨坪,需要做以下的事情庸疾,先看看它里面存的IP地址和我們的ipaddr是否匹配,如果匹配当编,好返回該索引值届慈,記住還要更新etharp_cached_entry為該索引值,如果不匹配忿偷,則判斷該索引的數(shù)據(jù)包指針是否為空金顿,find_entry試圖記錄生存時(shí)間最長(zhǎng)的pending狀態(tài)有數(shù)據(jù)緩沖或無數(shù)據(jù)緩沖的表項(xiàng)索引。如果一個(gè)表項(xiàng)也不是pending狀態(tài)鲤桥,則判斷它是不是stable狀態(tài)揍拆。對(duì)于stable狀態(tài)的表項(xiàng),與pending狀態(tài)的表項(xiàng)處理過程相似芜壁,find_entry試圖記錄生存時(shí)間最長(zhǎng)的stable表項(xiàng)的索引礁凡。很暈吧,我也很暈慧妄,看了下面這段可能你會(huì)好點(diǎn)顷牌!
如果到這里都還沒有找到匹配的表項(xiàng),那就很杯具了塞淹,我們需要為find_entry調(diào)用者返回一個(gè)empty的表項(xiàng)索引窟蓝。經(jīng)過上面一段后,find_entry已經(jīng)知道了第一個(gè)empty狀態(tài)表項(xiàng)的索引饱普、生存時(shí)間最老的pending狀態(tài)且有數(shù)據(jù)緩沖表項(xiàng)的索引运挫、生存時(shí)間最老的pending狀態(tài)且無數(shù)據(jù)緩沖表項(xiàng)的索引状共、生存時(shí)間最老的stable狀態(tài)表項(xiàng)的索引,我們暫且先將這四個(gè)值假設(shè)為a谁帕、b峡继、c、d匈挖。如果參數(shù)flags的值被設(shè)置為ETHARP_TRY_HARD碾牌,那么find_entry會(huì)按照a-->d-->c-->b的順序選擇一個(gè)合適的索引返回,為什么是這樣的順序儡循?很明顯舶吗,不解釋。find_entry首先判斷a是否在ARP表項(xiàng)范圍內(nèi)择膝,如果是誓琼,則選擇a,如果不是肴捉,則判斷b是否在ARP表項(xiàng)范圍內(nèi)腹侣,依此類推。當(dāng)選中一個(gè)索引后每庆,隨即就會(huì)將該索引對(duì)應(yīng)的表項(xiàng)設(shè)置為empty狀態(tài)筐带,并且將該表項(xiàng)的IP地址設(shè)置為ipaddr的值今穿,ctime值設(shè)置為0缤灵,最后返回索引。至此蓝晒,find_entry大功告成腮出!
接下來,我很感興趣的一個(gè)函數(shù)是etharp_query芝薇,該函數(shù)的功能是向給定的IP地址發(fā)送一個(gè)數(shù)據(jù)包或者發(fā)送一個(gè)ARP請(qǐng)求胚嘲,當(dāng)然情況遠(yuǎn)不如此簡(jiǎn)單。還是很有必要翻譯一下源代碼中函數(shù)功能注釋的內(nèi)容:如果給定的IP地址不在ARP表中洛二,則一個(gè)新的ARP表項(xiàng)會(huì)被創(chuàng)建馋劈,此時(shí)該表項(xiàng)處于pending狀態(tài),同時(shí)一個(gè)關(guān)于該IP地址的ARP請(qǐng)求包會(huì)被廣播出去晾嘶,再同時(shí)要發(fā)送的數(shù)據(jù)包會(huì)被掛接在該表項(xiàng)的數(shù)據(jù)緩沖指針上妓雾;如果IP地址在ARP表中有相應(yīng)的表項(xiàng)存在,但該表項(xiàng)處于pending狀態(tài)垒迂,則操作與前者相同械姻,即發(fā)送一個(gè)ARP請(qǐng)求和掛接數(shù)據(jù)包;如果IP地址在ARP表中有相應(yīng)的表項(xiàng)存在机断,且表項(xiàng)處于stable狀態(tài)楷拳,此時(shí)再來判斷給定的數(shù)據(jù)包是否為空绣夺,不為空則直接將該數(shù)據(jù)包發(fā)送出去,為空則向該IP地址發(fā)送一個(gè)ARP請(qǐng)求欢揖。
etharp_query函數(shù)原型如下所示陶耍,源代碼在150行左右,這里主要講解其流程:
err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
(1)首先判斷給定的ipaddr是否合法她混,對(duì)于空IP地址物臂、廣播IP地址、多播IP地址不予處理产上。
(2)將ipaddr作為參數(shù)調(diào)用函數(shù)find_entry棵磷,函數(shù)返回一個(gè)ARP表項(xiàng)索引,該表項(xiàng)可能是原來已經(jīng)有的晋涣,此時(shí)該表項(xiàng)應(yīng)該是pending或stable狀態(tài)仪媒;該表項(xiàng)也可能是新申請(qǐng)得到的,此時(shí)該表項(xiàng)應(yīng)該是empty狀態(tài)谢鹊。
(3)根據(jù)返回的表項(xiàng)索引找到該ARP表項(xiàng)算吩,判斷該表項(xiàng)是否為empty狀態(tài),如果是佃扼,說明該表項(xiàng)是新申請(qǐng)的偎巢,則將該表項(xiàng)狀態(tài)設(shè)置為pending狀態(tài)。
(4)判斷要發(fā)送的數(shù)據(jù)包是否為空兼耀,或者判斷ARP表項(xiàng)是否為pending狀態(tài)压昼,這兩個(gè)條件只要有一個(gè)成立,就發(fā)送一個(gè)ARP請(qǐng)求出去瘤运,發(fā)送ARP請(qǐng)求的函數(shù)是etharp_request窍霞。
(5)如果待發(fā)送的數(shù)據(jù)包不為空,此刻就根據(jù)ARP表項(xiàng)的狀態(tài)作不同的處理:若ARP表項(xiàng)處于stable狀態(tài)拯坟,則直接調(diào)用函數(shù)etharp_send_ip發(fā)送數(shù)據(jù)包但金;若ARP表項(xiàng)處于pending狀態(tài),則需要將該數(shù)據(jù)包掛接到表項(xiàng)的待發(fā)送數(shù)據(jù)鏈表上郁季,由于pending狀態(tài)的表項(xiàng)必然在第(4)步中發(fā)出了一個(gè)ARP請(qǐng)求冷溃,當(dāng)內(nèi)核接收到ARP回應(yīng)時(shí),會(huì)將表項(xiàng)設(shè)置為stable狀態(tài)梦裂,并將其鏈表上的數(shù)據(jù)全部發(fā)送出去似枕,當(dāng)然這項(xiàng)工作具體是怎樣完成的那是后話了。
將數(shù)據(jù)包掛接在表項(xiàng)的發(fā)送鏈表上塞琼,這又是一個(gè)較復(fù)雜的過程:最重要的一點(diǎn)是判斷該數(shù)據(jù)包pbuf的類型菠净,對(duì)于PBUF_REF、PBUF_POOL、PBUF_RAM型的數(shù)據(jù)包不能直接掛在發(fā)送鏈表上毅往,因?yàn)檫@些數(shù)據(jù)包在被掛接后并不會(huì)被立刻發(fā)送出去牵咙,這可能導(dǎo)致數(shù)據(jù)包在等待發(fā)送的過程中內(nèi)部數(shù)據(jù)被改動(dòng)。對(duì)于以上這些類型的待發(fā)送數(shù)據(jù)包攀唯,需要將數(shù)據(jù)拷貝至新的pbuf中洁桌,然后將新的pbuf掛接至發(fā)送鏈表。至此侯嘀,etharp_query函數(shù)功德圓滿另凌!