实验报告(1和2)

guaiguai000 / 2024-10-06 / 原文

学号:102201141 姓名:滕桂荣

一、实验目的

  1. 熟悉基础网络实验环境的搭建;
  2. 熟悉部分SDN控制器的使用;
  3. 掌握基于北向API的网络应用的基本开发方法;
  4. 理解基于动态异构冗余架构实现的网络内生安全实现机制

二、实验环境

  1. 基础网络环境基于ubuntu-20.04.6-desktop-amd64系统
  2. 应用开发工具包括但不限于python、java、vue、node

三、实验要求

任务1:Mininet网络构建

  1. 使用 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

  1. 熟悉OpenFlow协议,对照OpenFlow源码,了解OpenFlow主要消息类型对应的数据结构定义,回答以下问题:
  • 交换机与控制器建立通信时是使用TCP协议还是UDP协议?
  • OpenFlow控制器默认用哪个端口号和交换机通信?
  • Packet_IN消息由通信的哪一端发出,产生该消息的原因有哪些?
  • Flow_Mod和Packet_OUT的区别是什么?

任务2:POX、Ryu开源控制器

  1. 将任务1的网络拓扑连接POX控制器,OpenFlow可选版本为1.0,通过POX下发流表项实现主机连通性:(1) h1 - h4 互通;(2) h2 - h5 互通;(3) h3 - h6 互通;(4)其余主机不通。
  2. 将任务1的网络拓扑连接Ryu控制器,OpenFlow可选版本为1.0、1.3,甚至更多,通过RYU下发流表项实现主机连通性:(1) h1 - h4 互通;(2) h2 - h5 互通;(3) h3 - h6 互通;(4)其余主机不通。
  3. 【选做】跑通代码: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 地址的映射就会被替换为新的映射,交换机中的流规则被删除,数据包丢失。为了能够继续通信,主机必须再次解析域名。