徐大偉, 戴 鋮, 祝烈煌, 厙怡婕
(1. 北京理工大學(xué) 網(wǎng)絡(luò)空間安全學(xué)院, 北京 100081; 2. 長春大學(xué) 網(wǎng)絡(luò)安全學(xué)院, 吉林 長春 130022)
人們的生活與網(wǎng)絡(luò)的聯(lián)系越來越緊密,然而由于傳統(tǒng)網(wǎng)絡(luò)架構(gòu)的控制層與轉(zhuǎn)發(fā)層緊耦合,各個設(shè)備呈分布式管理,網(wǎng)絡(luò)行業(yè)的創(chuàng)新十分困難[1]。因此,SDN架構(gòu)應(yīng)運(yùn)而生,這是一個控制層與數(shù)據(jù)層松耦合、管理式集中的架構(gòu),并且由于SDN可編程的特點(diǎn),更有利于推進(jìn)網(wǎng)絡(luò)行業(yè)的創(chuàng)新。
但是SDN下的ARP攻擊仍然存在,并且在這種新型網(wǎng)絡(luò)架構(gòu)[2]中,所面臨的風(fēng)險形勢更加嚴(yán)峻[3]。因?yàn)镾DN架構(gòu)下的ARP攻擊不僅會攻擊終端機(jī),而且會攻擊控制器的節(jié)點(diǎn)。但是在已有的網(wǎng)絡(luò)中提出解決方案無法防止SDN中控制層遭受攻擊的風(fēng)險[4-6]。而且現(xiàn)有的SDN架構(gòu)下防止ARP攻擊的方案極少并且都有缺陷,文獻(xiàn)[7]中方案不能自動判斷ARP攻擊[7],文獻(xiàn)[8]中方案無法防止攻擊者2次接入網(wǎng)絡(luò)的攻擊[8],因此,提出一種新架構(gòu)下的解決方案極為重要。
本文闡釋了SDN網(wǎng)絡(luò)的架構(gòu)構(gòu)成,并將ARP攻擊劃分為2種方式,而本文所提方案解決了SDN架構(gòu)下這2種ARP攻擊方式的攻擊問題,基于控制器和終端機(jī)編寫了兩段程序,在虛假的ARP數(shù)據(jù)包到達(dá)目標(biāo)終端機(jī)之前被控制器檢測出并丟棄,同時控制器不會被ARP攻擊所欺騙。本文最大的創(chuàng)新在于使用了RSA非對稱密鑰算法對終端機(jī)進(jìn)行驗(yàn)證,并且使用Montgomery算法[9]對RSA進(jìn)行優(yōu)化,使得即使素數(shù)P、Q非常大也可以正常運(yùn)行。
軟件定義網(wǎng)絡(luò)(Software Defined Networking,縮寫為“SDN”)是美國斯坦福大學(xué)Nick McKeown教授團(tuán)隊(duì)提出的一種新型網(wǎng)絡(luò)創(chuàng)新架構(gòu)[10],它將網(wǎng)絡(luò)的控制平面和數(shù)據(jù)平面解耦分離[11],是實(shí)現(xiàn)高帶寬、動態(tài)網(wǎng)絡(luò)的理想架構(gòu),有利于網(wǎng)絡(luò)行業(yè)的創(chuàng)新。
SDN的基本思想是路由控制平面和數(shù)據(jù)轉(zhuǎn)發(fā)平面互相分離,數(shù)據(jù)平面更為通用,不再關(guān)注網(wǎng)絡(luò)協(xié)議的轉(zhuǎn)發(fā),只需要接收控制平面的命令并進(jìn)行轉(zhuǎn)發(fā)[12];路由轉(zhuǎn)發(fā)平面被從各個設(shè)備中剝離到一個統(tǒng)一的外部控制器,控制器掌握著所有網(wǎng)絡(luò)的信息[13-14],需要接收數(shù)據(jù)層面發(fā)送的消息,并指引數(shù)據(jù)平面進(jìn)行消息的轉(zhuǎn)路徑發(fā)送。SDN對整個網(wǎng)絡(luò)的資源的調(diào)度和管控性是傳統(tǒng)網(wǎng)絡(luò)不能相比的。
SDN架構(gòu)按層次結(jié)構(gòu)劃分,如圖1所示,包含5個層次,即應(yīng)用程序?qū)?、北向接口層、控制層、南向接口層和?shù)據(jù)層[15-16]。
SDN應(yīng)用程序?qū)又饕删W(wǎng)絡(luò)管理員或其他網(wǎng)絡(luò)研究人員用來部署一些網(wǎng)絡(luò)應(yīng)用程序,例如,負(fù)載均衡和訪問控制等[14]。北向接口層為其上層提供網(wǎng)絡(luò)的籠統(tǒng)視圖,使用戶有機(jī)會根據(jù)自己的需求開發(fā)適當(dāng)?shù)膽?yīng)用程序,根據(jù)網(wǎng)絡(luò)條件規(guī)劃資源??刂茖佑煽刂破鹘M成[17],主要有3個任務(wù):①將SDN最上層的需求轉(zhuǎn)交到軟件定義網(wǎng)絡(luò)的交換機(jī);②為軟件定義網(wǎng)絡(luò)應(yīng)用提供底層網(wǎng)絡(luò)的視圖;③對轉(zhuǎn)發(fā)層面進(jìn)行轉(zhuǎn)發(fā)的調(diào)節(jié)、自定義管理。南向接口層有如下功能:控制傳輸、查詢設(shè)備性能、報告統(tǒng)計和通知事件等,可以理解為數(shù)據(jù)平面的一個編程接口。數(shù)據(jù)層主要功能是執(zhí)行來自控制層的流規(guī)則,并應(yīng)答控制器的指令[18-19]。
圖1 SDN架構(gòu)層次[1]Fig.1 SDN architecture levels
OpenFlow協(xié)議是SDN架構(gòu)南向接口協(xié)議標(biāo)準(zhǔn),該協(xié)議定義了交換機(jī)傳輸計劃的某些功能組件[20],定義了交換機(jī)的工作機(jī)制和交換機(jī)的管控機(jī)制,同時也定義了控制器和交換機(jī)在通信過程中的消息格式和類型[21]。
流在協(xié)議中的定義是一個報文的集合,即在某一個時間段內(nèi),經(jīng)過某一網(wǎng)絡(luò),具有相同屬性,并按照一定次序發(fā)送的報文的集合[22]。其中流表是OpenFlow協(xié)議交換機(jī)中的轉(zhuǎn)發(fā)表[23]。協(xié)議交換機(jī)中的流表項(xiàng)等效于在傳統(tǒng)網(wǎng)絡(luò)中集成所有級別的網(wǎng)絡(luò)配置信息,因此,在轉(zhuǎn)發(fā)數(shù)據(jù)時使用更多樣的規(guī)則。
OpenFlow交換機(jī)的數(shù)據(jù)包處理流程為:數(shù)據(jù)包進(jìn)入交換機(jī),交換機(jī)中的協(xié)議解析模塊解析數(shù)據(jù)包包頭域,然后將解析后的結(jié)果與對應(yīng)的流表進(jìn)行匹配[24]。到了流表內(nèi)部,解析后的結(jié)果與每個流表項(xiàng)進(jìn)行比較,如果匹配成功,則根據(jù)表項(xiàng)上的動作進(jìn)行處理,否則丟棄或轉(zhuǎn)發(fā)給控制器以請求指令[25-26]。
(1)攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊
攻擊者在TCP/IP的網(wǎng)絡(luò)層面,竊取合法用戶的終端機(jī)的IP地址或者M(jìn)AC地址[24],使其他終端機(jī)和控制層面錯誤地將攻擊者的IP地址誤判為對應(yīng)合法終端機(jī)的MAC地址,或者錯誤地將攻擊者的MAC地址誤認(rèn)為對應(yīng)的合法終端機(jī)的IP地址[27-28]。
(2)攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊
攻擊者在網(wǎng)絡(luò)層和鏈路層同時竊取合法用戶終端機(jī)的IP地址和MAC地址,以欺騙控制層面產(chǎn)生錯誤的MAC地址、IP地址與接入位置映射信息[29]。
這2種攻擊形式在傳統(tǒng)的ARP攻擊解決方案中并沒有進(jìn)行劃分,只有在文獻(xiàn)[3]中對這2種形式有具體的區(qū)分。
方案設(shè)計思路如圖2所示。終端機(jī)在接入網(wǎng)絡(luò),獲得IP地址之后需要向控制器注冊驗(yàn)證信息,其中包括終端機(jī)的MAC地址和RSA公鑰。同時控制器會將IP地址、MAC地址和接入端口號等做成驗(yàn)證信息表項(xiàng),放在以交換機(jī)身份ID為索引的信息表中。在通信中,終端機(jī)發(fā)出ARP Request包[30],到達(dá)交換機(jī)后找不到對應(yīng)流,就會把該數(shù)據(jù)包上發(fā)控制器??刂破鲝腜acket_in消息中獲得ARP數(shù)據(jù)包[31]之后,將該包的IP地址、MAC地址映射與終端機(jī)的信息表項(xiàng)進(jìn)行比對,如果映射對應(yīng)不正確,則向DHCP服務(wù)器請求最新映射,如果還匹配不成功,則向管理員上報ARP攻擊;如果對應(yīng)成功則再將收到該數(shù)據(jù)包時的端口與匹配成功的表項(xiàng)對比,如果端口也匹配成功則代替目標(biāo)終端機(jī)回答該ARP報文,如果端口匹配不成功,則記錄此次ARP請求的目標(biāo)地址并以該報文MAC地址對應(yīng)的公鑰加密一條隨機(jī)消息發(fā)回該終端機(jī),如果該終端機(jī)的MAC地址真實(shí),將會回答出正確的信息,交換機(jī)會將請求的IP地址所對應(yīng)的MAC地址通過消息返回給終端機(jī),并且更新該終端機(jī)的信息。
圖2 方案設(shè)計基本思路Fig.2 Basic idea of scheme design
總之,本方案中,需要每個終端機(jī)向控制器注冊信息,而控制器代替目標(biāo)機(jī)回復(fù)ARP請求,并且只有通過驗(yàn)證的ARP請求才能收到回復(fù)。
本文所提方案要求控制器端擁有檢測ARP消息正確與否的能力,同時也需要終端機(jī)擁有檢測到控制器發(fā)送的驗(yàn)證信息并恢復(fù)的能力。方案主要包括2個主要階段:驗(yàn)證信息交換階段和通信中攻擊防御階段。
(1)驗(yàn)證信息交換階段主要分為5個步驟
步驟1:終端機(jī)生成RSA公鑰和私鑰。每個終端機(jī)有屬于自己的2個大素數(shù)P,Q,這2個大素數(shù)為自己的特征值,終端機(jī)利用自己的特征值計算出公鑰和私鑰,將公鑰存在本地,RSA算法流程圖如圖3所示。
圖3 RSA算法流程圖Fig.3 RSA algorithm flow chart
步驟2:終端機(jī)與控制器交換公私鑰及其它信息。終端機(jī)通過一個ARP消息向控制器發(fā)送注冊消息,包括自身的IP地址、MAC地址和最初接入端口以及自己的公鑰,其中終端機(jī)公鑰封裝在ARP消息的payload域中。帶有payload域的ARP消息如圖4所示。
圖4 帶有payload域的ARP消息Fig.4 ARP message with payload field
步驟3:控制器為終端機(jī)建立驗(yàn)證信息表??刂破魇盏浇K端機(jī)的公鑰和其它信息之后,在該終端機(jī)的交換機(jī)ID表項(xiàng)中創(chuàng)建一個驗(yàn)證信息表,如圖5所示,包括交換機(jī)身份ID、終端機(jī)的IP地址和MAC地址、初始接入端口、終端機(jī)的公鑰和為該終端機(jī)分配的密鑰對中的私鑰。
圖5 終端機(jī)驗(yàn)證信息表項(xiàng)Fig.5 Terminal verification information table entry
步驟4:控制器給每個終端機(jī)分配特征值并生成RSA公鑰和私鑰??刂破飨纫宰陨硖卣髦惦S機(jī)生成一對公鑰和私鑰,將私鑰存入對應(yīng)終端機(jī)的驗(yàn)證信息表項(xiàng)中。
步驟5:控制器向終端機(jī)分發(fā)RSA公鑰??刂破鲗⑸傻拿荑€對中的公鑰封裝在ARP的payload域并發(fā)送給對應(yīng)終端機(jī),終端機(jī)收到后將此公鑰存在本地,此步驟的目的是為了防止驗(yàn)證過程中中間人竊取驗(yàn)證的信息。
(2)攻擊防御階段主要分為3個步驟
步驟1:驗(yàn)證源地址對應(yīng)信息。當(dāng)控制器收到ARP請求包時,依照源MAC地址找尋對應(yīng)的表項(xiàng),如果交換機(jī)身份ID、MAC地址和IP地址、連接端口都相同的話,則以ARP Reply消息回復(fù)此數(shù)據(jù)包;如果MAC地址和IP地址映射不正確,則要繼續(xù)驗(yàn)證,如果驗(yàn)證無法通過,則將此報告給管理人員,類型是攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊;如果MAC地址和IP地址的映射正確,但是與交換機(jī)的連接端口不正確,則需要進(jìn)一步驗(yàn)證;如果找不到該對應(yīng)的表項(xiàng),說明該終端機(jī)未向控制器注冊信息,需要注冊后才能通信。
當(dāng)MAC地址和IP地址映射不正確時,首先將此次ARP請求的目的IP地址記錄在對應(yīng)的表項(xiàng)預(yù)留處,之后DHCP服務(wù)器請求該MAC地址最新對應(yīng)的IP地址,并修改驗(yàn)證表項(xiàng),然后再與此次ARP 請求報文的IP-MAC映射進(jìn)行比對,如果匹配不成功,則向管理員上報ARP攻擊,類型為攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊,并刪去預(yù)留的IP地址;如果匹配成功,以表項(xiàng)中預(yù)留的IP地址和此IP地址對應(yīng)的MAC地址為源地址向請求終端機(jī)回復(fù)ARP Reply,同時也刪去預(yù)留的IP地址。
步驟2:查驗(yàn)源MAC地址是否合法。如果可以查到MAC地址對應(yīng)的表項(xiàng),則直接比對交換機(jī)身份ID、MAC地址、IP地址和連接端口等信息即可驗(yàn)證身份。如果MAC地址和IP地址映射正確,但是接入端口不正確時,則隨機(jī)地生成一條信息,并以終端機(jī)對應(yīng)的RSA公鑰對信息進(jìn)行加密,發(fā)送給終端機(jī),此時如果終端機(jī)能回復(fù)正確的消息,則更新終端機(jī)驗(yàn)證信息表中終端機(jī)的位置,并且以表項(xiàng)中預(yù)留的IP地址和此IP地址對應(yīng)的MAC地址為源地址向請求終端機(jī)回復(fù)ARP Reply,并刪去預(yù)留的IP地址;如果源終端機(jī)不能回復(fù)正確的消息,或者等待超時后,直接刪去預(yù)留的IP地址。則向管理人員上報ARP攻擊,類型為攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊。
步驟3:驗(yàn)證目的終端機(jī)是否離開。當(dāng)終端機(jī)通過驗(yàn)證后,控制器向目標(biāo)終端機(jī)的IP地址發(fā)送一個帶有驗(yàn)證消息的數(shù)據(jù)包,如果目的終端機(jī)能回復(fù)正確的消息,并且接入端口等信息沒有變化,就說明目的終端機(jī)沒有移動;如果目的終端機(jī)能通過驗(yàn)證,但是接入端口等消息已經(jīng)發(fā)送了變化,則修改終端機(jī)驗(yàn)證信息表;如果目的終端機(jī)不能通過驗(yàn)證,說明終端機(jī)已經(jīng)離開,則等待目的終端機(jī)重新注冊。
(1)對于“攻擊者在網(wǎng)絡(luò)層或鏈路層”的ARP攻擊
這種ARP攻擊的特性是IP地址和MAC地址的映射關(guān)系一定不真實(shí)。本方案中,如果終端機(jī)要發(fā)送ARP請求數(shù)據(jù)包,必須先在控制器處注冊驗(yàn)證信息。在通信過程中,終端機(jī)發(fā)起ARP請求,控制器接收之后會將ARP數(shù)據(jù)包的源IP地址和MAC地址與注冊時的映射比較,如果映射不正確,則向DHCP服務(wù)器申請最新映射并比較,如果源終端機(jī)無法通過控制器的驗(yàn)證,則被判定為攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊。
(2)對于“攻擊者同時利用網(wǎng)絡(luò)層和鏈路層”的ARP攻擊
這種ARP攻擊的特性是ARP數(shù)據(jù)包中的MAC地址與終端機(jī)本身的MAC地址不同。在本方案中,控制器通過交換機(jī)上傳的Packet_in報文中終端機(jī)的連接端口與驗(yàn)證信息表中連接端口比較,然后向源終端機(jī)發(fā)送驗(yàn)證消息,如果其可以通過驗(yàn)證,則更新源終端機(jī)對應(yīng)MAC地址的表項(xiàng),如果沒有通過驗(yàn)證,則被判定為攻擊者,同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊。
該部分代碼在控制器安全模塊和終端機(jī)的防御模塊中都需要使用,用于生成屬于自己的公鑰與私鑰,并發(fā)給對方。
(1)計算最大公約數(shù)
∥ 尋找num1、num2的最大公約數(shù)
∥ GCD(num1,num2) = GCD(num2,num1 Mod num2)
Integer:Gcd(Integer:num1, Integer:num2)
While(num2 != 0)
∥計算余數(shù)
Integer:remainder = num1 Mod num2
∥計算GCD(num2,remainder)
num1 = num2
num2 = remainder
End While
∥GCD(num1,0)為num1
Return num1
End Gcd
(2)計算大整數(shù)大次冪并對大的整數(shù)取模
#超大整數(shù)超大次冪然后對超大的整數(shù)取模(bs ^ ept) mod n
def ep_md(bs, ept, n):
bin_a = bin(ept)[2:][::-1]
r = len(bin_a)
bs_a = []
pre_bs = bs
bs_a.append(pre_bs)
for _ in range (r - 1):
next_bs = (pre_bs * pre_bs) % n
bs_a.append(next_bs)
pre_bs = next_bs
a_w_b = __multi (bs_a, bin_a)
return a_w_b % n
def __multi (a, bin_a):
result = 1
for index in range(len(a)):
a = a[index]
if not int(bin_a[index]):
continue
result *= a
return result
在此步驟中,使用到了Montgomery算法,該算法最大作用的是使RSA 2個素數(shù)在數(shù)值特別大時也可以使用,由于Montgomery與本文討論的方向不同,故此處不再詳細(xì)介紹。
(3)公鑰與私鑰生成
def cre_key(p, q):
n=p*q
#計算與n互質(zhì)的整數(shù)個數(shù) 歐拉函數(shù)
fi=(p-1)*(q-1)
#選取e,一般選取65537,此處為示例故取小數(shù)值
e=13
a=e
b=fi
r, x, y = ext_gcd(a, b)
#計算出的x不能是負(fù)數(shù),如果是負(fù)數(shù),說明p、q、e選取失敗
while x<0:
e=e+1
if gcd(e, fi) !=1:
continue
a=e
b=fy
r, x, y = ext_gcd(a, b)
d=x
return (n, e), (n, d)
# 加密,m是明文,c為密文
def encrypt(m, pub_key):
n=pub_key[0]
e=pub_key[1]
c=ep_md(m, e, n)
return c
# 解密,c是密文,m是明文
def decrypt(c, self_key):
n=self_key[0]
d=self_key[1]
m=ep_md(c, d, n)
return m
控制器部分的安全模塊就是本文所提的方案,終端機(jī)在進(jìn)入網(wǎng)絡(luò)獲得IP地址之后,就向控制器注冊信息,包括IP地址、MAC地址、終端機(jī)接入端口和以自己特征值生成的RSA公鑰,控制器為每個終端機(jī)維護(hù)這一表項(xiàng)。當(dāng)有終端機(jī)發(fā)起ARP請求時,交換機(jī)轉(zhuǎn)發(fā)給控制器,控制器比對表項(xiàng),如果IP地址和MAC地址映射不正確,或者IP地址和MAC地址映射正確但終端機(jī)接入端口號不正確,則需要驗(yàn)證,驗(yàn)證通過就代替目標(biāo)終端機(jī)回復(fù)ARP應(yīng)答報文,驗(yàn)證不通過就向管理員報告ARP攻擊。而驗(yàn)證不通過時,如果是IP地址與MAC地址不匹配,則為攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊,如果IP地址與MAC地址匹配,但是與接入接口不匹配,則為攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊。
(1)原POX控制器數(shù)據(jù)包處理代碼
該部分為原POX控制器處理數(shù)據(jù)包過程,主要是創(chuàng)建數(shù)據(jù)包對象,過濾掉無法解析的數(shù)據(jù)包和鏈路層發(fā)現(xiàn)協(xié)議數(shù)據(jù)包,并且當(dāng)交換機(jī)的dpid不在ARP表中,將該減緩及匹配的虛擬網(wǎng)關(guān)加入ARP表中。
def _handle_openflow_PacketIn(self,event):
1)創(chuàng)建數(shù)據(jù)包對象,
dpid=event.connection.dpid
inport=event.port
packet=event.parsed
2)過濾無法解析的數(shù)據(jù)包,
if not packet.parsed:
log.warning("%i %i ignoring unparsed packet", dpid, inport)
return
3)如果交換機(jī)的dpid不在ARP表中,則為該交換機(jī)創(chuàng)建一個ARP表項(xiàng),并將交換機(jī)匹配的虛擬網(wǎng)關(guān)加入ARP表中,
if dpid not in self.arpTable:
self.arpTable[dpid]={}
for fake in self.fakeways:
self.arpTable[dpid][IPAddr(fake)]=Entry(of.OFPP_NONE,dpid_to_mac(dpid))
4)過濾LLDP報文,
if packet.type == ethernet.LLDP_TYPE:
Return
(2)原POX控制器處理IPv4數(shù)據(jù)包
if isinstance(packet.next, ipv4):
log.debug("%i %i IP %s => %s", dpid, inport,packet.next.srcip, packet.next.dstip)
#嘗試將緩沖區(qū)等待的數(shù)據(jù)包發(fā)出去
self._send_lost_buffers(dpid, packet.next.srcip, packet.src, inport)
1)如果包源IP在ARP表中,學(xué)習(xí)或者更新端口或MAC表項(xiàng),并且過濾ARP表項(xiàng)的入端口和包源地址不匹配的情況,
if packet.next.srcip in self.arpTable[dpid]:
if self.arpTable[dpid][packet.next.srcip] != (inport, packet.src):
#向管理員報arp攻擊
print 'there is a arp attrack'
2)如果包源IP不在arp表中,對應(yīng)包dpid和包源地址的條例創(chuàng)建為此包的匹配項(xiàng)目,
else:
log.debug("%i %i learned %s", dpid, inport, packet.next.srcip)
self.arpTable[dpid][packet.next.srcip]=Entry(inport, packet.src)
#嘗試轉(zhuǎn)發(fā),取包的目的地址
dstaddr=packet.next.dstip
3)如果目的地址在ARP表中,有端口信息并將消息發(fā)出,
if dstaddr in self.arpTable[dpid]:
#取表中目的交換機(jī)和目的地址對應(yīng)的端口
prt=self.arpTable[dpid][dstaddr].port
#取表中目的交換機(jī)和目的地址對應(yīng)的地址
mac=self. arpTable[dpid][dstaddr].mac
#排除出端口等于入端口的情況
if prt == inport:
log.warning("%i %i not sending packet for %s back out of the ""input port" % (dpid, inport, dstaddr))
else:
log.debug("%i %i installing flow for %s => %s out port %i"% (dpid, inport, packet.next.srcip, dstaddr, prt))
#建立行為列表
actions=[]
#添加動作,為設(shè)置目的MAC地址
actions.append(of.ofp_action_dl_addr. set_dst(mac))
#添加動作,為交換機(jī)發(fā)消息的出端口為ARP表中目的交換機(jī)的端口
actions.append(of.ofp_action_output (port=prt))
if self.wide:#如果為廣泛匹配
match=of.ofp_match(dl_type=packet.type, nw_dst=dstaddr)
else:
match=of.ofp_match.from_packet(packet, inport)
#添加新的流表項(xiàng)
msg=of.ofp_flow_mod(command =of.OFPFC_ADD,
#在FLOW_IDLE_TIMEOUT時間內(nèi),如果沒有報文觸發(fā),則該規(guī)則刪除
idle_timeout=FLOW_IDLE_TIMEOUT,
#到達(dá)OFP_FLOW_PERMANENT時間時,無論如何都刪除該規(guī)則
hard_timeout= of.OFP_FLOW_PERMANENT,
#設(shè)計數(shù)據(jù)包存儲在數(shù)據(jù)路徑中的緩沖區(qū)的ID
buffer_id=event.ofp.buffer_id,
actions=actions,
match=match)
event.connection.send(msg.pack())
4)當(dāng)目的地址不在ARP表中的情況,
elif self.arp_for_unknowns:
#如果交換機(jī)的dpid和目的地址不在緩沖區(qū)內(nèi),創(chuàng)建該交換機(jī)的表項(xiàng)
if (dpid, dstaddr) not in self.lost_buffers:
self.lost_buffers[(dpid, dstaddr)]=[]
#取緩沖區(qū)的表項(xiàng)
bucket=self.lost_buffers[(dpid,dstaddr)]
#建立條例,當(dāng)前的時間,條例最大存在時間,數(shù)據(jù)包存儲在數(shù)據(jù)路徑中的緩沖區(qū)的ID,入端口
entry=(time.time() + MAX_BUFFER_TIME, event.ofp.buffer_id, inport)
bucket.append(entry)
#如果bucket項(xiàng)目的長度大于交換機(jī)上用于未知ip的最大數(shù)據(jù)包數(shù)
while len(bucket) > MAX_BUFFERED_PER_IP: del bucket [0]
# Expire things from our outstanding ARP list...
self.outstanding_arps={k: v for k, v in self.outstanding_arps.iteritems() if v > time.time()}
if (dpid, dstaddr) in self.outstanding_arps:
return
# 目的地址表項(xiàng)過期時間為4 s,不再接收
self.outstanding_arps[(dpid, dstaddr)]=time.time() + 4
#構(gòu)建一個ARP消息
r=arp()
#ARP消息類型為以太網(wǎng)
r.hwtype=r.HW_TYPE_ETHERNET
#報文proto類型為IP
r.prototype=r.PROTO_TYPE_IP
r.hwlen=6
r.protolen=r.protolen
r.opcode=r.REQUEST
r.hwdst=ETHER_BROADCAST
#協(xié)議目的為目標(biāo)地址
r.protodst=dstaddr
#源地址是請求包的地址
r.hwsrc=packet.src
#協(xié)議源是請求包源IP
r.protosrc=packet.next.srcip
r.payload='1111'
e=ethernet (type=ethernet.ARP_TYPE, src= packet.src,dst=ETHER_BROADCAST)
#將ARP消息封裝在鏈路層協(xié)議中
e.set_payload(r)
log.debug("%i %i ARPing for %s on behalf of %s" % (dpid, inport,r.protodst, r.protosrc))
#指示交換機(jī)發(fā)送數(shù)據(jù)包
msg=of.ofp_packet_out()
#將e封裝在msg的數(shù)據(jù)域中
msg.data=e.pack()
msg.actions.append (of.ofp_action_output(port=of.OFPP_FLOOD))
msg.in_port=inport
event.connection.send(msg)
(3)處理ARP數(shù)據(jù)包的ARP防御模塊
elif isinstance(packet.next, arp):
1)創(chuàng)建一個ARP數(shù)據(jù)包對象,并過濾一些數(shù)據(jù)包,
a=packet.next
log.debug("%i %i ARP %s %s => %s", dpid, inport,{arp.REQUEST: "request", arp.REPLY: "reply"}.get(a.opcode,'op:%i' % (a.opcode,)), a.protosrc, a.protodst)
#過濾出ARP數(shù)據(jù)包
if a.prototype == arp.PROTO_TYPE_IP:
#如果是鏈路層協(xié)議
if a.hwtype == arp.HW_TYPE_ETHERNET:
#并且包的協(xié)議源地址不為0.0.0.0
if a.protosrc != 0:
#過濾目的地址為控制器本身的數(shù)據(jù)包
if a.protodst == '10.168.1.1':
2)當(dāng)ARP數(shù)據(jù)包為密鑰交換數(shù)據(jù)包時,為終端機(jī)分發(fā)私鑰并記錄終端機(jī)的公鑰,
if a.payload != '':
if '(' in str(a.payload):
#為該終端機(jī)生成密鑰
pubkey, selfkey=gen_key(p, q)
self.macTable[packet.src]={}
#將a負(fù)載域中的密鑰記錄在對應(yīng)的表項(xiàng)中
self.macTable[packet.src]['host_pubkey']=a.payload
#將生成的密鑰加入到a對應(yīng)的表項(xiàng)中
self.macTable[packet.src] ['controller_prakey']=selfkey
r=arp()
r.hwtype=a.hwtype
r.prototype=a.prototype
r.hwlen=a.hwlen
r.protolen=a.protolen
r.opcode=arp.REPLY
r.hwdst=a.hwsrc
r.protodst=a.protosrc
r.protosrc=a.protodst
r.payload=str(pubkey)
r.hwsrc=a.hwdst
e=ethernet (type=packet.type, src= dpid_to_mac(dpid),dst=a.hwsrc)
e.set_payload(r)
log.debug("%i %i answering ARP for %s" % (dpid, inport,r.protosrc))
msg=of.ofp_packet_out()
msg.data=e.pack()
msg.actions.append (of.ofp_action_output(port=of.OFPP_IN_PORT))
msg.in_port=inport
event.connection.send(msg)
return
3)當(dāng)ARP數(shù)據(jù)包為驗(yàn)證信息數(shù)據(jù)包時,用終端機(jī)的公鑰解密,并比對控制器本身生成的驗(yàn)證信息,
else:
recheck_prakey=self.macTable [packet.src]['controller_praker']
recheck_msg=a.payload
#用a的公鑰解密
recheck_num=decrypt (recheck_msg, recheck_prakey)
#比對解密后的信息與自己分發(fā)的信息
if recheck_num == self.macTable [packet.src]['check_num']:
self.arpTable[dpid][a.protosrc].port=inport
if self.macTable[packet.src] ['request_ipaddr'] in self.arpTable[dpid]:
# We have an answer...
# if not self.arpTable[dpid] [a.protodst].isExpired():
r=arp()
r.hwtype=a.hwtype
r.prototype=a.prototype
r.hwlen=a.hwlen
r.protolen=a.protolen
r.opcode=arp.REPLY
r.hwdst=a.hwsrc
r.protodst=a.protosrc
r.protosrc=self.macTable[packet.src]['request_ipaddr']
r.hwsrc=self.macTable[packet.src]['request_macaddr']
e=ethernet (type=packet.type, src=dpid_to_mac(dpid),dst=a.hwsrc)
e.set_payload(r)
log.debug("%i %i answering ARP for %s" % (dpid, inport,r.protosrc))
msg=of.ofp_packet_out()
msg.data=e.pack()
msg.actions.append (of.ofp_action_output(port=of.OFPP_IN_PORT))
msg.in_port=inport.connection.send(msg)
return
else:
#上報管理員發(fā)生網(wǎng)絡(luò)層和鏈路層的攻擊
print 'there is a arp attrack which steal and or mac'
return
else:
return
4)如果終端機(jī)的協(xié)議IP地址存在于主機(jī)驗(yàn)證表項(xiàng),則比對與MAC地址和接入端口的對應(yīng)關(guān)系,判斷是否發(fā)生ARP攻擊,
if a.protosrc in self.arpTable[dpid]:
#a的IP地址與MAC地址不對應(yīng),則上報管理員有網(wǎng)絡(luò)層或鏈路層的ARP攻擊
if self.arpTable[dpid][a.protosrc].mac != packet.src:
#上報管理員發(fā)生網(wǎng)絡(luò)層或鏈路層的攻擊
print 'there is a arp attrack which only steal ip or mac'
return
else:
#如果a的IP地址和MAC地址都與驗(yàn)證表中相同,但輸入端口不相同
if self.arpTable[dpid][a.protosrc].port != inport:
self.macTable[packet.src] ['request_ipaddr']=a.protodst
self.macTable[packet.src]['request_macaddr']=packet.src
check_pubkey=self.macTable[packet.src]['host_pubkey']
check_num=random.randint(0, 300)
check_msg=encrypt(check_num, check_pubkey)
self.macTable[packet.src]['check_num']=str(check_msg)
r=arp()
r.hwtype=a.hwtype
r.prototype=a.prototype
r.hwlen=a.hwlen
r.protolen=a.protolen
r.opcode=arp.REPLY
r.hwdst=a.hwsrc
r.protodst=a.protosrc
r.protosrc=a.protodst
r.payload=str(check_msg)
r.hwsrc=self.arpTable[dpid][a.protodst].mac
e=ethernet(type=packet.type, src=dpid_to_mac(dpid),dst=a.hwsrc)
e.set_payload(r)
log.debug("%i %i answering ARP for %s" % (dpid, inport,r.protosrc))
msg=of.ofp_packet_out()
msg.data=e.pack()
msg.actions.append(of.ofp_action_output(port=of.OFPP_IN_PORT))
msg.in_port=inport
event.connection.send(msg)
#上報管理員發(fā)生網(wǎng)絡(luò)層和鏈路層的攻擊
print 'there is a arp attrack which steal and or mac'
return
else:
log.debug("%i %i learned %s", dpid, inport, a.protosrc)
self.arpTable[dpid][a.protosrc]=Entry(inport, packet.src)
self._send_lost_buffers(dpid, a.protosrc, packet.src, inport)
if a.protosrc in self.arpTable[dpid]:
5)如果終端機(jī)身份驗(yàn)證成功,則正?;貜?fù)ARP數(shù)據(jù)包,
if self.arpTable[dpid][a.protosrc] == (inport, packet.src):
if a.opcode == arp.REQUEST:
# Maybe we can answer
if a.protodst in self.arpTable[dpid]:
if not self.arpTable[dpid] [a.protodst].isExpired():
r=arp()
r.hwtype=a.hwtype
r.prototype=a.prototype
r.hwlen=a.hwlen
r.protolen=a.protolen
r.opcode=arp.REPLY
r.hwdst=a.hwsrc
r.protodst=a.protosrc
r.protosrc=a.protodst
r.payload='111111'
r.hwsrc=self.arpTable[dpid][a.protodst].mac
e=ethernet(type=packet.type, src= dpid_to_mac(dpid),dst=a.hwsrc)
e.set_payload(r)
log.debug("%i %i answering ARP for %s" % (dpid, inport,r.protosrc))
msg=of.ofp_packet_out()
msg.data=e.pack()
msg.actions.append (of.ofp_action_output(port=of.OFPP_IN_PORT))
msg.in_port=inport
event.connection.send(msg)
return
log.debug("%i %i flooding ARP %s %s => %s" % (dpid, inport,{arp.REQUEST: "request", arp.REPLY: "reply"}.get(a.opcode,'op:%i' % (a.opcode,)),a.protosrc, a.protodst))
msg=of.ofp_packet_out(in_port=inport, data=event.ofp,action=of.ofp_action_output(port=of.OFPP_FLOOD))
event.connection.send(msg)
終端機(jī)安全模塊主要包括RSA加密部分和以下用于信息驗(yàn)證部分,信息驗(yàn)證部分包括了ARP嗅探,建立與控制器匹配的密鑰表項(xiàng)和發(fā)送驗(yàn)證ARP數(shù)據(jù)包。
f_arp_msg=sniff(filter='ARP')
if f_arp_msg.load is not None:
if '(' in f_arp_msg.load:
macTable['mac']=f_arp_msg.hwsrc
macTable['mac']['pubkey']=f_arp_msg.load
else:
pkt_hwsrc=f_arp_msg.hwsrc
pkt_psrc=f_arp_msg.psrc
check_msg=random.randint(0,300)
check_pubkey=macTable ['mac']['pubkey']
check_im=encrypt (check_msg,check_pubkey)
實(shí)驗(yàn)的仿真模擬環(huán)境中,需要一臺虛擬機(jī),具體配置如下:
(1)操作系統(tǒng)為Linux Ubuntu14.04 LTS或者更高版本;
(2)CPU英特爾i7處理器,主頻2.5 GHz;
(3)內(nèi)存2GB或者更高配置;
(4)運(yùn)行SDN控制器的網(wǎng)絡(luò)模擬平臺。
其中,SDN控制器選用POX 0.5.0開源控制器,使用Open vSwitch交換機(jī),虛擬網(wǎng)絡(luò)運(yùn)用Mininet 2.3.0網(wǎng)絡(luò)仿真平臺構(gòu)建,南向接口協(xié)議使用OpenFlow v1.0協(xié)議,使用Wireshark 2.6.6作為流量分析工具,其次使用Scapy 2.4.3[36]作為ARP攻擊的手段,編程語言為Python 2.7。
實(shí)驗(yàn)建立如圖6的網(wǎng)絡(luò)拓?fù)?。首先啟動POX控制器,如圖7,然后執(zhí)行mn命令創(chuàng)建一個簡單的網(wǎng)絡(luò)拓?fù)?如圖8,控制器指定為POX,使用Open vSwitch作為交換機(jī)。
圖6 本實(shí)驗(yàn)網(wǎng)絡(luò)拓?fù)銯ig.6 Network topology of this experiment
圖7 啟動pox控制器Fig.7 Start pox controller
圖8 建立網(wǎng)絡(luò)拓?fù)銯ig.8 Establish network topology
假設(shè)圖6中H1S2為攻擊者,對H2S2發(fā)起攻擊,攻擊的類型分為2種:①針對網(wǎng)絡(luò)層或鏈路層的ARP攻擊;②針對網(wǎng)絡(luò)層和鏈路層的ARP攻擊。
(1)攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊
攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP欺騙程序代碼如圖9,欺騙程序先盜用 H1S1 的IP地址,同時每間隔3 s向終端機(jī)H2S2發(fā)送 ARP 請求報文。首先觀察沒有使用方案之前該網(wǎng)絡(luò)遭受此類 ARP 攻擊的情況。終端機(jī) H1S2 發(fā)起攻擊之后,交換機(jī) S2 由于沒有對應(yīng)的流表項(xiàng),于是把消息封裝成 Packet_in消息,上傳給控制器,此時控制器已經(jīng)受到了欺騙,控制器將該數(shù)據(jù)包的相關(guān)信息記錄后,指示交換機(jī) S2 將其洪泛,于是終端機(jī) H2S2 接收到了終端機(jī) H1S2 的數(shù)據(jù)包,同時修改自身的 ARP 緩存(圖10),并進(jìn)行回復(fù),交換機(jī)S2 又因?yàn)闆]有找到對應(yīng)流表項(xiàng),于是上報控制器,控制器記錄該數(shù)據(jù)包的信息后,向 S2 下發(fā)流規(guī)則,指示 S2 將該數(shù)據(jù)包轉(zhuǎn)發(fā)給終端機(jī) H1S2。之后終端機(jī) H2S2 再與H1S1通信,終端機(jī) H2S2 對H1S1發(fā)出的數(shù)據(jù)包都會發(fā)送給終端機(jī) H1S2,所以ARP攻擊完成。
圖9 攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP欺騙程序代碼
使用方案之后,4臺終端機(jī)都向控制器注冊信息,密鑰交換的過程是終端機(jī)向控制器發(fā)送自己的公鑰,同時控制器向終端機(jī)發(fā)送自己的公鑰,如圖11和圖12。此時終端機(jī)H1S2再發(fā)起ARP攻擊時,交換機(jī)S2因?yàn)闆]有流,將該數(shù)據(jù)包上報控制器,控制器比對終端機(jī)驗(yàn)證信息之后,發(fā)現(xiàn)映射不正確,于是向管理員報告ARP攻擊的錯誤。并且這個時候終端機(jī)H1S1與終端機(jī)H2S2為正常通信,具體攻擊結(jié)果如圖13所示。使用方案之后終端機(jī)H2S2與終端機(jī)H1S1通信結(jié)果如圖14所示。
圖11 終端機(jī)向控制器發(fā)送自己的公鑰Fig.11 The terminal sends its own public key to the controller
圖12 控制器向終端機(jī)發(fā)送自己的公鑰Fig.12 The controller sends its own public key to the terminal
圖13 攻擊者在網(wǎng)絡(luò)層或鏈路層的ARP攻擊結(jié)果
(2)攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊
攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊程序代碼如圖15所示。4臺終端機(jī)都向控制器注冊了信息,當(dāng)終端機(jī)H1S2開啟攻擊程序之后,向網(wǎng)絡(luò)中傳輸ARP欺騙包,S2在收到欺騙數(shù)據(jù)包后,由于找不到對應(yīng)的流表項(xiàng),于是封裝消息后,將消息數(shù)據(jù)包發(fā)送到控制器,控制器比對驗(yàn)證信息表之后,發(fā)現(xiàn)IP、MAC地址與輸入端口的映射與表項(xiàng)中的信息不同,于是向終端機(jī)H1S2發(fā)送驗(yàn)證信息,因?yàn)榻K端機(jī)H1S2無法回答正確的消息,控制器將向管理員上報系統(tǒng)發(fā)生了ARP攻擊,攻擊類型是攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊,攻擊結(jié)果如圖16所示。
圖14 使用方案之后終端機(jī)H2S2與終端機(jī)H1S1通信
圖15 攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊代碼
圖16 攻擊者同時利用網(wǎng)絡(luò)層和鏈路層的ARP攻擊結(jié)果
文章分析了現(xiàn)有網(wǎng)絡(luò)架構(gòu)存在的問題,概括了軟件定義網(wǎng)絡(luò)所具有的特點(diǎn),在分析了SDN架構(gòu)下ARP攻擊原理和形式之后,提出了解決SDN架構(gòu)下ARP攻擊的方案。但是本文所提方案驗(yàn)證中終端機(jī)驗(yàn)證信息表比較大,在控制器中占用的空間也比較大,而且在終端機(jī)下線之后也必須維持這樣的表項(xiàng),當(dāng)接入的終端機(jī)變多的時候控制器的存儲空間會變得緊缺,因此,在以后的方案改進(jìn)中可以考慮更小的表項(xiàng),并且能動態(tài)地存儲表項(xiàng)。還有驗(yàn)證IP地址與MAC地址映射時使用了DHCP服務(wù)器,而控制器與DHCP服務(wù)器之間的通信安全沒有保障,這是下一步的完善方向。