0
点赞
收藏
分享

微信扫一扫

多路icmp/tcp转发实验

小北的爹 2022-01-31 阅读 24

之前在cloudlab上做的都是单switch转发实验,这次试了多switch转发实验,并且在ping通(icmp转发)的基础上,增加了tcp转发,实验拓扑如上图

controller代码如下:

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import arp
from ryu.lib.packet import ipv4
from ryu.lib.packet import icmp
from ryu.lib.packet import tcp
import random

class ExampleSwitch13(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
 
    def __init__(self, *args, **kwargs):
        super(ExampleSwitch13, self).__init__(*args, **kwargs)
        self.mac = {"h1e1":"00:04:23:b7:42:1f","h2e1":"00:04:23:b7:1d:f4","s1v1":"ae:0b:ab:2e:20:7f","s1v2":"0e:62:a1:df:6e:ad","s1v3":"ea:5c:c0:1e:3c:14","s1v4":"52:11:e8:91:5e:c5","s2v1":"a2:84:49:d3:fc:b5","s2v2":"de:34:a6:a1:a6:b6","s2v3":"ee:99:01:e0:e6:9a","s2v4":"76:7c:8b:c1:d2:3f","s3v1":"56:53:78:96:c6:d8","s3v2":"a2:0e:5f:be:57:f9","s4v1":"1e:95:c8:d1:92:74","s4v2":"7a:4b:5c:e6:11:9b","s5v1":"b6:2e:89:cb:48:5c","s5v2":"8e:00:b1:56:0f:99"}
        self.ip = {"h1e1":"10.10.1.1","h2e1":"10.10.8.2","s1v1":"10.10.3.1","s1v2":"10.10.4.1","s1v3":"10.10.1.2","s1v4":"10.10.2.1","s2v1":"10.10.7.1","s2v2":"10.10.8.1","s2v3":"10.10.5.1","s2v4":"10.10.6.1","s3v1":"10.10.5.2","s3v2":"10.10.2.2","s4v1":"10.10.6.2","s4v2":"10.10.3.2","s5v1":"10.10.4.2","s5v2":"10.10.7.2"}
        self.v2e = {"s1v1":5,"s1v2":6,"s1v3":7,"s1v4":8,"s2v1":5,"s2v2":6,"s2v3":7,"s2v4":8,"s3v1":3,"s3v2":4,"s4v1":3,"s4v2":4,"s5v1":3,"s5v2":4}
        self.s2s = {"s1":{"h1":"s1v3","s3":"s1v4","s4":"s1v1","s5":"s1v2"},"s2":{"h2":"s2v2","s3":"s2v3","s4":"s2v4","s5":"s2v1"},"s3":{"s1":"s3v2","s2":"s3v1"},"s4":{"s1":"s4v2","s2":"s4v1"},"s5":{"s1":"s5v1","s2":"s5v2"}}
    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        mac, ip = self.mac, self.ip
        v2e, s2s = self.v2e, self.s2s
        datapath = ev.msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
 
        # install the table-miss flow entry.
        match = parser.OFPMatch()
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)]
        self.add_flow(datapath, 0, match, actions)
        dpid = datapath.id
        print("******setting features for s" + str(dpid) + "******")
        self.send_set_config(datapath)
        if dpid == 1:
            #icmp:
            #h1->h2
            inport, outport, toport = s2s["s1"]["h1"], s2s["s1"]["s3"], s2s["s3"]["s1"]
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            inport, outport, toport = s2s["s1"]["s4"], s2s["s1"]["h1"], "h1e1"
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])
            #h2->h1
            #tcp:
            #h2->h1:
            inport, outport, toport = s2s["s1"]["s3"], s2s["s1"]["h1"], "h1e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])
            inport, outport, toport = s2s["s1"]["s4"], s2s["s1"]["h1"], "h1e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])
            inport, outport, toport = s2s["s1"]["s5"], s2s["s1"]["h1"], "h1e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])

        elif dpid == 2:
            #icmp:
            #h1->h2
            inport, outport, toport = s2s["s2"]["s3"], s2s["s2"]["h2"], "h2e1"
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            #h2->h1
            inport, outport, toport = s2s["s2"]["h2"], s2s["s2"]["s4"], s2s["s4"]["s2"]
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])
            #tcp:
            #h1->h2:
            inport, outport, toport = s2s["s2"]["s3"], s2s["s2"]["h2"], "h2e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            inport, outport, toport = s2s["s2"]["s4"], s2s["s2"]["h2"], "h2e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            inport, outport, toport = s2s["s2"]["s5"], s2s["s2"]["h2"], "h2e1"
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])

        elif dpid == 3:
            #icmp:
            #h1->h2
            inport, outport, toport = s2s["s3"]["s1"], s2s["s3"]["s2"], s2s["s2"]["s3"]
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            #tcp:
            #h1->h2
            inport, outport, toport = s2s["s3"]["s1"], s2s["s3"]["s2"], s2s["s2"]["s3"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            #h2->h1
            inport, outport, toport = s2s["s3"]["s2"], s2s["s3"]["s1"], s2s["s1"]["s3"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])

        elif dpid == 4:
            #icmp:
            #h2->h1
            inport, outport, toport = s2s["s4"]["s2"], s2s["s4"]["s1"], s2s["s1"]["s4"]
            self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])
            #tcp:
            #h1->h2
            inport, outport, toport = s2s["s4"]["s1"], s2s["s4"]["s2"], s2s["s2"]["s4"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            #h2->h1
            inport, outport, toport = s2s["s4"]["s2"], s2s["s4"]["s1"], s2s["s1"]["s4"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])

        elif dpid == 5:
            #icmp:
            #tcp:
            #h1->h2
            inport, outport, toport = s2s["s5"]["s1"], s2s["s5"]["s2"], s2s["s2"]["s5"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h1e1"],ip["h2e1"], mac[outport], mac[toport])
            #h2->h1
            inport, outport, toport = s2s["s5"]["s2"], s2s["s5"]["s1"], s2s["s1"]["s5"]
            self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["h2e1"],ip["h1e1"], mac[outport], mac[toport])

 
    #下发流表逻辑
    def add_flow(self, datapath, priority, match, actions):
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
 
        # construct flow_mod message and send it.
        inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
        mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst)
        datapath.send_msg(mod)
        	
    #对icmp单独的转发规则:h1-s1-s3-s4-h2/h2-s4-s3-s1-h1
    def add_icmp_rules(self, datapath, in_port, out_port, ips, ipd, macs, macd):
        priority = 20
        parser = datapath.ofproto_parser
        actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd), parser.OFPActionOutput(out_port)]
        match = parser.OFPMatch(eth_type =ether.ETH_TYPE_IP,ip_proto=inet.IPPROTO_ICMP, in_port= in_port, ipv4_src=ips, ipv4_dst=ipd)
        self.add_flow(datapath, priority, match, actions)

    def add_tcp_rules(self, datapath, in_port, out_port, ips, ipd, macs, macd):
        priority = 20
        parser = datapath.ofproto_parser
        actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd), parser.OFPActionOutput(out_port)]
        match = parser.OFPMatch(eth_type =ether.ETH_TYPE_IP,ip_proto=inet.IPPROTO_TCP, in_port= in_port, ipv4_src=ips, ipv4_dst=ipd)
        self.add_flow(datapath, priority, match, actions)

 
 
    def send_set_config(self, datapath):
        #把Normal先安装给datapath
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        req = ofp_parser.OFPSetConfig(datapath, ofp.OFPC_FRAG_NORMAL, 256)
        datapath.send_msg(req)
 
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _pakcet_in_handler(self, ev):
        #如果发生了table-miss,就把对应的default forwarding安装上
        #如果miss的是icmp,就把对应的icmp forwarding安装上
        datapath = ev.msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        dpid = datapath.id
        msg = ev.msg
        print("****** receiving a packet in from " + str(dpid) + " ******")
        pkt = packet.Packet(msg.data)
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        in_port = msg.match["in_port"]
        ethertype = eth_pkt.ethertype
        if not eth_pkt:
            return
        pkt_arp = pkt.get_protocol(arp.arp)

        if ethertype == ether.ETH_TYPE_ARP:
            self.handle_arp(datapath, in_port, pkt)
            return
        if ethertype == ether.ETH_TYPE_IP:
            self.handle_ip(datapath, in_port, pkt)
            return

    def handle_arp(self, datapath, in_port, pkt):
        print("*************** handling arp packet in  from "+str(datapath.id)+" ****************")
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        # parse out the ethernet and arp packet
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        arp_pkt = pkt.get_protocol(arp.arp)
        # obtain the MAC of dst IP  
        dpid = datapath.id

        if dpid == 1:#sw1上的arp请求            
            arp_resolv_mac = self.mac["s1v1"]
        elif dpid == 2:#sw2上的arp请求
            arp_resolv_mac = self.mac["s2v4"]
        else:
            print("*** require handling arp on switch: dpid="+str(dpid)+" *****")
            return
        ### generate the ARP reply msg, please refer RYU documentation
        ### the packet library section
    # ARP Reply Msg
        ether_hd = ethernet.ethernet(dst = eth_pkt.src, 
                                src = arp_resolv_mac, 
                                ethertype = ether.ETH_TYPE_ARP);
        arp_hd = arp.arp(hwtype=1, proto = 2048, hlen = 6, plen = 4,
                         opcode = 2, src_mac = arp_resolv_mac, 
                         src_ip = arp_pkt.dst_ip, dst_mac = eth_pkt.src,
                         dst_ip = arp_pkt.src_ip);
        arp_reply = packet.Packet()
        arp_reply.add_protocol(ether_hd)
        arp_reply.add_protocol(arp_hd)
        arp_reply.serialize()
        
    # send the Packet Out mst to back to the host who is initilaizing the ARP
        actions = [parser.OFPActionOutput(in_port)];
        out = parser.OFPPacketOut(datapath, ofproto.OFP_NO_BUFFER, 
                                  ofproto.OFPP_CONTROLLER, actions,
                                  arp_reply.data)
        print("************** sending arp packet out **************")
        datapath.send_msg(out)

    def handle_ip(self, datapath, in_port, pkt):
        ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
        if ipv4_pkt.proto == inet.IPPROTO_ICMP:
            self.handle_icmp(datapath, in_port, pkt)
        elif ipv4_pkt.proto == inet.IPPROTO_TCP:
            self.handle_tcp(datapath, in_port, pkt)

    def handle_tcp(self, datapath, in_port, pkt):
        print("************* handling tcp packet in ****************")
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        mac = self.mac
        v2e, s2s = self.v2e, self.s2s
        ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
        src_ip = ipv4_pkt.src
        dst_ip = ipv4_pkt.dst
        tcp_pkt = pkt.get_protocol(tcp.tcp)
        src_port, dst_port = tcp_pkt.src_port, tcp_pkt.dst_port
        rand = random.randint(0, 2)
        inp = in_port
        if datapath.id == 1:
            if inp != v2e[s2s["s1"]["h1"]]:
                macs = mac[s2s["s1"]["h1"]]
                macd = mac["h1e1"]
                oup = v2e[s2s["s1"]["h1"]]
            elif rand == 0:
                macs=mac[s2s["s1"]["s3"]]
                macd=mac[s2s["s3"]["s1"]]
                oup = v2e[s2s["s1"]["s3"]]
            elif rand == 1:
                macs=mac[s2s["s1"]["s4"]]
                macd=mac[s2s["s4"]["s1"]]
                oup = v2e[s2s["s1"]["s4"]]
            else:
                macs=mac[s2s["s1"]["s5"]]
                macd=mac[s2s["s5"]["s1"]]
                oup = v2e[s2s["s1"]["s5"]]

        elif datapath.id == 2:
            if inp != v2e[s2s["s2"]["h2"]]:
                macs = mac[s2s["s2"]["h2"]]
                macd = mac["h2e1"]
                oup = v2e[s2s["s2"]["h2"]]
            elif rand == 0:
                macs=mac[s2s["s2"]["s3"]]
                macd=mac[s2s["s3"]["s2"]]
                oup = v2e[s2s["s2"]["s3"]]
            elif rand == 1:
                macs=mac[s2s["s2"]["s4"]]
                macd=mac[s2s["s4"]["s2"]]
                oup = v2e[s2s["s2"]["s4"]]
            else:
                macs=mac[s2s["s2"]["s5"]]
                macd=mac[s2s["s5"]["s2"]]
                oup = v2e[s2s["s2"]["s5"]]
        else:
            return

        match = parser.OFPMatch(eth_type = ether.ETH_TYPE_IP,
                                    ipv4_src = src_ip,
                                    ipv4_dst = dst_ip,
                                    tcp_dst = dst_port,
                                    tcp_src = src_port,
                                    ip_proto = inet.IPPROTO_TCP)

        actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd),parser.OFPActionOutput(oup)]
        self.add_flow(datapath, 50, match, actions)



    def handle_icmp(self, datapath, in_port, pkt):
        print("************* handling icmp packet in ****************")
        #对于任意不是h1/h2互相传的icmp,这里的处理是switch一旦拿到这样的match不到的icmp packet,controller直接给一个回复(有时间想想是不是有更好的方法)
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
        src_ip, dst_ip = ipv4_pkt.src, ipv4_pkt.dst
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        icmp_pkt = pkt.get_protocol(icmp.icmp)
        #ethernet源和目的互换
        ether_hd = ethernet.ethernet(dst = eth_pkt.src, src = eth_pkt.dst, ethertype = ether.ETH_TYPE_IP)
        ipv4_hd = ipv4.ipv4(proto = 1, src = dst_ip, dst = src_ip)
        #icmp源和目的互换
        icmp_hd = icmp.icmp(type_=icmp.ICMP_ECHO_REPLY,code=icmp.ICMP_ECHO_REPLY_CODE,csum=0,data=icmp_pkt.data)

        icmp_reply = packet.Packet()
        icmp_reply.add_protocol(ether_hd)
        icmp_reply.add_protocol(ipv4_hd)
        icmp_reply.add_protocol(icmp_hd)
        icmp_reply.serialize()

        #从inport把包发出去
        actions = [parser.OFPActionOutput(in_port)];
        out = parser.OFPPacketOut(datapath, ofproto.OFP_NO_BUFFER, ofproto.OFPP_CONTROLLER, actions, icmp_reply.data)
        print("************* sending icmp packet out ***************")
        datapath.send_msg(out)
 

 需要注意:

(1)像上图那样设置了5个ovs后,如果不通过手动或者controller的方式添加流表(表项只有normal),因为topo中有环路,会发生icmp广播风暴。设置流表,包的转发规则,可以避免风暴

(2)可以登陆每个switch设置dpid,使用的命令如下:

ovs-vsctl set bridge br1 other-config:datapath-id=0000000000000001
ovs-vsctl set bridge br2 other-config:datapath-id=0000000000000002
ovs-vsctl set bridge br3 other-config:datapath-id=0000000000000003
ovs-vsctl set bridge br4 other-config:datapath-id=0000000000000004
ovs-vsctl set bridge br5 other-config:datapath-id=0000000000000005
ovs-ofctl show br2

设置controller的命令如下:

ovs-vsctl set-controller br1 tcp:155.98.38.235:6633
ovs-vsctl show

(3)上面的代码可以实现h1-h2之间的icmp/tcp转发,但功能不完善,switch之间的包的转发规则没有设置

(4)sw1和sw2是edge switch,本实验主要是靠tcp table-miss,第一个table-miss的packet被传到controller,controller通过随机数下发不同路径的流表项,来实现每次tcp流从不同的路线(sw3/sw4/sw5)来转发,所以tcp在edge switch上的转发规则是通过handle-tcp函数实现的,而不是在set-switches时实现的

