前言
一般按照odl官方文档或者wiki安装L2switch组件会采用在karaf控制台上输入feature:install odl-l2switch-all命令,该命令是将L2switch组件全部安装到odl控制器,而L2switch组件包括packethandler、loopremover、l2switch-main、hosttracker、arphandler、addresstracker模块这几个,模块之间的相互依赖关系可以在features当中查看。
模块功能介绍
如上所述,L2switch组件包括packethandler、loopremover、l2switch-main、hosttracker、arphandler、addresstracker模块,各个模块先简单描述功能,后面会详细从代码上分析。
1、Packethandler :数据包处理器
Packethandler就是将openflowplugin接收到的packetin数据包解封装送到其他模块,并且按照顺序发送,是L2switch与openflowplugin的桥梁,所有的需要l2switch处理的packetin报文都会经过它处理解析,所以分析l2switch需要先从Packethandler分析。
Packethandler模块的入口在PacketHandlerModule.java的createInstance函数:
此函数当中调用了initiateDecoders函数进行初始化,其实也就是new出一些解码器对象,比如EthernetDecoder、ArpDecoder、Ipv4Decoder、Ipv6Decoder解码器。
解码器的设计是先定义一个抽象类,这样一来其他解码器只要继承该抽象类,然后做自己的具体实现就可以了。所以各个文件描述如下:
AbstractPacketDecoder.java——定义的抽象类,所有解封装器必须继承实现其中的方法。
EthernetDecoder.java——mac帧解码器类,openflowplugin接收到的报文上送至控制器,由EthernetDecoder统一mac帧解包(去掉mac帧头),再发送给对应的解码类(ARP、IPV4、IPV6)处理(此过程并不是先判断是arp还是ip报文再发送,而是发送广播notification,所有解码器都能接受到该广播,只不过解码器当中自己去判断是否是自己应该解的报文——canDecode,如果不是则不处理)。
先看EthernetDecoder以太网mac帧的解码,首先EthernetDecoder是实现PacketProcessingListener接口,当openflowplugin接收到packetIn报文时,就会发出notification出来,此时PacketProcessingListener的onPacketReceived函数就会被调用,所以所有packetIn报文都会通过onPacketReceived函数进行后续处理。
报文进入decodeAndPublish之后转到AbstractPacketDecoder抽象类当中,
然后进入decode函数,此函数是抽象函数,所以由具体类实现,比如刚刚是EthernetDecoder转进来的报文,则由EthernetDecoder解析。
解析完了之后,继续采用notification的方式发布,这样报文就经过了mac帧的解码,继续流向下一个环节。
再看IPV4解码类,注意到刚刚EthernetDecoder将报文解码之后,发出notification,而Ipv4Decoder实现了EthernetPacketListener,所以Ipv4Decoder能够收到上述通告,进入onEthernetPacketReceived函数
同理Ipv4Decoder也会跟EthernetDecoder将报文内容解析出来,并将报文以notification形式发出去。
整个Packethandler的报文处理就是这样,所以如果我们想自己定义一种新的packetin报文上送到控制器,就可以在此处按照这样的流程进行报文解析,比如思科自己的cdp报文解析。
最后归类下:
EthernetDecoder.java – 以太网帧解码类
ArpDecoder.java – arp报文解码类
Ipv4Decoder.java -- IPV4报文解码类
Ipv6Decoder.java --IPV6报文解码类
2、Addresstracker
由openflowplugin发上来的报文经过Packethandler处理之后,就会经过Addresstracker模块进行网络设备的MAC地址以及IP学习。
Addresstracker的入口在AddressTrackerModule.java文件当中的函数createInstance。
稍微解释下,AddressObservationWriter是一个公有类,在56-addresstracker.xml当中配置了observe-addresses-from节点,可以是arp、ipv4、ipv6,默认是arp,是arp则调用AddressObserverUsingArp监听arp报文,然后调用addressObservationWriter的addAddress函数添加地址(mac-ip)到md-sal的Inventory,从而触发Hosttracker的主机发现。如果发现地址是已经存在的,则通过timestampUpdateInterval来判断是否需要更新地址时间戳。
比如根据arp来学习设备地址,则进入AddressObserverUsingArp.java文件,可以看到该文件对可以监听上述的Packethandler通告,这样arp报文就流入此文件进行处理。
接收arp报文通告之后,可以根据报文的源mac、源IP等加入datastore。
进入addressObservationWriter.addAddress()函数就是MAC的学习,一般来说是主机host的MAC学习,为下面的hosttracker提供数据。
学习到了MAC之后,将该mac信息以AddressCapableNodeConnector通过Augmentation的方式merge到NodeConnector数据节点,也就是在address-tracker.yang中定义的数据节点,此时merge的数据是在inventory数据库当中。
3、Hosttracker 主机发现
随着上述的流程,当学习到主机host的mac之后,Hosttracker会将新主机以node的形式添加到network-topology当中,这样web前端就有主机节点显示出来。
Hosttracker入口在HostTrackerModule.java的createInstance函数当中,从该函数当中new出一个HostTrackerImpl对象,并注册数据监听器,主要是监听上述的NodeConnector 的AddressCapableNodeConnector数据变化。
当Addresstracker学习到主机mac之后,merge主机的AddressCapableNodeConnector到NodeConnector就会引起hostracker进入onDataChanged函数。
从而进入packetReceived函数,进而进入processHost函数。
在processHost函数当中就将主机添加到network-topology数据节点当中,并建立主机与交换机的link链接。
private void processHost(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
NodeConnector nodeConnector,
Addresses addrs) {
List<Host> hostsToMod = new ArrayList<>();
List<Host> hostsToRem = new ArrayList<>();
List<Link> linksToRem = new ArrayList<>();
List<Link> linksToAdd = new ArrayList<>();
synchronized (hosts) {
log.trace("Processing nodeConnector " + nodeConnector.getId().toString());
HostId hId = Host.createHostId(addrs);
if (hId != null) {
if (isNodeConnectorInternal(nodeConnector)) {
log.trace("NodeConnector is internal " + nodeConnector.getId().toString());
removeNodeConnectorFromHost(hostsToMod, hostsToRem, nodeConnector);
hosts.removeAll(hostsToRem);
hosts.putAll(hostsToMod);
} else {
log.trace("NodeConnector is NOT internal " + nodeConnector.getId().toString());
//新建主机节点
Host host = new Host(addrs, nodeConnector);
if (hosts.containsKey(host.getId())) {
hosts.get(host.getId()).mergeHostWith(host);
} else {
hosts.put(host.getId(), host);
}
//创建主机与交换机的连接
List<Link> newLinks = hosts.get(host.getId()).createLinks(node);
if (newLinks != null) {
linksToAdd.addAll(newLinks);
}
//保存主机节点信息
hosts.submit(host.getId());
}
}
}
//保存link信息
writeDatatoMDSAL(linksToAdd, linksToRem);
}
在hosttracker当中实现主机发现功能,只有主机主动发出报文并经过openflow交换机以packetin形式上送控制器,该报文经过packethandler处理,再到addresstracker,最后由hosttracker写入数据库,该主机节点才能显示到web的拓扑上,因此当我们采用mininet模拟一个哑铃式网络,启动网络是在web的拓扑上只能看到两台交换机,并未出现主机节点,只有pingall之后才能出现主机节点。
由于篇幅有限,下篇继续分析Loopremover、Arphandler等模块。