lwip---(七)ARP表查詢

  ARP攻擊,是針對以太網地址解析協議(ARP)的一種攻擊技術。在局域網中,ARP病毒收到廣播的ARP 請求包,能夠解析出其它節點的 (IP, MAC) 地址, 然後病毒僞裝爲目的主機,告訴源主機一個假MAC地址,這樣就使得源主機發送給目的主機的所有數據包都被病毒軟件截取,而源主機和目的主機卻渾然不知。ARP攻擊通過僞造IP地址和MAC地址實現ARP欺騙,能夠在網絡中產生大量的ARP通信量使網絡阻塞,攻擊者只要持續不斷的發出僞造的ARP響應包就能更改目標主機ARP緩存中的IP-MAC條目。ARP協議在設計時未考慮網絡安全方面的特性,這就註定了其很容易遭受ARP攻擊。黑客只要在局域網內閱讀送上門來的廣播ARP請求數據包,就能偷聽到網內所有的 (IP, MAC)地址。而源節點收到ARP 響應時,它也不會質疑,這樣黑客很容易冒充他人。

  這一節主要針對ARP講解ARP表的創建,更新,查詢等操作。這裏我們先從幾個簡單的函數入手講解ARP各個子模塊功能,然後再將各個模塊與上層協議結合起來,宏觀的講解ARP模塊。

  第一個需要迫不及待要說的函數是find_entry該函數最重要的輸入是一個IP地址返回值是該IP地址對應的ARP緩存表項索引。函數聲明原型如下,

static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)

  這裏,很有必要翻譯一下源代碼中註釋的內容:該函數主要功能是尋找一個匹配的ARP表項或者創建一個新的ARP表項,並返回該表項的索引號如果參數ipaddr爲給定的非空的內容,則函數需要返回一個處於pendingstable的索引表項如果沒有匹配的表項,則該函數需要返回一個empty的表項但該表項的IP字段的值要被設置爲ipaddr的值,這種情況下,find_entry函數返回後,調用者需要將表項從狀態empty改爲pending。還有一種情況,如果參數ipaddr爲空值,同樣返回一個狀態爲empty的表項

  返回狀態爲empty的表項首先從狀態標示爲empty的空閒ARP表項中選取,如果這樣的表項都用完了,同時參數flags的值被設置爲ETHARP_TRY_HARD,則find_entry回收最老的ARP表項,將該表項設置爲empty狀態返回

  這個函數比較大,有將近200行代碼,這裏就不貼了,直接講講它的工作流程。

  首先,lwip有一個比較巧妙的地方,它並不是衝上去就是就把ARP緩存中所有的表項搜索一遍,而是做了一個假設,假設這次的表項索引還是上一次的(在很多情況下就是這樣的)。所以,LWIP中有個全局的變量etharp_cached_entry,它始終保存着上次用到的索引號,如果這個索引恰好就是我們要找的內容,且索引的表項已經處於stable狀態,那就直接返回這個索引號就完成了

  如果情況不夠理想,就必須去檢索整個ARP表了,檢索的過程是從ARP表的第一個表項開始,依次往後檢索直至最後一個表項,過程較複雜。對於每個表項首先判斷它是否爲empty狀態find_entry只關心第一個狀態爲empty的表項索引值,對該索引值以後的empty表項不感興趣,忽略。如果一個表項不是empty狀態,則判斷它是不是pending狀態對於pending狀態的表項,需要做以下的事情,先看看它裏面存的**IP地址和我們的ipaddr是否匹配**,如果匹配,好返回該索引值,記住還要更新etharp_cached_entry爲該索引值,如果不匹配,則判斷該索引的數據包指針是否爲空,find_entry試圖記錄生存時間最長的pending狀態有數據緩衝或無數據緩衝的表項索引如果一個表項也不是pending狀態,則判斷它是不是stable狀態。對於stable狀態的表項,與pending狀態的表項處理過程相似, find_entry試圖記錄生存時間最長的stable表項的索引

  如果到這裏都還沒有找到匹配的表項,那就很杯具了,我們需要爲find_entry調用者返回一個empty的表項索引。經過上面一段後,find_entry已經知道了第一個empty狀態表項的索引生存時間最老的pending狀態且有數據緩衝表項的索引生存時間最老的pending狀態且無數據緩衝表項的索引生存時間最老的stable狀態表項的索引,我們暫且先將這四個值假設爲a、b、c、d。如果參數flags的值被設置爲ETHARP_TRY_HARD,那麼find_entry按照a-->d-->c-->b的順序選擇一個合適的索引返回。find_entry首先判斷a是否在ARP表項範圍內,如果是,則選擇a,如果不是,則判斷b是否在ARP表項範圍內,依此類推。當選中一個索引後,隨即就會將該索引對應的表項設置爲empty態,並且將該表項的IP地址設置爲ipaddr的值,ctime值設置爲0,最後返回索引。至此,find_entry大功告成!

  接下來的一個函數是etharp_query,該函數的功能是向給定的IP地址發送一個數據包或者發送一個ARP請求,當然情況遠不如此簡單。還是很有必要翻譯一下源代碼中函數功能註釋的內容如果給定的IP地址不在ARP表中,則一個新的ARP表項會被創建,此時該表項處於pending狀態,同時一個關於該IP地址的ARP請求包會被廣播出去,再同時要發送的數據包會被掛接在該表項的數據緩衝指針上如果IP地址在ARP表中有相應的表項存在,但該表項處於pending狀態,則操作與前者相同,發送一個ARP請求和掛接數據包;如果IP地址在ARP表中有相應的表項存在,表項處於stable狀態,此時來判斷給定的數據包是否爲空,不爲空則直接將該數據包發送出去,爲空則向該IP地址發送一個ARP請求。

  etharp_query函數原型如下所示,源代碼在150行左右,這裏主要講解其流程:

