學號:102201141 姓名:滕桂榮
一、實驗目的
- 熟悉基礎網路實驗環境的搭建;
- 熟悉部分SDN控制器的使用;
- 掌握基於北向API的網路應用的基本開發方法;
- 理解基於動態異構冗餘架構實現的網路內生安全實現機制
二、實驗環境
- 基礎網路環境基於ubuntu-20.04.6-desktop-amd64系統
- 應用開發工具包括但不限於python、java、vue、node
三、實驗要求
任務1:Mininet網路構建
- 使用 Mininet 生成下圖所示的拓撲,交換機為 Open vSwitch ,完成相關IP和埠號配置,透過
ovs-ofctl 命令在 s1 和 s2 上完成 VLAN 配置,實現主機連通性:(1) h1 - h4 互通;(2) h2 -
h5 互通;(3) h3 - h6 互通;(4)其餘主機不通。
主機名稱 | 主機地址 | 所屬VLAN ID |
---|---|---|
h1 | 192.168.0.101/24 | 0 |
h2 | 192.168.0.102/24 | 1 |
h3 | 192.168.0.103/24 | 2 |
h4 | 192.168.0.104/24 | 0 |
h5 | 192.168.0.105/24 | 1 |
h6 | 192.168.0.106/24 | 2 |
- 熟悉OpenFlow協議,對照OpenFlow原始碼,瞭解OpenFlow主要訊息型別對應的資料結構定義,回答以下問題:
- 交換機與控制器建立通訊時是使用TCP協議還是UDP協議?
- OpenFlow控制器預設用哪個埠號和交換機通訊?
- Packet_IN訊息由通訊的哪一端發出,產生該訊息的原因有哪些?
- Flow_Mod和Packet_OUT的區別是什麼?
任務2:POX、Ryu開源控制器
- 將任務1的網路拓撲連線POX控制器,OpenFlow可選版本為1.0,透過POX下發流表項實現主機連通性:(1) h1 - h4 互通;(2) h2 - h5 互通;(3) h3 - h6 互通;(4)其餘主機不通。
- 將任務1的網路拓撲連線Ryu控制器,OpenFlow可選版本為1.0、1.3,甚至更多,透過RYU下發流表項實現主機連通性:(1) h1 - h4 互通;(2) h2 - h5 互通;(3) h3 - h6 互通;(4)其餘主機不通。
- 【選做】跑通程式碼:Moving-Target-Defense-RHM-using-SDN,闡述專案實現了一個什麼功能,具體是如何實現的,你認為這樣一個機制能實現一個什麼樣的防禦?
四、實驗內容
任務1:Mininet網路構建
1. 展示構建拓撲所用到的Python程式碼和Mininet命令,以及VLAN配置後的主機連通性測試結果:
- Python程式碼 topo_ovsvlan.py
#!/usr/bin/python
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller, RemoteController, Host
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLink
class MyTopo(Topo):
"Simple topology example."
def build(self):
# Create switches
s1 = self.addSwitch('s1', dpid='00:00:00:00:00:00:00:01',
protocols='OpenFlow13')
s2 = self.addSwitch('s2', dpid='00:00:00:00:00:00:00:02',
protocols='OpenFlow13')
# Create hosts
h1 = self.addHost('h1', ip='192.168.0.101/24')
h2 = self.addHost('h2', ip='192.168.0.102/24')
h3 = self.addHost('h3', ip='192.168.0.103/24')
h4 = self.addHost('h4', ip='192.168.0.104/24')
h5 = self.addHost('h5', ip='192.168.0.105/24')
h6 = self.addHost('h6', ip='192.168.0.106/24')
# Add links with specific bandwidth and delay settings
linkopts0 = dict(bw=300, delay='1ms', loss=0)
linkopts1 = dict(bw=100, delay='1ms', loss=0)
self.addLink(h1, s1, cls=TCLink, **linkopts1)
self.addLink(h2, s1, cls=TCLink, **linkopts1)
self.addLink(h3, s1, cls=TCLink, **linkopts1)
self.addLink(h4, s2, cls=TCLink, **linkopts1)
self.addLink(h5, s2, cls=TCLink, **linkopts1)
self.addLink(h6, s2, cls=TCLink, **linkopts1)
self.addLink(s1, s2, cls=TCLink, **linkopts0)
def startMyNet():
"Create network and run CLI"
topo = MyTopo()
net = Mininet(topo=topo, switch=OVSSwitch, controller=None,
autoSetMacs=True, autoStaticArp=True)
c0 = net.addController('c0', controller=Controller)
net.start()
# Add OpenFlow rules to the switches
s1 = net.get('s1')
s2 = net.get('s2')
# Rules for s1
s1.cmd('ovs-ofctl -O OpenFlow13 del-flows s1')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=1,actions=push_vlan:0x8100,set_field:4096-\>vlan_vid,output:4')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=2,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:4')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,in_port=3,actions=push_vlan:0x8100,set_field:4098-\>vlan_vid,output:4')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=0,actions=pop_vlan,output:1')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=1,actions=pop_vlan,output:2')
s1.cmd('ovs-ofctl -O OpenFlow13 add-flow s1 priority=100,dl_vlan=2,actions=pop_vlan,output:3')
# Rules for s2
s2.cmd('ovs-ofctl -O OpenFlow13 del-flows s2')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=1,actions=push_vlan:0x8100,set_field:4096-\>vlan_vid,output:4')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=2,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:4')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,in_port=3,actions=push_vlan:0x8100,set_field:4098-\>vlan_vid,output:4')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=0,actions=pop_vlan,output:1')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=1,actions=pop_vlan,output:2')
s2.cmd('ovs-ofctl -O OpenFlow13 add-flow s2 priority=100,dl_vlan=2,actions=pop_vlan,output:3')
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel('info')
startMyNet()
執行topo_ovsvlan.py:sudo python3 topo_ovsvlan.py
- Mininet命令
mininet> sh ovs-ofctl dump-flows s1 -O OpenFlow13//檢視交換機s1流表項
mininet> sh ovs-ofctl dump-flows s2 -O OpenFlow13//檢視交換機s2流表項
mininet> pingall//進行連通性測試
- 主機連通性測試結果:
- 用wireshark在s1-eth4處抓包可以驗證vlan標籤的正確性:
2. 寫出四個問題的答案:
- 交換機與控制器建立通訊時是使用TCP協議還是UDP協議?
答:使用TCP協議,OpenFlow協議的控制器和交換機之間的通訊是穩定的、可靠的,所以採用TCP協議。 - OpenFlow控制器預設用哪個埠號和交換機通訊?
答:OpenFlow控制器預設使用6633埠號和交換機通訊。 - Packet_IN訊息由通訊的哪一端發出,產生該訊息的原因有哪些?
答:Packet_IN訊息由交換機發出。產生該訊息的原因主要有:- 資料包在交換機中匹配不到流表項,則向控制器傳送Packet_IN訊息 (OFPR_NO_MATCH)。
- 資料包在流表中有匹配的條目,但其中所指示的動作列表中包含轉發給控制器的動作 (OFPR_ACTION)。
- Flow_Mod和Packet_OUT的區別是什麼?
答:Flow_Mod訊息用於在交換機上新增、修改或刪除流表項。當控制器收到Packet_in訊息時,它可以傳送Flow_Mod訊息來指導交換機如何處理匹配的資料包。
Packet_Out訊息是直接告訴交換機如何處理特定的資料包,而不是下發流表項。控制器指定了資料包應該如何被處理,例如轉發到某個埠或傳送到控制器自身。
任務2:POX、Ryu開源控制器
1.POX關鍵程式碼、交換機下發的流表項以及連通性測試結果
- POX關鍵程式碼
poxcontroller.py 在pox目錄下執行命令./pox.py poxcontroller.py
from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.util import dpidToStr
log = core.getLogger()
def _handle_ConnectionUp(event):
dpid = event.connection.dpid
sw = dpidToStr(dpid)
log.info("Switch %s has connected.", sw)
if sw == '00-00-00-00-00-01': # s1
configure_switch(event.connection, is_s1=True)
elif sw == '00-00-00-00-00-02': # s2
configure_switch(event.connection, is_s1=False)
else:
log.warning("Unknown switch connected: %s", sw)
def configure_switch(conn, is_s1):
if is_s1:
host_pairs = [
("192.168.0.101", "192.168.0.104", 1),
("192.168.0.102", "192.168.0.105", 2),
("192.168.0.103", "192.168.0.106", 3)
]
else:
host_pairs = [
("192.168.0.104", "192.168.0.101", 1),
("192.168.0.105", "192.168.0.102", 2),
("192.168.0.106", "192.168.0.103", 3)
]
for src_ip, dst_ip, local_port in host_pairs:
# 配置從源 IP 到目標 IP 的流表項
msg = of.ofp_flow_mod()
msg.priority = 1000
msg.match.dl_type = 0x0800 # IPv4
msg.match.nw_src = src_ip
msg.match.nw_dst = dst_ip
msg.actions.append(of.ofp_action_output(port=4)) # 這裡的埠號需要與實際情
況匹配
conn.send(msg)
log.info("Flow mod sent: src_ip=%s, dst_ip=%s, action_port=4", src_ip,
dst_ip)
# 配置從目標 IP 到源 IP 的流表項
msg = of.ofp_flow_mod()
msg.priority = 1000
msg.match.dl_type = 0x0800 # IPv4
msg.match.nw_src = dst_ip
msg.match.nw_dst = src_ip
msg.actions.append(of.ofp_action_output(port=local_port)) # 這裡的埠號需
要與實際情況匹配
conn.send(msg)
log.info("Flow mod sent: src_ip=%s, dst_ip=%s, action_port=%d", dst_ip,
src_ip, local_port)
log.info("Configured IP-based flows for switch %s", "s1" if is_s1 else "s2")
def launch():
core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp)
log.info("POX IP-based communication module running.")
topo_of10.py 執行命令啟動Mininetsudo python3 topo_of10.py
#!/usr/bin/python
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller, RemoteController, Host
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLink
class MyTopo(Topo):
"Simple topology example."
def build(self):
# Create switches
s1 = self.addSwitch('s1', dpid='00:00:00:00:00:00:00:01',protocols='OpenFlow10')
s2 = self.addSwitch('s2', dpid='00:00:00:00:00:00:00:02',protocols='OpenFlow10')
# Create hosts
h1 = self.addHost('h1', ip='192.168.0.101/24')
h2 = self.addHost('h2', ip='192.168.0.102/24')
h3 = self.addHost('h3', ip='192.168.0.103/24')
h4 = self.addHost('h4', ip='192.168.0.104/24')
h5 = self.addHost('h5', ip='192.168.0.105/24')
h6 = self.addHost('h6', ip='192.168.0.106/24')
# Add links with specific bandwidth and delay settings
linkopts0 = dict(bw=300, delay='1ms', loss=0)
linkopts1 = dict(bw=100, delay='1ms', loss=0)
self.addLink(h1, s1, cls=TCLink, **linkopts1)
self.addLink(h2, s1, cls=TCLink, **linkopts1)
self.addLink(h3, s1, cls=TCLink, **linkopts1)
self.addLink(h4, s2, cls=TCLink, **linkopts1)
self.addLink(h5, s2, cls=TCLink, **linkopts1)
self.addLink(h6, s2, cls=TCLink, **linkopts1)
self.addLink(s1, s2, cls=TCLink, **linkopts0)
def startMyNet():
"Create network and run CLI"
topo = MyTopo()
net = Mininet(topo=topo, switch=OVSSwitch, controller=None,autoSetMacs=True, autoStaticArp=True)
# Add a remote controller
c0 = net.addController('c0', controller=RemoteController, ip='127.0.0.1',port=6633)
net.start()
# Add OpenFlow rules to the switches and any other configurations needed
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel('info')
startMyNet()
- 交換機下發的流表項:
- 連通性測試結果:
2. RYU關鍵原始碼、交換機下發的流表項以及連通性測試結果
- RYU關鍵原始碼
ryucontroller.py 啟動ryu命令ryu-manager ryucontroller.py
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls
from ryu.ofproto import ofproto_v1_0
from ryu.lib.packet import packet, ethernet, ipv4
class CustomSwitch(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(CustomSwitch, self).__init__(*args, **kwargs)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
dpid = datapath.id
self.logger.info("Packet in DPID: %s", dpid) # 列印 DPID
pkt = packet.Packet(msg.data)
eth = pkt.get_protocol(ethernet.ethernet)
ip_pkt = pkt.get_protocol(ipv4.ipv4)
if ip_pkt is None:
return
src_ip = ip_pkt.src
dst_ip = ip_pkt.dst
self.logger.info("Received packet: SRC IP: %s, DST IP: %s", src_ip,dst_ip)
# 根據源 IP 和目的 IP 來決定流表規則
if dpid == 1: # Switch 1
if src_ip == '192.168.0.101' and dst_ip == '192.168.0.104': # h1 ->h4
out_port = 4
elif src_ip == '192.168.0.104' and dst_ip == '192.168.0.101': # h4-> h1
out_port = 1
elif src_ip == '192.168.0.102' and dst_ip == '192.168.0.105': # h2-> h5
out_port = 4
elif src_ip == '192.168.0.105' and dst_ip == '192.168.0.102': # h5-> h2
out_port = 2
elif src_ip == '192.168.0.103' and dst_ip == '192.168.0.106': # h3-> h6
out_port = 4
elif src_ip == '192.168.0.106' and dst_ip == '192.168.0.103': # h6-> h3
out_port = 3
else:
return
elif dpid == 2: # Switch 2
if src_ip == '192.168.0.101' and dst_ip == '192.168.0.104': # h1 ->h4
out_port = 1
elif src_ip == '192.168.0.104' and dst_ip == '192.168.0.101': # h4-> h1
out_port = 4
elif src_ip == '192.168.0.102' and dst_ip == '192.168.0.105': # h2-> h5
out_port = 2
elif src_ip == '192.168.0.105' and dst_ip == '192.168.0.102': # h5-> h2
out_port = 4
elif src_ip == '192.168.0.103' and dst_ip == '192.168.0.106': # h3-> h6
out_port = 3
elif src_ip == '192.168.0.106' and dst_ip == '192.168.0.103': # h6-> h3
out_port = 4
else:
return
else:
return
self.logger.info("Installing flow: DPID %s, SRC IP %s -> DST IP %s, OUTPORT %s", dpid, src_ip, dst_ip, out_port)
actions = [parser.OFPActionOutput(out_port)]
# 安裝流表規則
match = parser.OFPMatch(dl_type=eth.ethertype, nw_src=src_ip,nw_dst=dst_ip)
mod = parser.OFPFlowMod(datapath=datapath, priority=1, match=match, actions=actions)
datapath.send_msg(mod)
# 立即轉發資料包
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,in_port=msg.in_port, actions=actions, data=msg.data)
datapath.send_msg(out)
topo_of10.py Mininet指令碼與任務2的Pox的Mininet指令碼相同。
執行命令啟動Mininetsudo python3 topo_of10.py
- 啟動ryu並在Mininet中pingall
- 交換機下發的流表項
- 連通性測試結果
3. 【選做】跑通程式碼:Moving-Target-Defense-RHM-using-SDN,闡述專案實現了一個什麼功能,具體是如何實現的,你認為這樣一個機制能實現一個什麼樣的防禦?
- 跑通程式碼如下圖:
- 功能與實現
這個專案主要實現了移動目標防禦的機制,以防止來自內部和外部網路的 IP 掃描。- 功能
透過動態地址分配來防止 IP 掃描。在這個專案中,每個網路主機都有兩個地址,一個是真實地址,另一個是虛擬地址。真實 IP 地址之間的通訊被阻止,僅允許在虛擬 IP 地址上進行通訊。因此,即使攻擊者獲得了目標資訊,目標在一段超時後也會改變,實現網路資訊對外部和內部攻擊者隱藏。 - 實現
使用控制器提供的北向 API 開發了一種新的路由機制,採用多執行緒、裝飾器、事件建立和事件處理技術,使用Ryu 作為 SDN 控制器,Open vSwitch 作為資料平面裝置,實現了基於隨機主機變換技術控制資料包流動。
當一臺主機想要向另一臺主機傳送 ping 時,它首先會嘗試解析域名,而該請求會被控制器攔截,控制器充當代理伺服器,偽造 DNS 響應,返回虛擬地址。此外,SDN 控制器持有一組真實 IP 地址與虛擬 IP 地址的對映,這些對映會定期超時,但是終端主機不會對傳送到虛擬 IP 地址的包作出響應。
SDN 控制器在入口處攔截所有資料包,並將真實源 IP 地址替換為其虛擬地址;在出口處,將虛擬目標 IP 地址替換為其真實地址。控制器還負責使用 OpenFlow 協議安裝流規則,以在未來的情況下處理資料平面。經過一段時間以後,真實 IP 地址與虛擬 IP 地址的對映就會被替換為新的對映,交換機中的流規則被刪除,資料包丟失。為了能夠繼續通訊,主機必須再次解析域名。
- 功能