SDN控制器Floodlight源碼學習(三)--控制器和交換機交互(1)

之前學習了Floodlight控制器工作機制:

http://blog.csdn.net/crystonesc/article/details/69055341

今天來學習控制器怎樣與交換機進行交互的。
說到控制器如何來管理連接的交換機,Floodlight中有一個重要的類:
net.floodlightcontroller.core.internal.OFSwitchManager
以下爲該類的原版註釋:

The Switch Manager class contains most of the code involved with
dealing with switches. The Switch manager keeps track of the switches
known to the controller,their status, and any important information
about the switch lifecycle. The Switch Manager also provides the
switch service, which allows other modules to hook in switch listeners
and get basic access to switch information.

大致意思就是:Switch Manager的代碼主要涉及與交互機的交互,它能夠管理連接到Controller交換機的狀態以及生命週期的信息。同時提供一些對其它模塊的服務,使其它模塊也能夠獲取交換機的信息.
那麼Controller和交換機之間一定是客戶端和服務器端的模型,Controller作爲服務器端,交換機則爲客戶端。Floodlight使用Netty作爲通信的框架,我們趕緊看看服務器端的代碼:

@Override
    public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
        startUpBase(context);
        bootstrapNetty();
    }

1.Switch Manager首先作爲模塊在模塊加載的時候啓動