err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)

  (1) 首先判斷給定的ipaddr是否合法,對於空IP地址、廣播IP地址、多播IP地址不予處理。

  (2) 將ipaddr作爲參數調用函數find_entry,函數返回一個ARP表項索引,該表項可能是原來已經有的,此時該表項應該是pendingstable狀態;該表項也可能是新申請得到的,此時該表項應該是empty狀態。

  (3) 根據返回的表項索引找到該ARP表項,判斷該表項是否爲empty狀態,如果是,說明該表項是新申請的,則將該表項狀態設置爲pending狀態。

  (4) 判斷要發送的數據包是否爲空,或者判斷ARP表項是否爲pending狀態,這兩個條件只要有一個成立,就發送一個ARP請求出去,發送ARP請求的函數是etharp_request

  (5) 如果待發送的數據包不爲空,此刻就根據ARP表項的狀態作不同的處理ARP表項處於**stable狀態**,則直接調用函數etharp_send_ip發送數據包ARP表項處於**pending狀態**,需要將該數據包掛接到表項的待發送數據鏈表上,由於pending狀態的表項必然在第(4)步中發出了一個ARP請求,當內核接收到ARP迴應時,會將表項設置爲stable狀態,並將其鏈表上的數據全部發送出去,當然這項工作具體是怎樣完成的那是後話了。

  將數據包掛接在表項的發送鏈表上,這又是一個較複雜的過程:最重要的一點是判斷該數據包pbuf的類型對於PBUF_REFPBUF_POOLPBUF_RAM的數據包不能直接掛在發送鏈表上,因爲這些數據包在被掛接後並不會被立刻發送出去,這可能導致數據包在等待發送的過程中內部數據被改動。對於以上這些類型的待發送數據包,需要將數據拷貝至新的pbuf中,然後將新的pbuf掛接至發送鏈表。至此,etharp_query函數功德圓滿!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章