Floodlight中模块对Packet_in报文的处理

前言

看源码最痛苦的是知其然而不知其所以然,当然也有人建议我不要在意这些细节~~,可是看不懂浑身不舒服啊亲,我也希望自己有高超的写代码能力,事实是还需努力。接触Floodlight这么久了,以前做的最多的是调用RET API进行一些实验,改过官方为开发者提供的MACTracker代码,但实际上没花太多时间看源码,最近做实验啥的感觉瓶颈了,唉,遂决心好好研究一下源码,看是否能给自己新的灵感。此次参考的源码是我修改过的MACTracker,我将要分析的是其中对Packet_in事件处理的方法recevie()。

Packet_in处理分析

首先,贴上代码,主要功能是获取连上控制器的主机的MAC地址,IP地址并在控制台显示出来。

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(eth.getPayload() instanceof IPv4){
IPv4 pkt = (IPv4)eth.getPayload().clone();
String src = IPv4.fromIPv4Address(pkt.getSourceAddress());
String dst = IPv4.fromIPv4Address(pkt.getDestinationAddress());
if (!macAddresses.contains(sourceMACHash)) {
macAddresses.add(sourceMACHash);
logger.info("MAC Address: {} IP Address: {} seen on switch",HexString.toHexString(sourceMACHash),src);
}
}
return Command.CONTINUE;
}

Packet_in包处理流程

1.receive()是模块用来处理Packt_in数据包的函数,每个需要处理Packet_in包的模块均默认以它命名,所以它们获取OpenFlow消息的方式也是相同的。
2.首先通过代码

Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD)

获取消息。其中通过调用接口IFloodlightProviderService的静态方法bcstore获取OpenFlow消息,而该消息是一个Ethernet对象。我们可以通过Ethernet里的方法getSourceMACaddress()获取该报文的源MAC地址;然后再通过getPayload()获取报文的有效载荷(所谓有效载荷,指的是除去协议头部之外实际传输的数据),根据有效载荷可以判断报文的协议类型:ARP,IPv4,ICMP,DUCP等网络层协议类型。
3.若是IPv4包:IPv4类里有个方法getSourceAddress()获取源IP地址(二进制),通过方法fromIPv4Address将IP地址转换为点分十进制的。

bcstore

bcstore是接口IFloodlightProvider里定义的一个对象,主要是存储OpenFlow消息,消息的对象是Ethernet类型的。bcstore本身是FloodightContextStore<>类型的,该类型有个方法用来获取存储的消息:get(FloodlightContext bc,String key).方法具体实现如下:

public V get(FloodlightContext bc, String key) {
return (V)bc.storage.get(key);}

return.Command.CONTINUE

最开始照着开发者文档做的时候,看到recieve()结尾处的return.Command.CONTINUE的解释是“该方法返回Command.CONTINUE以便其他处理程序继续处理”,而且我查看了其他好几个模块recieve函数返回值都是Command.CONTINUE,于是就找到了Command的源头。原来Command是接口Ilistener下的一个枚举类型:

public enum Command {
CONTINUE, STOP
}

当时我就纳闷了,返回一个枚举类型值,谁来处理它?怎么处理这个值以实现能让其他模块继续执行的功能?于是我挨个模块源码寻找,终于在模块net.floodlight.core.internal.controller找到了它的身影:

pktinProcTime.recordStartTimePktIn();
Command cmd;
for (IOFMessageListener listener : listeners) {
pktinProcTime.recordStartTimeComp(listener);
cmd = listener.receive(sw, m, bc);
pktinProcTime.recordEndTimeComp(listener);
if (Command.STOP.equals(cmd)) {
break;
}
}

这段代码就是对recevie()返回值进行处理,如果返回的是STOP,则该事件停止处理,也就是当前模块处理完后其他模块就没法处理了,而默认情况下事件会被每个模块遍历处理,所以我们会看到大多处理事件的模块返回的值都是CONTINUE。