SDN控制器Floodlight源码学习(三)--控制器和交换机交互(1)

来源:互联网 发布:开淘宝的经验分享 编辑:程序博客网 时间:2024/06/05 14:52

之前学习了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。

1 1