当前位置: 首页 > 工具软件 > Floodlight > 使用案例 >

Floodlight 入门 之 起步篇 - 建立一个Floodlight模块

漆雕深
2023-12-01

Floodlight 入门 之 起步篇 - 建立一个Floodlight模块

  • 2017-3-1

网上也有不少关于Floodlight的入门教程了,我写该博文的目的主要是问了整理个人在学习和使用Floodlight时掌握的知识,如果这些文字能为大家带来帮助是最好不过的了。

博文会持续更新,如果想要跟踪该博文的进度,推荐使用RSS。每次更新会在博文开始出更改时间戳。

该博文系列是从Floodlight官网的官方教程翻译而来,想嚼原味教程请点击链接Tutorials


前提条件

我们在进行下面的实验前,首先要检查我们的实验环境是否满足下面的条件:

  • 成功地完成了Getting Started的教程,包括配置Eclipse。
  • 安装了Mininet或者有一个可以使用的OpenFlow物理交换机。

确保满足上述条件后,就可以进行下面的实验了。

创建一个监听器

在Eclipse中添加一个Class

  1. 展开floodlight项,你会看到“src/main/java”文件夹。
  2. 选中“src/main/java”文件夹,然后右击,在弹出的菜单中选中 “New/Class”。
  3. 在“Package”栏写入net.floodlightcontroller.mactracker。
  4. 在“Name”栏写入MACTracker。
  5. 然后就是“Interfaces”栏,我们点击旁边的“Add..”为该类添加接口。
  6. 添加”IOFMessageListener”和”IFloodlightModule”,然后点击OK。
  7. 点击“Finish”完成类的创建。

上面的步骤完成后,你将会看到下面的代码:

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);
}

处理Packet-In消息

经过上面的操作,我们的模块已经可以作为一个模块正常的运行了。然而我们的目的是创建一个能跟踪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;
    }

在处理Openflow消息是模块的序列

这一段说明在该教程中并不是必要的,但当我们用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)。

如何将Mininet与Floodlight控制器相连

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]
 类似资料: