前言

用mininet + floodlight搭建好环境之后,运行flooglight,然后在mininet中建立网络拓扑,建好之后,pingall,所有host之间可以ping通。

然后向控制器floodlight中添加防火墙规则,比如

curl -X POST -d '{"src-ip": "10.0.0.1/32", "dst-ip": "10.0.0.2/32","action":"DENY","priority":"10"}' http://localhost:8080/wm/firewall/rules/json

,然后10.0.0.1与10.0.0.2之间就不能ping通了。

那么,这条规则是如何起作用的呢?

注:为简单起见,文中贴的代码片段是在Floodloght源代码文件基础上删减后的。

首先定位到控制数据包转发的类net.floodlightcontroller.forwarding.Forwarding,从该类的processPacketInMessage方法入手。当交换机收到不知道怎么处理的数据包时(即新来的数据包与交换机里现存的所有flow-entry都不匹配),就以packetIn消息的形式将该数据包传给controller。以下方法就是处理packetIn消息的代码:

public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
// If a decision has been made we obey it
// otherwise we just forward
if (decision != null) {switch(decision.getRoutingAction()) {
case NONE:
// don't do anything
return Command.CONTINUE;case FORWARD:
doForwardFlow(sw, pi, cntx, false);
return Command.CONTINUE;
case MULTICAST:
// treat as broadcast
doFlood(sw, pi, cntx);
return Command.CONTINUE;
case DROP:
doDropFlow(sw, pi, decision, cntx);
return Command.CONTINUE;
default:
}
} else {
doForwardFlow(sw, pi, cntx, false);
}
return Command.CONTINUE;
}

上述代码表明,decision决定了一个数据包是被forward还是被drop。特别的,当decision为null时,对该packet进行forward或者flood。

所以接下来看哪里调用了processPacketInMessage方法。找到net.floodlightcontroller.routing.ForwardingBase这个类,该类的receive方法是唯一一个调用了processPacketInMessage方法的函数,以下是代码:

@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
switch (msg.getType()) {
case PACKET_IN:
IRoutingDecision decision = null;
if (cntx != null)
decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
default:
break;
}
return Command.CONTINUE;
}

上述代码表明decision的值来源于cntx。cntx是一个对象,创建它的类是FloodlightContext,源代码里的注释表示“This is a context object where floodlight listeners can register and later retrieve context information associated with an event”。调用receive的方法有三个,如下:

net.floodlightcontroller.core.internal.Controller.handleMessage(IOFSwitch, OFMessage, FloodlightContext)
net.floodlightcontroller.core.internal.Controller.handleOutgoingMessage(IOFSwitch, OFMessage, FloodlightContext)
net.floodlightcontroller.core.internal.OFSwitchImpl.deliverStatisticsReply(OFMessage)

而cntx的意思大概也明白了:是一个floodlight运行的上下文环境,cntx从floodlight一开始运行的时候就收集一些信息,floodlight里的监听器相关连的事件,如果事件发生,监听器可以从当前的cntx中检索需要的值。可见这个cntx是一个处于线程池中动态更新的对象。

再往下的跟踪超出我的理解范围了(有兴趣的可以自己去跟以下,会跟到net.floodlightcontroller.core.internal.Controller这个类)。可以这么理解:当你向控制器中添加防火墙规则时候,监听器会检测到新事件的产生(添加防火墙规则时,eclipse控制台的突然大量输出可以说明这一点),并将相关信息注册到cntx。

返回来看看processPacketInMessage方法里调用的doDropFlow和doForwardFlow方法。这两个方法也在net.floodlightcontroller.forwarding.Forwarding这个类里。先看doDropFlow方法,代码如下:

protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
// initialize match structure and populate it using the packet
OFMatch match = new OFMatch();
match.loadFromPacket(pi.getPacketData(), pi.getInPort());
if (decision.getWildcards() != null) {
match.setWildcards(decision.getWildcards());
} // Create flow-mod based on packet-in and src-switch
OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
long cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); fm.setCookie(cookie)
.setHardTimeout((short) 0)
.setIdleTimeout((short) 5)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setMatch(match)
.setActions(actions)
.setLengthU(OFFlowMod.MINIMUM_LENGTH); // +OFActionOutput.MINIMUM_LENGTH); try {
messageDamper.write(sw, fm, cntx);
} catch (IOException e) {
log.error("Failure writing drop flow mod", e);
}
}

上述代码里核心的一句是messageDamper.write(sw, fm, cntx);这行代码表示往交换机sw里下发流表策略。sw是将这个packet传送给controller的交换机。而下发的这些流表就能实现防火墙规则的功能,禁止10.0.0.1与10.0.0.2之间通信。留意fm和cntx,这两个变量决定了下发的流表是什么样的。

再看doForwardFlow方法。doForwardFlow代码复杂一些,把前人的分析copy过来:

参考自http://lamoop.com/post/2013-11-28/40060265578

首先声明一个OFMatch对象,并将packet-in中的相关信息加载到该对象中:

OFMatch match = new OFMatch(); match.loadFromPacket(pi.getPacketData(), pi.getInPort());

然后通过packet-in消息获取目标和源设备(idevice),即以下代码:

IDevice dstDevice =IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
if (dstDevice != null) {
IDevice srcDevice =
IDeviceService.fcStore.
get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);

再然后定位发生packet-in消息的switch所属于的island的标志

Long srcIsland = topology.getL2DomainId(sw.getId());

接下来,判断目标和源设备是否处于同一个island,是则继续,否则执行doFlood方法(类似广播),代码如下:

// Validate that we have a destination known on the same island
// Validate that the source and destination are not on the same switchport
boolean on_same_island = false;
boolean on_same_if = false;
for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
long dstSwDpid = dstDap.getSwitchDPID();
Long dstIsland = topology.getL2DomainId(dstSwDpid);
if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
on_same_island = true;
if ((sw.getId() == dstSwDpid) &&
(pi.getInPort() == dstDap.getPort())) {
on_same_if = true;
}
break;
}
} if (!on_same_island) {
// Flood since we don't know the dst device
if (log.isTraceEnabled()) {
log.trace("No first hop island found for destination " +
"device {}, Action = flooding", dstDevice);
}
doFlood(sw, pi, cntx);
return;
}

如果在同一个island中,则通过getAttachmentPoints方法获取目标和源设备相连的交换机的端口信息

SwitchPort[] srcDaps = srcDevice.getAttachmentPoints();
Arrays.sort(srcDaps, clusterIdComparator);
SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
Arrays.sort(dstDaps, clusterIdComparator);

获取到的结果是两个排序的端口数组,数组的元素类似:

SwitchPort [switchDPID=7, port=2, errorStatus=null]

再接下来进入一个while循环,循环的终止条件是取完上一步得到的两个数组中的元素,目标是找到属于同一个island的分布在两个数组中的元素。循环内部先通过getL2DomainId方法判断,所选取的与目标和源相连接的交换机是否在同一个island,如果不在,则根据两个island标志的大小选择性的选取srcDaps或dstDaps中的其它元素,继续比较,之所以可以这么做,是因为两个数组是经过排序的,而且key就是各个交换机所属的island标志,代码为while循环最后的if-else语句:

} else if (srcVsDest < 0) {
iSrcDaps++;
} else {
iDstDaps++;
}

如果找到两个island相同的元素,会调用routingEngine的getRoute方法获取两个端口之间的路由,这才是我们真正关心的流程。这里并未继续跟踪getRoute是如何获取两个交换机端口之间的最短路径(官网提到获取的路由是最短路径),其获取的结果类似:

[[id=00:00:00:00:00:00:00:07, port=2], [id=00:00:00:00:00:00:00:07, port=3],
[id=00:00:00:00:00:00:00:05, port=2], [id=00:00:00:00:00:00:00:05, port=3],
[id=00:00:00:00:00:00:00:01, port=2], [id=00:00:00:00:00:00:00:01, port=1],
[id=00:00:00:00:00:00:00:02, port=3], [id=00:00:00:00:00:00:00:02, port=1],
[id=00:00:00:00:00:00:00:03, port=3], [id=00:00:00:00:00:00:00:03, port=1]]

接下来利用最初生成的OFMatch对象信息定义一个规则:

wildcard_hints = ((Integer) sw.getAttribute(IOFSwitch.PROP_FASTWILDCARDS))
.intValue()
& ~OFMatch.OFPFW_IN_PORT
& ~OFMatch.OFPFW_DL_VLAN
& ~OFMatch.OFPFW_DL_SRC
& ~OFMatch.OFPFW_DL_DST
& ~OFMatch.OFPFW_NW_SRC_MASK
& ~OFMatch.OFPFW_NW_DST_MASK;

最后调用pushRoute方法,下发路由策略,即flow信息。

pushRoute(route, match, wildcard_hints, pi, sw.getId(), cookie,
cntx, requestFlowRemovedNotifn, false,
OFFlowMod.OFPFC_ADD);

继续追踪pushRoute函数,其在ForwardingBase.java中实现,其实就是循环上面route获取到的最短路径,在每个交换机上添加一条flow,包含inport和outport,当然上面提到的wildcard_hints也会通过setMatch方法设置到没条flow中。

