過去要攻擊某服務的方法很簡單,對其地址傳送大量垃圾封包就可以消耗掉攻擊對象的運算資源、頻寬,不過很快的防火牆開始有了機制,能在偵測到來自某個位址的突然大量垃圾流量時就封鎖來自該位址的所有封包,後來派生出了DDoS,分散式阻斷服務攻擊。攻擊者在攻擊主要目標前會先透過社交網路之類的手段侵入其他防禦脆弱的host,在他們的主機中埋下malisious file,之後該主機就成為殭屍電腦。將被compromise的殭屍電腦們集中統一起來,同時對主要目標進行ICMP洪水攻擊,將導致前述防禦辦法失效,因此名為分散式阻斷服務攻擊。除了ICMP攻擊方法外,也有利用TCP的三向交握的漏洞來進行的方式,因交握的過程中server也會等待ACK,因此若設計程式來發送大量的SYN然後不回應來自server的SYN/ACK,也可達成同樣的效果。
Berkeley Packet Filter,是一種能在kernel space裡執行由user space定義的程式的技術。為了實現穩定與安全性,作業系統在核心的功能很難發生太大的改變,畢竟kernel擁有控制整個系統的權限,牽一髮而動全身,而透過eBPF,我們可以在將user space的程式run在作業系統,並透過eBPF map來讓kernel發生的事也可以被user space的application捕捉到,具體方法為撰寫C語言後由BPF編譯為byte code後丟下作業系統來verify與執行,並通過map將統計資料回傳給user space,供可視性的分析。
透過eBPF的技術,在linux kernel中透過使用者定義的規則來提早判斷packets的verdict。為了縮短從判斷到執行的時間,XDP hook在NIC的driver上,在Network stack被複製到記憶體前就已判斷完畢,因為複製到記憶體是非常expensive的行為,也提供了hacker能攻擊成功的間隙。透過在kernel space的分析,XDP可以根據eBPF的定義採取幾種行為:
此外XDP也支援將eBPF的program run在NIC card的controller,可減輕CPU的負擔。
透過eBPF based XDP來將ICMP DDoS mitigation code編譯為BPF code並run在NIC driver上,並透過BPF map將資料傳回user space的process,並將現在執行的情況以real time的形式show出來。當遭受ICMP DDoS攻擊時,對所有source ip紀錄pps(packets-per-second)的數值,因為ICMP為瞬間的暴漲流量,因此偵測到異常pps值時便將該source ip封鎖。
要達成上述的行為,我們需要對封包進行packet parsing,將各個header依序解析來獲取我們需要的資訊。先從ethernet header獲取mac address,再移到network header來獲取IP等的資訊,並通過此層標明的transport protocol來決定transport header的處理方式,如TCP、UDP、ICMP。因為packet的header資料結構都可以在linux的kernel source code裡找到以C撰寫的data struture,因此獲取這些header資訊只是以簡單的pointer就可以取得,另外接下來的header 如application就不用再分析下去,因為那些是由user space定義的,kernel並沒有能力去處理及解析。
在ubuntu 18.04 desktop上重現一次實驗。
$ git clone https://github.com/johnnylord/xdp-icmp-ddos-mitigation.git
將source code載下來
$ sudo apt install clang llvm libelf-dev libpcap-dev gcc-multilib build-essential
$ sudo apt install linux-tools-$(uname -r)
$ sudo apt install linux-headers-$(uname -r)
$ sudo apt install linux-tools-common linux-tools-generic
安裝需要的dependencies
$ sudo testenv/testenv.sh setup --name dos --legacy-ip
透過xdp-tutorial提供的setup script將網路環境建立好
$ make
將source code編譯為BPF bytecode
$ sudo ip link
現在我們觀察是否有XDP程式註冊在NIC上,根據下圖可以發現並沒有
$ sudo testenv/testenv.sh load
將bytecode load進kernel後再check一次,發現多了一個XDP program註冊在dos這張網卡上
$ sudo testenv/testenv.sh stats
開啟視覺化監控程式,監控dos這張卡的網路狀態
$ sudo ip netns exec dos /bin/bash
透過另一個terminal進入剛剛建立的virtual namespace裡,下ipconfig觀察
# ping 10.11.1.1
在該vm以正常的頻率先來ping host,
# ping -i 0.2 10.11.1.1
而若加快封包的傳送頻率,可以發現PPS值也會跟著變化。
$ sudo ip netns exec dos /bin/bash
# hping3 -q -n -d 200 --icmp --flood 10.11.1.1
再new一個新的terminal來模擬DDoS攻擊,使用的同樣為10.11.1.2,並對host進行ICMP洪水攻擊。
DDos的難解之處在於其是利用殭屍網路造成平行的大量流量,導致server端很難去區分哪些來自殭屍網路、哪些來自正常用戶。此次實驗只模擬了1對1的攻擊,未來想試試模擬出多對1的攻擊下,XDP的效果表現(by mininet? IP namespace?)。
原本採用ubuntu 20.04 server,kernel版本5.4.0-100-generic,與文中的5.4.0-94-generic基本上十分接近,但將XDP load進kernel時仍出現了問題:
libbpf: Error loading BTF: Invalid argument(22)
不知道問題出在哪邊,未來也是值得研究的主題(還是就只是單純版本對不上?)。
而同步傳輸模式,指的是TDMA(Time division multiple access)分時多工的情況下,雙方溝通時的虛擬通道走的time slot固定,也就是如下圖:
透過MPLS搭建企業的VPN已經比以往方便許多了,但大部分情況下仍然需要一條專用線路從企業拉出至最近的ISP,且企業端(Customer Edge, CE)與(Provider Edge, PE)若想增加site便要人工configure所有設備,這還是算較為不方便的點。
若能透過SDN的方式來達成的話,理想情境是透過central的controller來實現無接觸的configure方式,加入bare metal machine後將其連接上controller就好了。
支持SDN的測試環境,可在虛擬化的場景搭建虛擬switch、router及host,且Mininet對於網路行為的code與linux kernel的原始碼是一樣的,不像cisco packet tracer的封包是用軟體模擬出來的。
$ git clone git://github.com/mininet/mininet
$ mininet/util/install.sh -a
安裝mininet。
這個時候可以透過
$ mn
這個指令來觀察現在的預設網路拓樸。
root@mininet:~# mn
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...
*** Starting CLI:
可以看到預設給了兩個host:h1以及h2,一個switch s1以及controller c0。而switch已經幫我們開好機了。
我們可以輸入
mininet> nodes
來觀察現在有哪些節點。
而使用
mininet> net
則可以讓我們看到node間的連接情況。
輸入
mininet> dump
可以讓我們看到關於節點的詳細資訊。
我們可以試試用
mininet> h1 ping -c 1 h2
來試試用h1 ping h2一個封包。
建立一個3個host、1個switch搭配上一個controller的拓樸,如下圖。
$ sudo mn --topo single,3 --mac --switch ovsk --controller remote
mininet> net
觀察連接的情況
這裡有一個非常有用的指令
mininet> pingall
可以讓所有host互ping。
$ sudo mn --topo single,3 --mac --switch ovsk
mininet> pingall
這個時候我們可以重置flow table
mininet> sh ovs-ofctl del-flows s1
然後讓h1多ping h2幾個封包
mininet> h1 ping -c 6 h2
我們可以看,第一個封包有著明顯的延遲,因為這時候的flow table是空的,packet需要先送給controller做解讀並插入相關的flow rule,因此後續幾個封包的延遲都少上許多。
接著把ovs的flow table dump下來(這裡不知道為什麼只有在機器視窗才能看到dump下來的table而ssh介面看不到)
前三個是關於arp的flow(為什麼會有三個?),後兩個為h1與h2相互ICMP的flow,關於flow的機制與原理預計下次開始研究,這裡先放上參數代表的意義。
這次遇到很多問題,有關mininet的remote controller設不過,以及對flow單個entry並不是很了解等等,還有幾個實驗:
mininet安裝時就幫我們也順便安裝好pox了,因此我們只要在pox資料夾
$ ./pox.py forwarding.l2_learning
先將pox controller開起來,接著我們就可以建立mininet拓墣:
mn --topo single,3 --mac --switch ovsk --controller remote
這裡我們不用預設的controller,而是改用run在localhost的pox controller。
可以看到他走的port是6633且已連接成功。
接著使用ping all試試,一切連接正常。
接下來試試看使用pos controller做簡易的防火牆,在./pox/pox/forwarding裡面找到我們先前執行的l2_learning程式。
$ cp l2_learning.py l2_testfirewall.py
copy一份讓我們來做修改。
在最上面加上:
from pox.lib.packet.ipv4 import ipv4
from pox.lib.addresses import IPAddr, EthAddr
來import我們需要的套件,接著在*def_handle_PacketIn(self, event)*裡加上簡單的判斷式:
if isinstance(packet.next, ipv4):
if str(packet.next.srcip) == IPAddr('10.0.0.1'):
actions = []
msg = of.ofp_flow_mod(
command=of.OFPFC_ADD,
idle_timeout=20,
hard_timeout=of.OFP_FLOW_PERMANENT,
buffer_id=None,
actions=actions,
match=of.ofp_match(dl_src = packet.src,)
)
event.connection.send(msg.pack())
return
其中:
接著把他run起來
啟動mininet拓樸
先試試用h2 ping h3
成功,接下來換用h2 ping h1:
發現ping不到了,接著我們檢查ovs的flow table:
可以看到h2向h1的arp與icmp都正常通過,但h1回傳給h2的packet卻被drop了。