网上也有不少关于Floodlight的入门教程了,我写该博文的目的主要是问了整理个人在学习和使用Floodlight时掌握的知识,如果这些文字能为大家带来帮助是最好不过的了。
博文会持续更新,如果想要跟踪该博文的进度,推荐使用RSS。每次更新会在博文开始出更改时间戳。
该博文系列是从Floodlight官网的官方教程翻译而来,想嚼原味教程请点击链接Tutorials。
我们在进行下面的实验前,首先要检查我们的实验环境是否满足下面的条件:
确保满足上述条件后,就可以进行下面的实验了。
上面的步骤完成后,你将会看到下面的代码:
package net.floodlightcontroller.mactracker;
import java.util.Collection;
import java.util.Map;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.types.MacAddress;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
public class MACTracker implements IOFMessageListener, IFloodlightModule {
@Override
public String getName() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
// TODO Auto-generated method stub
return false;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
// TODO Auto-generated method stub
return null;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// TODO Auto-generated method stub
}
@Override
public void startUp(FloodlightModuleContext context) {
// TODO Auto-generated method stub
}
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
// TODO Auto-generated method stub
return null;
}
}
在我们开始之前,我们需要为我们创建的代码设置一系列的依赖项。使用Eclipse的话,添加这些依赖项会很容易。如果你没有使用Eclipse来编写代码,你就需要手动将下面的依赖项添加到java文件的开头:
import net.floodlightcontroller.core.IFloodlightProviderService;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.Set;
import net.floodlightcontroller.packet.Ethernet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
现在我们有了必需的依赖项,而且通过之前创建类的操作,我们有了一个有框架的类。然而这样的类肯定还是不能正常使用的,我们需要实现一些方法来确保我们创建的模块可以被加载。首先,我们需要在该类中添加一些必要的成员变量。我们现在要创建的模块是要实现跟踪MAC地址的功能,所以我们需要监听Openflow messages,需要添加一个FloodlightProvider((IFloodlightProviderService class)。我们还需要一个数据结构存储我们监听到的MAC地址,需要一个logger来输出我们监听到的信息。最后我们需要的成员变量如下所示:
protected IFloodlightProviderService floodlightProvider;
protected Set<Long> macAddresses;
protected static Logger logger;
现在我们需要完成模块被加载的功能。我们对getModuleDependencies()方法进行更改,以此来告诉模块加载器我们依赖于它:
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
现在我们的模块可以被加载了。一般来说,一个模块被加载时是要进行初始化的。所以接下来我们来对初始化方法进行实现:
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
macAddresses = new ConcurrentSkipListSet<Long>();
logger = LoggerFactory.getLogger(MACTracker.class);
}
经过上面的操作,我们的模块已经可以作为一个模块正常的运行了。然而我们的目的是创建一个能跟踪MAC地址的模块,所以我们接下来还要对模块的功能进行实现。首先,我们需要实现一个简单的监听器(用来监听OpenFlow消息)。我们将会在我们的startUp方法中注册一个监听PACKET_IN消息的监听器。这里我们假定我们依赖的其他模块已经初始化好了:
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
我们还须要为我们的OFMessage监听器放入一个ID什么的(原文:We also have to put in an ID for our OFMessage listener.)。这里可以在在回调函数getName()中完成:
@Override
public String getName() {
return MACTracker.class.getSimpleName();
}
现在我们就可以叫我们的PACKET_IN消息与特定的行为进行绑定了。这在receive方法中实现,注意:我们这里要返回Command.CONTINUE,这样在我们之后模块才可以继续执行针对该PACKET_IN消息的操作:
@Override
public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Ethernet eth =
IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Long sourceMACHash = eth.getSourceMACAddress().getLong();
if (!macAddresses.contains(sourceMACHash)) {
macAddresses.add(sourceMACHash);
logger.info("MAC Address: {} seen on switch: {}",
eth.getSourceMACAddress().toString(),
sw.getId().toString());
}
return Command.CONTINUE;
}
这一段说明在该教程中并不是必要的,但当我们用IOFMessageListeners处理OpenFlow消息时,定义处理顺序往往是必要的。IOFMessageListeners的isCallbackOrderingPrereq(OFType type, String name)和isCallbackOrderingPostreq(OFType type, String name)方法定义了处理来自交换机的消息的模块的处理顺序。
isCallbackOrderingPrereq()定义了在处理某一类的OpenFlow消息时,哪些模块应该跑在我们的模块前。 isCallbackOrderingPostreq()定义了在处理某一类的OpenFlow消息时,哪些模块应该跑在我们的模块后。
经过上面的操作,我们差不多已经完成了模块的实现。现在我们只需要告诉Floodlight我们要在启动时加载该模块。首先我们要告诉加载器这些模块的存在。我们打开src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule,然后添加下面一行文字:
net.floodlightcontroller.mactracker.MACTracker
然后我们还需要修改配置文件src/main/resources/floodlightdefault.properties:
floodlight.modules = <leave the default list of modules in place>, net.floodlightcontroller.mactracker.MACTracker
最后,我们启动控制器(选中Run As…/Java Application,然后选中Main.java)。
Floodlight默认的监听端口是6653(可以在配置文件中查看和修改src/main/resources/floodlightdefault.properties),我们连接Floodlight需要知道Floodlight所在主机的IP地址(如果是本地,就输入127.0.0.1或0.0.0.0),注意:好像只有Floodlight v1.2版本支持OpenFlow1.3协议,如果使用早期版本的Floodlight如v1.0,建议将protocols=OpenFlow13改为protocols=OpenFlow10:
mininet@mininet:~$ sudo mn --controller=remote,ip=<Floodlight IP Address>,port=6653 --switch=ovsk,protocols=OpenFlow13
*** Loading ofdatapath
*** Adding controller
*** Creating network
*** Adding hosts:
h2 h3
*** Adding switches:
s1
*** Adding edges:
(s1, h2) (s1, h3)
*** Configuring hosts
h2 h3
*** Starting controller
*** Starting 1 switches
s1
*** Starting CLI:
mininet>pingall
连接成功后,输入pingall,则可以在Eclipse的Console中看到模块的输出信息。
作为练习,我写了一个模块,但并不是MACtracer,而是监听所有Packet-In然后输出Payload,输出结果如下:
13:56:05.521 INFO [n.f.p.m.NetMonitor:nioEventLoopGroup-3-4] Switch Id:00:00:00:00:00:00:00:01,Payload:IPv6 [version=6, trafficClass=0, flowLabel=0, payloadLength=16, nextHeader=0x3a, hopLimit=-1, sourceAddress=fe80::ac97:c0ff:fe0b:a39b, destinationAddress=ff02::2, parent=
0x86dd
dl_vlan: untagged
dl_vlan_pcp: 0
dl_src: ae:97:c0:0b:a3:9b
dl_dst: 33:33:00:00:00:02
nw_src: fe80::ac97:c0ff:fe0b:a39b
nw_dst: ff02::2
nw_tclass: 0
nw_proto: 0x3a, payload=net.floodlightcontroller.packet.Data@da31eb25]
13:56:08.717 INFO [n.f.p.m.NetMonitor:nioEventLoopGroup-3-4] Switch Id:00:00:00:00:00:00:00:01,Payload:IPv6 [version=6, trafficClass=0, flowLabel=0, payloadLength=16, nextHeader=0x3a, hopLimit=-1, sourceAddress=fe80::ac7d:88ff:fe59:6165, destinationAddress=ff02::2, parent=
0x86dd
dl_vlan: untagged
dl_vlan_pcp: 0
dl_src: ae:7d:88:59:61:65
dl_dst: 33:33:00:00:00:02
nw_src: fe80::ac7d:88ff:fe59:6165
nw_dst: ff02::2
nw_tclass: 0
nw_proto: 0x3a, payload=net.floodlightcontroller.packet.Data@f8ec7dee]
13:56:09.529 INFO [n.f.p.m.NetMonitor:nioEventLoopGroup-3-4] Switch Id:00:00:00:00:00:00:00:01,Payload:IPv6 [version=6, trafficClass=0, flowLabel=0, payloadLength=16, nextHeader=0x3a, hopLimit=-1, sourceAddress=fe80::ac97:c0ff:fe0b:a39b, destinationAddress=ff02::2, parent=
0x86dd
dl_vlan: untagged
dl_vlan_pcp: 0
dl_src: ae:97:c0:0b:a3:9b
dl_dst: 33:33:00:00:00:02
nw_src: fe80::ac97:c0ff:fe0b:a39b
nw_dst: ff02::2
nw_tclass: 0
nw_proto: 0x3a, payload=net.floodlightcontroller.packet.Data@da31eb25]