我使用 Ryu 控制器,我的 mininet 主机可以与互联网通信,这是我使用的脚本:
#!/usr/bin/python
from mininet.net import Mininet
from mininet.node import Controller, RemoteController, OVSSwitch
from mininet.cli import CLI
from mininet.link import Intf
from mininet.log import setLogLevel, info
def myNetwork():
net = Mininet( topo=None,
build=False)
info( '*** Adding controller\n' )
net.addController(name='RYU', controller=RemoteController)
info( '*** Add switches\n')
s1 = net.addSwitch('s1')
Intf('ens37', node=s1)
# s2 = net.addSwitch('s2')
# Intf('ens38', node=s2)
info( '*** Add hosts\n')
h1 = net.addHost('h1', ip='0.0.0.0', mac='00:00:00:00:00:01')
h2 = net.addHost('h2', ip='0.0.0.0', mac='00:00:00:00:00:02')
noeud1 = net.addHost('noeud1', ip='192.168.80.11', mac='00:00:00:00:00:11')
noeud2 = net.addHost('noeud2', ip='192.168.80.12', mac='00:00:00:00:00:12')
info( '*** Add links\n')
net.addLink(h1, s1)
net.addLink(h2, s1)
net.addLink(noeud1, s1)
# net.addLink(noeud1, s2)
net.addLink(noeud2, s1)
# net.addLink(noeud2, s2)
info( '*** Starting network\n')
net.start()
h1.cmdPrint('dhclient '+h1.defaultIntf().name)
h2.cmdPrint('dhclient '+h2.defaultIntf().name)
CLI(net)
net.stop()
if __name__ == '__main__':
setLogLevel( 'info' )
myNetwork()
现在我正在尝试在我的两个节点“noeud1”和“noeud2”之间加入负载平衡,这两个节点都是我的 mininet 网络之外的虚拟机。 (所以总共有 3 个虚拟机 => Mininet、Noeud1、Noeud2)
这是负载均衡的脚本:
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.lib.packet import packet
from ryu.lib.packet import ether_types
from ryu.lib.packet import ipv4
from ryu.lib.mac import haddr_to_int
from ryu.lib.packet.ether_types import ETH_TYPE_IP
from ryu.lib.packet import arp
from ryu.lib.packet import ethernet
class SimpleSwitch13(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
VIRTUAL_IP = '192.168.80.9' # The virtual server IP
SERVER1_IP = '192.168.80.11'
SERVER1_MAC = '00:00:00:00:00:11'
SERVER1_PORT = 3
SERVER2_IP = '192.168.80.12'
SERVER2_MAC = '00:00:00:00:00:12'
SERVER2_PORT = 4
def __init__(self, *args, **kwargs):
super(SimpleSwitch13, self).__init__(*args, **kwargs)
self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install table-miss flow entry
#
# We specify NO BUFFER to max_len of the output action due to
# OVS bug. At this moment, if we specify a lesser number, e.g.,
# 128, OVS will send Packet-In with invalid buffer_id and
# truncated packet data. In that case, we cannot output packets
# correctly. The bug has been fixed in OVS v2.1.0.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions, buffer_id=None):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
# If you hit this you might want to increase
# the "miss_send_length" of your switch
if ev.msg.msg_len < ev.msg.total_len:
self.logger.debug("packet truncated: only %s of %s bytes",
ev.msg.msg_len, ev.msg.total_len)
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst_mac = eth.dst
src_mac = eth.src
dpid = datapath.id
self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src_mac, dst_mac, in_port)
# learn a mac address to avoid FLOOD next time.
self.mac_to_port[dpid][src_mac] = in_port
if dst_mac in self.mac_to_port[dpid]:
out_port = self.mac_to_port[dpid][dst_mac]
else:
out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time
if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst_mac, eth_src=src_mac)
# verify if we have a valid buffer_id, if yes avoid to send both
# flow_mod & packet_out
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
self.add_flow(datapath, 10, match, actions, msg.buffer_id)
return
else:
self.add_flow(datapath, 10, match, actions)
# Handle ARP Packet
if eth.ethertype == ether_types.ETH_TYPE_ARP:
arp_header = pkt.get_protocol(arp.arp)
if arp_header.dst_ip == self.VIRTUAL_IP and arp_header.opcode == arp.ARP_REQUEST:
self.logger.info("***************************")
self.logger.info("---Handle ARP Packet---")
# Build an ARP reply packet using source IP and source MAC
reply_packet = self.generate_arp_reply(arp_header.src_ip, arp_header.src_mac)
actions = [parser.OFPActionOutput(in_port)]
packet_out = parser.OFPPacketOut(datapath=datapath, in_port=ofproto.OFPP_ANY,
data=reply_packet.data, actions=actions, buffer_id=0xffffffff)
datapath.send_msg(packet_out)
self.logger.info("Sent the ARP reply packet")
return
# Handle TCP Packet
if eth.ethertype == ETH_TYPE_IP:
self.logger.info("***************************")
self.logger.info("---Handle TCP Packet---")
ip_header = pkt.get_protocol(ipv4.ipv4)
packet_handled = self.handle_tcp_packet(datapath, in_port, ip_header, parser, dst_mac, src_mac)
self.logger.info("TCP packet handled: " + str(packet_handled))
if packet_handled:
return
# Send if other packet
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)
# Source IP and MAC passed here now become the destination for the reply packet
def generate_arp_reply(self, dst_ip, dst_mac):
self.logger.info("Generating ARP Reply Packet")
self.logger.info("ARP request client ip: " + dst_ip + ", client mac: " + dst_mac)
arp_target_ip = dst_ip # the sender ip
arp_target_mac = dst_mac # the sender mac
# Making the load balancer IP as source IP
src_ip = self.VIRTUAL_IP
if haddr_to_int(arp_target_mac) % 2 == 1:
src_mac = self.SERVER1_MAC
else:
src_mac = self.SERVER2_MAC
self.logger.info("Selected server MAC: " + src_mac)
pkt = packet.Packet()
pkt.add_protocol(
ethernet.ethernet(
dst=dst_mac, src=src_mac, ethertype=ether_types.ETH_TYPE_ARP)
)
pkt.add_protocol(
arp.arp(opcode=arp.ARP_REPLY, src_mac=src_mac, src_ip=src_ip,
dst_mac=arp_target_mac, dst_ip=arp_target_ip)
)
pkt.serialize()
self.logger.info("Done with processing the ARP reply packet")
return pkt
def handle_tcp_packet(self, datapath, in_port, ip_header, parser, dst_mac, src_mac):
packet_handled = False
if ip_header.dst == self.VIRTUAL_IP:
if dst_mac == self.SERVER1_MAC:
server_dst_ip = self.SERVER1_IP
server_out_port = self.SERVER1_PORT
else:
server_dst_ip = self.SERVER2_IP
server_out_port = self.SERVER2_PORT
# Route to server
match = parser.OFPMatch(in_port=in_port, eth_type=ETH_TYPE_IP, ip_proto=ip_header.proto,
ipv4_dst=self.VIRTUAL_IP)
actions = [parser.OFPActionSetField(ipv4_dst=server_dst_ip),
parser.OFPActionOutput(server_out_port)]
self.add_flow(datapath, 20, match, actions)
self.logger.info("<==== Added TCP Flow- Route to Server: " + str(server_dst_ip) +
" from Client :" + str(ip_header.src) + " on Switch Port:" +
str(server_out_port) + "====>")
# Reverse route from server
match = parser.OFPMatch(in_port=server_out_port, eth_type=ETH_TYPE_IP,
ip_proto=ip_header.proto,
ipv4_src=server_dst_ip,
eth_dst=src_mac)
actions = [parser.OFPActionSetField(ipv4_src=self.VIRTUAL_IP),
parser.OFPActionOutput(in_port)]
self.add_flow(datapath, 20, match, actions)
self.logger.info("<==== Added TCP Flow- Reverse route from Server: " + str(server_dst_ip) +
" to Client: " + str(src_mac) + " on Switch Port:" +
str(in_port) + "====>")
packet_handled = True
return packet_handled
如果进入我的主机 h1 并执行命令:
卷曲 192.168.80.11
我必须重新启动我的节点并且命令有效,但我的负载平衡不起作用。
我去 Wireshark 找出问题所在,我认为这是因为 TCP 重传。
这可以解释为什么我总是必须在命令“curl”起作用之前重新启动我的节点。但我不知道如何修复它,因为我是 SDN、OVS 和 Mininet 的新手。
另外,我的最终目标是在上面的脚本中添加第二个开关“s2”,为我的客户添加高可用性,但我想先用一个开关来完成这项工作,而不是添加第二个开关。
欢迎任何帮助,这是我的毕业论文。