Floodlight各模块处理PacketIn消息的顺序

  当Controller和SW建立连接之后,就可以处理来自SW的各种OF msg。当接收到 packetin 消息之后,会将其分发给各个监听了这个OFMessage的listeners,所以如果我们要设计自己的控制器模块,只需要实现相应的接口方法,约定执行顺序即可。接口IListener主要抽象了监听器模块的名字,执行顺序,接口IOFMessageListener则抽象了我们的Controller如何处理具体的这个openflow消息。

   在Floodlight中所有监听OpenFlow消息的模块都需要实现IOFMessageListener接口,在Eclipse里打开IOFMessageListener,Type Hierarchy窗口可以看到所有监听OpenFlow消息的类, 它们之间执行的先后顺序如下图:
 
   这里写图片描述

通过阅读代码来判断这些模块处理packetin消息的相对顺序。

public interface IListener<T> {
    public enum Command {
        CONTINUE , STOP
    }

    //这个监听者的名字
    public String getName();

    //名为name的module在处理这个消息的时候,要在这个module之前
    public boolean isCallbackOrderingPrereq(T type, String name);

    //在处理type消息的时候,name_module 要在这个模块之后
    public boolean isCallbackOrderingPostreq(T type, String name);
}

public interface IOFMessageListener extends IListener< OFType> {
      //Floodlight利用这个方法呼叫这些listeners来处理这个OF MSG;
       public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx);
}

接下来看看具体细节。
1.Divice manager 要在topology之前。

    @Override
    public String getName() {
        return "devicemanager" ;
    }

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        return ((type == OFType.PACKET_IN || type == OFType.FLOW_MOD)
                && name.equals( "topology" ));
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        return false ;
    }

    @Override
    public Command receive(IOFSwitch sw, OFMessage msg,FloodlightContext cntx) {
        switch (msg.getType()) {
            case PACKET_IN:
                return this .processPacketInMessage(sw,(OFPacketIn) msg, cntx);
        }

        logger.error("received an unexpected message {} from switch {}",
                     msg, sw);
        return Command.CONTINUE;
    }

2. LinkDiscoveryManager 中没有做出对顺序的规定,由其他模块来约束的。

     @Override
         public String getName() {
             return "linkdiscovery" ;
         }

         @Override
         public Command receive( IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
             switch (msg.getType()) {
                 case PACKET_IN:
                     return this .handlePacketIn(sw, (OFPacketIn) msg, cntx);
                 case PORT_STATUS:
                     return this .handlePortStatus(sw, (OFPortStatus) msg);
             }

             log.error( "Received an unexpected message {} from switch {}", msg, sw);
             return Command.CONTINUE;
         }
  1. TopologyManager要在 LinkDiscoveryManager 之后操作。
  @Override
    public String getName() {
        return "topology" ;
    }

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        return "linkdiscovery" .equals(name);
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        return false ;
    }

    @Override
    public Command receive(IOFSwitch sw, OFMessage msg,
                           FloodlightContext cntx) {
        switch (msg.getType()) {
            case PACKET_IN:
                return this .processPacketInMessage(sw,
                                                   (OFPacketIn) msg, cntx);
        }

        log.error("received an unexpected message {} from switch {}",
                  msg, sw);
        return Command.CONTINUE;
    }