模塊加載介紹: http://blog.csdn.net/crystonesc/article/details/68483960
在startUp方法中,Switch Manager調用了bootstrapNetty()方法,啓動了服務器端,bootstrapNetty方法:

 public void bootstrapNetty() {
        try {
            bossGroup = new NioEventLoopGroup(bossThreads);
            workerGroup = new NioEventLoopGroup(workerThreads);

            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_SNDBUF, tcpSendBufferSize)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMsec)
                    .option(ChannelOption.SO_BACKLOG, connectionBacklog);

            OFChannelInitializer initializer = new OFChannelInitializer(
                    this, 
                    this, 
                    debugCounterService, 
                    timer, 
                    ofBitmaps, 
                    defaultFactory, 
                    keyStore, 
                    keyStorePassword);

            bootstrap.childHandler(initializer);

            cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

            Set<InetSocketAddress> addrs = new HashSet<InetSocketAddress>();
            if (openFlowAddresses.isEmpty()) {
                cg.add(bootstrap.bind(new InetSocketAddress(InetAddress.getByAddress(IPv4Address.NONE.getBytes()), openFlowPort.getPort())).channel());
            } else {
                for (IPv4Address ip : openFlowAddresses) {
                    addrs.add(new InetSocketAddress(InetAddress.getByAddress(ip.getBytes()), openFlowPort.getPort()));
                }
            }

            for (InetSocketAddress sa : addrs) {
                cg.add(bootstrap.bind(sa).channel());
                log.debug("Listening for switch connections on {}", sa);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

2.這裏不對Netty框架的知識做介紹,只需要明白代碼中做了些什麼
首先建立了一個NioServerSocketChannel,它可以理解爲Controller和交換機之間的通道.
然後需要給NioServerSocketChannel設置一個ChannelPipeline,在ChannelPipeline中加入ChannelHandler,真正對IO事件進行處理的是ChannelHandler
借用《Netty權威指南 第二版》中的圖來說明下:
這裏寫圖片描述
現在我們可以看看控制器中設置了那些ChannelHandler,我們調轉到
net.floodlightcontroller.core.internal.OFChannelInitializer的initChannel方法中:

pipeline.addLast(PipelineHandler.OF_MESSAGE_DECODER,
                new OFMessageDecoder());
pipeline.addLast(PipelineHandler.OF_MESSAGE_ENCODER,
                new OFMessageEncoder());
pipeline.addLast(PipelineHandler.MAIN_IDLE,
                new IdleStateHandler(PipelineIdleReadTimeout.MAIN,
                        PipelineIdleWriteTimeout.MAIN,
                        0));
pipeline.addLast(PipelineHandler.READ_TIMEOUT, new ReadTimeoutHandler(30));
pipeline.addLast(PipelineHandler.CHANNEL_HANDSHAKE_TIMEOUT,
                new HandshakeTimeoutHandler(
                        handler,
                        timer,
                        PipelineHandshakeTimeout.CHANNEL));
pipeline.addLast(PipelineHandler.CHANNEL_HANDLER, handler);

其中最後一個handler,OFChannelHandler真正用於交換機與控制器的IO通信處理.
OFChannelHandler繼承SimpleChannelInboundHandler,用於在IO不同的階段進行不同的處理,那麼我們來看看OFChannelHandler是怎麼樣來工作的,首先它裏面定義了一個抽象狀態類OFChannelState,那麼在IO的不同階段通過將不同的子類設置到state來完成與交換機的交互.
在閱讀代碼前先看下調用關係,方便理解:
這裏寫圖片描述
以下爲主要代碼:

class OFChannelHandler extends SimpleChannelInboundHandler<Iterable<OFMessage>> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.debug("channelConnected on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this)));
        counters.switchConnected.increment();
        channel = ctx.channel();
        log.info("New switch connection from {}", channel.remoteAddress());
        setState(new WaitHelloState());
    }
    class WaitHelloState extends OFChannelState {

        WaitHelloState() {
            super(false);
        }

        @Override
        void processOFHello(OFHello m) throws IOException {
            OFVersion theirVersion = m.getVersion();
            OFVersion commonVersion = null;
            /* First, check if there's a version bitmap supplied. WE WILL ALWAYS HAVE a controller-provided version bitmap. */
            if (theirVersion.compareTo(OFVersion.OF_13) >= 0 && !m.getElements().isEmpty()) {
                List<U32> bitmaps = new ArrayList<U32>();
                List<OFHelloElem> elements = m.getElements();
                /* Grab all bitmaps supplied */
                for (OFHelloElem e : elements) {
                    if (e instanceof OFHelloElemVersionbitmap) {
                        bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps());
                    } else {
                        log.warn("Unhandled OFHelloElem {}", e);
                    }
                }
                /* Lookup highest, common supported OpenFlow version */
                commonVersion = computeOFVersionFromBitmap(bitmaps);
                if (commonVersion == null) {
                    log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.remoteAddress());
                    channel.disconnect();
                    return;
                } else {
                    log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.remoteAddress());
                    factory = OFFactories.getFactory(commonVersion);
                    OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
                    decoder.setVersion(commonVersion);
                }
            }
            /* If there's not a bitmap present, choose the lower of the two supported versions. */
            else if (theirVersion.compareTo(factory.getVersion()) < 0) {
                log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.remoteAddress());
                factory = OFFactories.getFactory(theirVersion);
                OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
                decoder.setVersion(theirVersion);
            } /* else The controller's version is < or = the switch's, so keep original controller factory. */
            else if (theirVersion.equals(factory.getVersion())) {
                log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.remoteAddress());
            }
            else {
                log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.remoteAddress());
            }

            setState(new WaitFeaturesReplyState());
        }

        @Override
        void enterState() throws IOException {
            sendHelloMessage();
        }
    }
    class WaitFeaturesReplyState extends OFChannelState {
        WaitFeaturesReplyState() {
            super(false);
        }
        @Override
        void processOFFeaturesReply(OFFeaturesReply  m)
                throws IOException {
            featuresReply = m;

            featuresLatency = (System.currentTimeMillis() - featuresLatency) / 2;

            // Mark handshake as completed
            setState(new CompleteState());

        }

        @Override
        void processOFHello(OFHello m) throws IOException {
            /*
             * Brocade switches send a second hello after
             * the controller responds with its hello. This
             * might be to confirm the protocol version used,
             * but isn't defined in the OF specification.
             * 
             * We will ignore such hello messages assuming
             * the version of the hello is correct according
             * to the algorithm in the spec.
             * 
             * TODO Brocade also sets the XID of this second
             * hello as the same XID the controller used.
             * Checking for this might help to assure we're
             * really dealing with the situation we think
             * we are.
             */
            if (m.getVersion().equals(factory.getVersion())) {
                log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.remoteAddress(), state.toString());
            } else {
                super.processOFHello(m); /* Versions don't match as they should; abort */
            }
        }

        @Override
        void processOFPortStatus(OFPortStatus m) {
            log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.remoteAddress());
        }

        @Override
        void enterState() throws IOException {
            sendFeaturesRequest();
            featuresLatency = System.currentTimeMillis();
        }

        @Override
        void processOFMessage(OFMessage m) throws IOException {
            if (m.getType().equals(OFType.PACKET_IN)) {
                log.warn("Ignoring PACKET_IN message from {} during OpenFlow channel establishment.", channel.remoteAddress());
            } else {
                super.processOFMessage(m);
            }
        }
    };
    class CompleteState extends OFChannelState{

        CompleteState() {
            super(true);
        }

        @Override
        void enterState() throws IOException{

            setSwitchHandshakeTimeout();

            // Handle non 1.3 connections
            if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0){
                connection = new OFConnection(featuresReply.getDatapathId(), factory, channel, OFAuxId.MAIN, debugCounters, timer);
            }
            // Handle 1.3 connections
            else {
                connection = new OFConnection(featuresReply.getDatapathId(), factory, channel, featuresReply.getAuxiliaryId(), debugCounters, timer);

                // If this is an aux connection, we set a longer echo idle time
                if (!featuresReply.getAuxiliaryId().equals(OFAuxId.MAIN)) {
                    setAuxChannelIdle();
                }
            }

            connection.updateLatency(U64.of(featuresLatency));
            echoSendTime = 0;

            // Notify the connection broker
            notifyConnectionOpened(connection);
        }
    };
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Iterable<OFMessage> msgList) throws Exception {
        for (OFMessage ofm : msgList) {
            try {
                // Do the actual packet processing
                state.processOFMessage(ofm);
            }
            catch (Exception ex) {
                // We are the last handler in the stream, so run the
                // exception through the channel again by passing in
                // ctx.getChannel().
                ctx.fireExceptionCaught(ex);
            }
        }
    }
    private void sendFeaturesRequest() throws IOException {
        // Send initial Features Request
        OFFeaturesRequest m = factory.buildFeaturesRequest()
                .setXid(handshakeTransactionIds--)
                .build();
        write(m);
    }

    private void sendHelloMessage() throws IOException {
        // Send initial hello message

        OFHello.Builder builder = factory.buildHello();

        /* Our highest-configured OFVersion does support version bitmaps, so include it */
        if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) {
            List<OFHelloElem> he = new ArrayList<OFHelloElem>();
            he.add(factory.buildHelloElemVersionbitmap()
                    .setBitmaps(ofBitmaps)
                    .build());
            builder.setElements(he);
        }

        OFHello m = builder.setXid(handshakeTransactionIds--)
                .build();

        write(m);
        log.debug("Send hello: {}", m); 
    }
}

可以看出OFChannelHandler主要用於處理Controller與交換機之間Hello,Feature,Echo等的報文,建立它們之間的鏈接,最後將數據傳遞給上層的OFSwitchHandshakeHandler進行處理,有後者對交換機的鏈接和報文進行管理和分發,下一節我們將繼續沿着數據報文的路線,介紹OFSwitchHandshakeHandler。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章