(5)sw3/sw4/sw5是core switch,它们上面的tcp转发规则是在set-features阶段实现的

(6)icmp h1->h2通过sw3转发,h2->h1通过sw4转发

(7)cloudlab上的节点时间久了会自动关机

(8)做实验时用到的ep-vp对应表格,做了表格之后会比较方便写下发流表规则的逻辑

S1

ip

eport

emac

vport

vmac

num

toSwitch

10.10.3.1

enp10s3f0

00:04:23:b7:1a:b8

vp1

ae:0b:ab:2e:20:7f

5

s4

10.10.4.1

enp10s3f1

00:04:23:b7:1a:b9

vp2

0e:62:a1:df:6e:ad

6

s5

10.10.1.2

enp9s4f0

00:04:23:b7:20:46

vp3

ea:5c:c0:1e:3c:14

7

h1

10.10.2.1

enp9s4f1

00:04:23:b7:20:47

vp4

52:11:e8:91:5e:c5

8

s3

S2

ip

eport

emac

vport

vmac

num

toSwitch

10.10.7.1

enp10s3f0

00:04:23:b7:14:88

vp1

a2:84:49:d3:fc:b5

5

s5

10.10.8.1

enp10s3f1

00:04:23:b7:14:89

vp2

de:34:a6:a1:a6:b6