4.OFMessageFilterManager 模块在处理packetin消息时的顺序在 DeviceManager 之后,LearningSwitch 之前。这个模块的作用是允许我们添加一些过滤规则。

     @Override
         public String getName() {
             return "messageFilterManager" ;
         }

         @Override
         public boolean isCallbackOrderingPrereq(OFType type, String name) {
             return (type == OFType.PACKET_IN && name.equals("devicemanager" ));
         }

         @Override
         public boolean isCallbackOrderingPostreq(OFType type, String name) {
             return (type == OFType.PACKET_IN && name.equals("learningswitch" ));
         }

         @Override
         public Command receive( IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {

             if (filterMap == null || filterMap.isEmpty()) return Command.CONTINUE;

             HashSet<String> matchedFilters = null;
             if (log.isDebugEnabled()) {
                 log.debug( "Received packet {} from switch {}", msg, sw.getStringId());
             }

             matchedFilters = getMatchedFilters(msg, cntx);
             if (matchedFilters == null) {
                 return Command.CONTINUE;
             } else {
                 try {
                     sendPacket(matchedFilters, sw, msg, cntx, true );
                 } catch (TException e) {
                     log.error( "sendPacket Texception: {}", e);
                 } catch (Exception e) {
                     log.error( "sendPacket exception: {}", e);
                 }
             }

             return Command.CONTINUE;
         }

5. VirtualNetworkFilter(二层虚拟网络模块,对于不属于这个虚拟网络的packetin会丢弃)的执行顺序在forwarding之前,在devicemanager,linkdiscoveryManager之后。

  @Override
    public String getName() {
        return "virtualizer" ;
    }

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        // Link discovery should go before us so we don't block LLDPs
        return (type.equals(OFType.PACKET_IN) &&
              (name.equals( "linkdiscovery" ) || (name.equals("devicemanager" ))));
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        // We need to go before forwarding
        return (type.equals(OFType.PACKET_IN) && name.equals("forwarding" ));
    }

    @Override
    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
        switch (msg.getType()) {
            case PACKET_IN:
                return processPacketIn(sw, (OFPacketIn)msg, cntx);
        }
        log.warn("Received unexpected message {}" , msg);
        return Command.CONTINUE;
    }

6.ForwardingBase 路由模块未对顺序做限制。
7.其他的没有查看。

附:模块名称和类名对照表:
这里写图片描述

  isCallbackOrderingPrereq方法和isCallbackOrderingPostreq方法是用来控制模块调用顺序的,这两个方法的参数都是T type和String name。其中type是泛型参数,在IOFMessageListener中已被声明为OFType,也就是使用getName方法得到的字符串,用来标示一个消息监听模块。
  如果将模块A的名称传给模块B的isCallbackOrderingPrereq方法,且该方法返回True,那就表示模块A要在模块B之前执行,如果返回False那就表示模块A不在乎自己是否在模块B之前执行;同样,如果将模块A的名称传给模块B的isCallbackOrderingPostreq方法,且该方法返回True,那就表示模块A要在模块B之后执行,如果返回False那就表示模块A不在乎自己是否在模块B之后执行。在有些模块中这两个方法都返回False,那就表示这个模块不在乎自己在PacketIn处理序列中的顺序,比如linkdiscovery模块,但这并不能表明该模块可以处于PacketIn处理序列中的任何位置,例如在topology模块中就明确指明linkdiscovery要在topology模块之前执行。所以必须遍历所有实现了IOFMessageListener的类才能确定各个模块的执行顺序。
   遍历完所有实现了IOFMessageListener的类之后,Floodlight需要两个步骤来确定所有模块的执行顺序:

1)寻找Terminal module

Terminal module是指没有任何模块在需要在其后面执行,此类模块并不执行顺序的限制,需要注意的是Terminal module不止一个。伪代码如下:

for(int i=0; i<modules.size; i++){ 
isTerminal = true;
for(int j=0;j<modules.size;j++){ 
if( modules[j] is after modules[i]){ 
isTerminal = false; break; } }
if(isTerminal) 
terminalQueue.add(modules[i]); }

只要任何一个模块需要在modules[i]之后执行,modules[i]就不是最后执行的模块。

2)在每个最后执行的模块上执行DFS算法以建立执行顺序,伪代码如下:

function dfs(){
  for(int i=0; i<terminalQueue.size(); i++){
visit(terminalQueue[i]); } } 
function visit(listener){
  if(!visted.contain(listener){
visted.add(listener)
for(int i=0;i<modules.size();i++){ 
if( modules[i] is before listener) 
visit(modules[i])
}
orderingQueue.add(listener); 
} }

 到这里就讲完了,如果要为Floodlight添加模块,就应该先想清楚自己的模块应该在什么时候运行,然后再实现isCallbackOrderingPrereq方法和isCallbackOrderingPostreq即可。

   前面通过阅读代码知道了如何判断各个模块处理某个消息的先后顺序,那么内部是如何实现的呢?
  每当一个模块表示对一个消息感兴趣的时候,就会调用IFloodlightProviderService(具体有Controller类实现)的addOFMessageListener方法进行注册订阅,核心工作是由ListenerDispatcher类来完成:
   1)每次增加一个观察者的时候都会判断其是否是终结点(也就是不被其他的listener所依赖),因为最终确定这些观察者顺序的时候就是由这些终结点开始往前进行DFS遍历而得到;
   2)比如说ForwardingBase和Distributing (我们自己加的,没有约束其顺序),当它们注册packetin消息的时候,会加入到终结点集合terminals中,所以从它们开始深度遍历的时候得到的有序集合ordering=linkdiscovery,topology,devicemanager, forwarding, distributing(这里进行了两次DFS traverse)。接下来看代码:

————-Controller中实现IFloodlightProviderService的方法

         @Override
         public synchronized void addOFMessageListener(OFType type,
                                                       IOFMessageListener listener) {
                //先判断与type对应的 ListenerDispatcher对象是否存在
             ListenerDispatcher< OFType, IOFMessageListener> ldd =
                 messageListeners.get(type);
             if (ldd == null ) {
                 ldd = new ListenerDispatcher< OFType, IOFMessageListener>();
                 messageListeners.put(type, ldd);
             }
             //注册监听type这个消息;
             ldd.addListener(type, listener);
         }
--------------ListenerDispatcher实现(维护这些观察者,有依赖关系)

public class ListenerDispatcher <U, T extends IListener<U>> {
    protected static Logger logger = LoggerFactory.getLogger(ListenerDispatcher. class );
    List<T> listeners = null;
    //每个OF msg都有唯一的ListenerDispatcher对象,观察者存在listeners链表中

    //从listener这个观察者开始,依据有没有监听者在他之前,进行深度优先遍历
    //最终有序序列存在ordering中;visited用于存已经访问过的terminal listener。
    private void visit(List<T> newlisteners, U type, HashSet<T> visited,
                       List<T> ordering, T listener) {
        if (!visited.contains(listener)) {
            visited.add(listener);

            for (T i : newlisteners) {
                if (ispre(type, i, listener)) {
                    visit(newlisteners, type, visited, ordering, i);
                }
            }
           ordering.add(listener);
        }
    }

    //判断观察者l1 是否在 l2 之前(每个观察者实现了IListener接口)
    private boolean ispre(U type, T l1, T l2) {
        return (l2.isCallbackOrderingPrereq(type, l1.getName()) ||
                l1.isCallbackOrderingPostreq(type, l2.getName()));
    }

    //订阅type消息;
    public void addListener(U type, T listener) {
        List<T> newlisteners = new ArrayList<T>();
        if (listeners != null)
            newlisteners.addAll( listeners );

        newlisteners.add(listener);
        // Find nodes without outgoing edges
        List<T> terminals = new ArrayList<T>();
        for (T i : newlisteners) {
            boolean isterm = true;
            for (T j : newlisteners) {
                if (ispre(type, i, j)) {
                    isterm = false ;
                    break ;
                }
            }
            if (isterm) {
                terminals.add(i); //维护终节点集合
            }
        }

        if (terminals.size() == 0) {
            logger .error("No listener dependency solution: " +
                        "No listeners without incoming dependencies");
            listeners = newlisteners;
            return ;
        }

        //接下来得到有序的listeners;
        // visit depth-first traversing in the opposite order from
        // the dependencies.  Note we will not generally detect cycles
        HashSet<T> visited = new HashSet<T>();
        List<T> ordering = new ArrayList <T>();
        for (T term : terminals) {
            visit(newlisteners, type, visited, ordering, term);
        }
        listeners = ordering;
    }

    //观察者退出,为何不直接remove??
    public void removeListener(T listener) {
        if (listeners != null) {
            List<T> newlisteners = new ArrayList<T>();
            newlisteners.addAll( listeners );
            newlisteners.remove(listener);
            listeners = newlisteners;
        }
    }

    //清除所有listeners;
    public void clearListeners() {
        listeners = new ArrayList<T>();
    }

    //
    public List<T> getOrderedListeners() {
        return listeners ;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值