pushRoute函数里也有messageDamper.write(sw, fm, cntx);这行代码。作用与doDropFlow方法里的这行代码一样:往交换机里下发流表策略。doDropFlow里是下发阻止某两个主机通信的流表,而这里下发的流表是为了网络中的其他主机可以通信。

Floodlight 防火墙是如何起作用的的更多相关文章

  1. linux下如何关闭防火墙?如何查看防火墙当前的状态

    从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙查看防火墙状态:/etc/init.d/iptables status暂时关闭防火墙:/etc/init.d/iptables stop ...

  2. [转]Openstack neutron 防火墙

    全文阅读传送门:http://www.ustack.com/wp-content/uploads/2013/11/Neutron%E9%98%B2%E7%81%AB%E5%A2%99.pdf 原作者: ...

  3. Linux防火墙基本知识

    一.防火墙的分类 (一).包过滤防火墙. 数据包过滤(packet Filtering)技术是在网络层对数据包进行选择,选择的依据是系统内设置的过滤逻辑,称为访问控制表(access control ...

  4. fedora之防火墙

    通过iptables的设置可以学一些东西 从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙查看防火墙状态:/etc/init.d/iptables status暂时关闭防火墙:/etc ...

  5. linux下查看防火墙当前状态,开启关闭等

    从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙 查看防火墙状态: /etc/init.d/iptables status 暂时关闭防火墙: /etc/init.d/iptables  ...

  6. 【linux命令】打开关闭防火墙iptables

    防火墙关闭 关闭防火墙(linux) 经过自己的实验,发现在ubuntu中service iptables 无法使用. 同时,在init.d中并没有iptables的程序,iptables程序在/sb ...

  7. Linux防火墙操作

    从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙. 查看防火墙状态: /etc/init.d/iptables status暂时关闭防火墙: /etc/init.d/iptables  ...

  8. linux下如何关闭防火墙、查看当前的状态、开放端口

    从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙查看防火墙状态:/etc/init.d/iptables status暂时关闭防火墙:/etc/init.d/iptables stop ...

  9. centos 防火墙关闭/开启

    从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙查看防火墙状态:/etc/init.d/iptables status暂时关闭防火墙:/etc/init.d/iptables stop ...

随机推荐

  1. c++11: less的用法

    less主要是重载了operator()方法,用来比较lhs 和 rhs std::less::operator() bool operator()(const T &lhs, const T ...

  2. 正则匹配 去掉 多余的js和html标签

    $reg17 = '/><strong>公司介绍<\/strong><\/td>([\S\s*]+?)<\/div>/'; $this->d ...

  3. linux的7种运行级别

    Linux有7个运行级别(runlevel)运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆运行级别2:多用户 ...

  4. [大牛翻译系列]Hadoop(2)MapReduce 连接:复制连接(Replication join)

    4.1.2 复制连接(Replication join) 复制连接是map端的连接.复制连接得名于它的具体实现:连接中最小的数据集将会被复制到所有的map主机节点.复制连接有一个假设前提:在被连接的数 ...

  5. Android Material Design:滑动指示选项卡android.support.design.widget.TabLayout的简单使用

    该TabLayout的功用,简单的说,就是当用户在该TabLayout的选项卡子item中选择触摸时候,文字和下方的指示器横条滑动指示.这个功能就是以前APP开发常用的选项卡某一卡片被切换.选中时候的 ...

  6. Django之Model(一)--基础篇

    0.数据库配置 django默认支持sqlite,mysql, oracle,postgresql数据库.Django连接数据库默认编码使用UTF8,使用中文不需要特别设置. sqlite djang ...

  7. hdu 5738 2016 Multi-University Training Contest 2 Eureka 计数问题(组合数学+STL)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5738 题意:从n(n <= 1000)个点(有重点)中选出m(m > 1)个点(选出的点只 ...

  8. vim中执行shell命令小结

    vim中执行shell命令,有以下几种形式 1):!command 不退出vim,并执行shell命令command,将命令输出显示在vim的命令区域,不会改变当前编辑的文件的内容 例如:!ls -l ...

  9. netlink---Linux下基于socket的内核和上层通信机制 (转)

    需要在linux网卡 驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文COPY下必要的数据交给用户 态来完成(因为过于复杂的报文消耗 ...

  10. win2008修改最大远程桌面连接数

    win2008修改最大远程桌面连接数 运行——gredit.msc——管理模板——windows组件——远程桌面服务——远程桌面回话主机——连接——限制连接的数量——修改为999999