6

h2

10.10.5.1

enp9s4f0

00:04:23:b7:17:6c

vp3

ee:99:01:e0:e6:9a

7

s3

10.10.6.1

enp9s4f1

00:04:23:b7:17:6d

vp4

76:7c:8b:c1:d2:3f

8

s4

S3

ip

eport

emac

vport

vmac

num

toSwitch

10.10.5.2

enp10s3f0

00:04:23:b7:1e:26

vp1

56:53:78:96:c6:d8

3

s2

10.10.2.2

enp9s4f0

00:04:23:b7:1d:44

vp2

a2:0e:5f:be:57:f9

4

s1

S4

ip

eport

emac

vport

vmac

num

toSwitch

10.10.6.2

enp10s3f0

00:04:23:b7:12:ba

vp1

1e:95:c8:d1:92:74

3

s2

10.10.3.2

enp9s4f0

00:04:23:b7:13:02

vp2

7a:4b:5c:e6:11:9b

4

s1

S5

ip

eport

emac

vport

vmac

num

toSwitch

10.10.4.2

enp9s4f0

00:04:23:b7:23:a0

vp1

b6:2e:89:cb:48:5c

3

s1

10.10.7.2

enp9s4f1

00:04:23:b7:23:a1

vp2

8e:00:b1:56:0f:99

4

s2

h1

eport

emac

ip

enp10s3f1

00:04:23:b7:42:1f

10.10.1.1

h2

eport

emac

ip

enp9s4f0

00:04:23:b7:1d:f4

10.10.8.2

(9)一开始没ping通有可能是节点挂了,也有可能是逻辑有地方没写对,多试几次

(10)没了

举报

相关推荐

0 条评论