概述
Floodlight内部定义报文格式的代码位于net.floodlightcontroller.packet,其中定义的报文类型有ARP,BPDU,BSN,BSNPROBE,DHCP,Ethernet,ICMP,IPv4,LLC,LLDP,TCP,UDP。
其中定义了一个名为IPacket的接口,该接口结构如图:
包内还定义了一个实现了IPacket接口的抽象类BasePacket,BasePacket数据域中定义了两个IPacket类型的对象parent和payload。所有报文对象都继承了BasePacket类并根据不同情况添加了新的方法。以一个IPv4报文为例,其parent域为一个Ethernet对象,其payload域为一个TCP/UDP对象。
在Floodlight内部IFloodlightProviderService类中定义了一个名为bcStore的对象来存储OpenFlow消息所传递上来的消息,其中存储的数据为一个Ethernet对象,其payload域携带着该报文的三层信息,而payload域对象自身的payload域又携带着该报文的四层信息。
一个典型的二层报文Ethernet对象数据结构如下:
现在已获取到了一个Ethernet对象(假设对象名为eth),假设我们想从中取出报文的源目IP地址,可对报文进行如下操作:
if(eth.getPayload() instanceof IPv4) //或if(eth.getEtherType() == Ethernet.TYPE_IPv4)
{
IPv4 pkt =(IPv4)eth.getPayload().clone();//IPv4 pkt =(IPv4)eth.getPayload();应该也可以,未测试。
String src = pkt.fromIPv4Address(pkt.getSourceAddress());
String dst = pkt.fromIPv4Address(pkt.getSourceAddress());
}
要在Floodlight内部获取到OpenFlow消息的步骤如下:
1.新建一个类,使其实现IOFMessageListener, IFloodlightModule接口
2.在getModuleDependencies()方法中声明模块的依赖关系
3.在init()方法中初始化一个IFloodlightProviderService对象
4.在startUp()方法中为上一步中初始化好的对象添加OF消息监听器,并指明要监听的消息类型,如OFType.PACKET_IN
5.在receive方法中,用IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);为一个Ethernet对象赋值。此方法的功能为从 Floodlight控制器上下文对象中取出bcStore所携带的数据。此方法中CONTEXT_PI_PAYLOAD被预定以为一个静态字符串常量 “net.floodlightcontroller.core.IFloodlightProvider.piPayload”。
eclipse设置:
打开eclipse创建一个新的工程
文件->导入->常规->现有项目到工程中->下一步
点击“选择根目录”,点击“浏览”。选择之前放置Floodlight的父路径
点击Floodlight
点击“完成”
现在就产生了一个Floodlight的Eclipse工程。由于我们是使用静态模块加载系统运行Floodlight,我们必须配置eclipse来正确的运行Floodlight。
创建Floodlight目标文件:
点击运行->运行配置
右击java 应用->新建
“Name”使用“FloodlightLaunch”
“Project”使用“Floodlight”
“Main”使用“net.floodlightcontroller.core.Main”
点击“应用”
这里要设置一下eclipse 里console,在window->preperences->Run/Debug->console 中,把’show when program writes to standard out’的勾去掉,这样console消息缓存就不受限制。
Run->Run Configurations->Common->File 使eclipse控制台输出重定向到文件
程序框架:
a.新建一个类继承IOFMessageListerner和IFloodlightModule。
b.声明变量:
protected IFloodlightProviderService floodlightProvider;
protected Set<Long> macAddresses;
protected static Logger logger;
由于要监听openflow消息,所以要向FloodlightProvider 注册。同时需要一个集合变量macAddresses 来存放控制器发现的MAC地址。最终,需要一个记录变量logger来输出发现过程中的记录信息。
c.实现getModuleDependencies方法,把依赖关系告诉模块加载系统:
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
编写模块加载代码。 通过完善getModuleDependencies() 告知加载器在floodlight启动时将自己加载。
d.实现init方法,这个方法会在controller启动时调用,以加载依赖和数据结构:
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
macAddresses = new ConcurrentSkipListSet<Long>();
logger = LoggerFactory.getLogger(MACTracker.class);
}
创建Init方法,Init()将在控制器启动初期被调到,其主要功能室加载依赖关系并初始化数据结构。
e. 实现startUp方法,为PACKET_IN消息绑定事件处理委托,在这之前我们必须保证所有依赖的模块已经 初始化
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
在实现基本监听功能时,packet-in消息需在startup方法中被记录和注册,同时确认新增模块需要依赖的其他模块已被正常初始化。
f.实现getName方法为OFMessage监听器添加ID
@Override
public String getName() {
return "MACTracker"; // 或 return MACTracker.class.getSimpleName();
}
g.为PACKET_IN事件处理程序添加实现代码,该方法返回Command.CONTINUE以便Ifloodprovider能够将Packin消息发往下一个模块,其它事件处理程序继 续处理。
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Ethernet eth
=IFloodlightProviderService.bcStore.get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOA
D);
Long sourceMACHash = Ethernet.toLong(eth.getSourceMACAddress());
if (!macAddresses.contains(sourceMACHash))
{
macAddresses.add(sourceMACHash);
logger.info("MAC Address: {} seen on switch:{}",HexString.toHexString(sourceMACHash),
sw.getId());
}
return Command.CONTINUE;
}
至此,与packet-in消息相关的操作完成。另外还需注意,要返回 Command.CONTINUE 以保证这个消息能够继续被packet-in消息处理。
h. 编写完事件处理程序还需要向Floodlight注册模块,这样Floodlight启动的时候才能加载
具体实现步骤:
添加模块
创建一个监听模块
在Eclipse中添加类
在floodlight中找出“src/main/java”。
在“src/main/java”目录下选择“New/Class”.
在packet中输入“net.floodlightcontroller.mactracker”
在 name中输入“MACTracker”
在“Interfaces”中,单击“Add…”,“chooseinterface”增加“IOFMessageListener”and the “IFloodlightModule”, 单击“OK”.
单击“Finish”
产生的对应程序如下:
public class MACTracker implements IOFMessageListener, IFloodlightModule {
@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 String getName() {
// TODO Auto-generated method stub
return null;
}
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
// TODO Auto-generated method stub
return null;
}
}
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;
}
设置模块化关系并初始化
开始前需处理一系列代码依赖关系。Eclipse中可根据代码需要在编译过程中自动添加依赖包描述。没有相关工具,就需要手动添加代码如下:
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.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
至此,代码基本框架完成,进而要实现必要功能使模块能正确被加载。首先,注册Java类中需要的成员变量。由于要监听openflow消息,所以要向FloodlightProvider 注册。同时需要一个集合变量macAddresses 来存放控制器发现的MAC地址。最终,需要一个记录变量logger来输出发现过程中的记录信息。
protected IFloodlightProviderService floodlightProvider;
protected Set macAddresses;
protected static Logger logger;
编写模块加载代码。 通过完善getModuleDependencies() 告知加载器在floodlight启动时将自己加载。
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
创建Init方法,Init()将在控制器启动初期被调到,其主要功能室加载依赖关系并初始化数据结构。
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
macAddresses = new ConcurrentSkipListSet<Long>();
logger = LoggerFactory.getLogger(MACTracker.class);
}
处理Packet-In 消息
在实现基本监听功能时,packet-in消息需在startup方法中被记录和注册,同时确认新增模块需要依赖的其他模块已被正常初始化。
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
还需要为 OFMessage监听者提供一个ID,可通过调用getName()实现。
@Override
public String getName() {
return MACTracker.class.getSimpleName();
}
至此,与packet-in消息相关的操作完成。另外还需注意,要返回 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 = Ethernet.toLong(eth.getSourceMACAddress());
if (!macAddresses.contains(sourceMACHash)) {
macAddresses.add(sourceMACHash);
logger.info("MAC Address: {} seen on switch: {}",
HexString.toHexString(sourceMACHash),
sw.getId());
}
return Command.CONTINUE;
}
注册模块
如果要在floodlight启动时加载新增模块,需向加载器告知新增模块的存在,在src/main/resources/META-INF/services/net.floodlight.core.module.IFloodlightModule文件上增加一个符合规则的模块名,即打开该文件并在最后加上如下代码。
net.floodlightcontroller.mactracker.MACTracker
然后,修改floodlight的配置文件将 MACTracker相关信息添加在文件最后。Floodlight的缺省配置文件是src/main/resources/floodlightdefault.properties。其中,floodlight.module选项的各个模块用逗号隔开,相关信息如下:
floodlight.modules = (leave the default list of modules in place),
net.floodlightcontroller.mactracker.MACTracker
最终,即可运行控制器并观察新增模块的功能:mininet创建拓扑执行pingall命令。
完整代码
package net.floodlightcontroller.mactracker;
import java.util.Collection;
import java.util.Map;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFType;
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;
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.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MACTracker implements IFloodlightModule, IOFMessageListener {
protected IFloodlightProviderService floodlightProvider;
protected Set<Long> macAddresses;
protected static Logger logger;
@Override
public String getName() {
// TODO Auto-generated method stub
return MACTracker.class.getSimpleName();
}
@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 net.floodlightcontroller.core.IListener.Command receive(
IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
// TODO Auto-generated method stub
Ethernet eth =
IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Long sourceMACHash = Ethernet.toLong(eth.getSourceMACAddress());
if (!macAddresses.contains(sourceMACHash)) {
macAddresses.add(sourceMACHash);
logger.info("MAC Address: {} seen on switch: {}",
HexString.toHexString(sourceMACHash),
sw.getId());
}
return Command.CONTINUE;
}
@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
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// TODO Auto-generated method stub
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
macAddresses = new ConcurrentSkipListSet<Long>();
logger = LoggerFactory.getLogger(MACTracker.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
// TODO Auto-generated method stub
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
}