之前在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)没了
|