从 controller1 发送消息到 controller2

sending a message from controller1 to controller2

我在 mininet 中有一个拓扑结构,它由 2 个泛光灯控制器(c1 和 c2)、一个连接到 c1 的交换机 (s1) 和连接到该交换机的 2 个主机(h1 和 h2)组成。我正在编写一个程序,当 c1 从 s1 收到一个 ICMP 数据包时,它会向 c2 发送一条 Hello 消息。

我正在使用这个 tutorial 用于此目的,上面写着:

Messages can be sent from one controller to another using the send function, and the messages have to be tagged with an ‘m’ “m”. You will send this message TO a particular controller so the TO address comprises of two parts IP:port. The IP is the machine IP address of the other controller (HAServer is listening on all ips), and the port is the corresponding listening port of HAServer on that machine.

By default, HAServer on controller 1 is listening on 4242, on controller 2 on 4243, on controller 3 on 4244 … and so on.

recv() function is similar to the send function and you will be giving the FROM address to hear back FROM a particular controller. The from address also comprises of two parts, IP:port. The IP is the machine IP address of the other controller (HAServer is listening on all ips), and the port is the corresponding listening port of HAServer on that machine.

Ideally, this function is called after calling a corresponding send() function, otherwise, a connection might not have been established, and it will just return an error.

这是我的模块的完整代码:

package net.floodlightcontroller.mactracker;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.hasupport.IHAControllerService;
import net.floodlightcontroller.hasupport.NetworkNode;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.ICMP;
import net.floodlightcontroller.packet.IPv4;

import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IpProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Mactracker implements IFloodlightModule, IOFMessageListener {

    protected static IHAControllerService hacontroller;
    protected static Logger logger = LoggerFactory.getLogger(Mactracker.class);
    protected IFloodlightProviderService floodlightProvider;
    protected Set<Long> macAddresses;
    private static NetworkNode network;

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
        // TODO Auto-generated method stubs
        return null;
    }

    @Override
    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
        // TODO Auto-generated method stub
        Collection<Class<? extends IFloodlightService>> l =
                new ArrayList<Class<? extends IFloodlightService>>();
            l.add(IFloodlightProviderService.class);
            l.add(IHAControllerService.class);
        return l;
    }

    @Override
    public void init(FloodlightModuleContext context) throws FloodlightModuleException {
        // TODO Auto-generated method stub
        hacontroller = context.getServiceImpl(IHAControllerService.class);
        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
        macAddresses = new ConcurrentSkipListSet<Long>();
    }

    @Override
    public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
        // TODO Auto-generated method stub
        floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
        // After more than 51% of configured controllers are started, this function will return,
        // or when a timeout of 60s is reached, whichever is earlier.
        hacontroller.pollForLeader();       
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return Mactracker.class.getSimpleName();
    }

    @Override
    public boolean isCallbackOrderingPrereq(OFType type, String name) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCallbackOrderingPostreq(OFType type, String name) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg,
            FloodlightContext cntx) {
        // TODO Auto-generated method stub
        Ethernet eth =
                IFloodlightProviderService.bcStore.get(cntx,
                                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
        if (eth.getEtherType() == EthType.IPv4) {
            IPv4 ipv4 = (IPv4) eth.getPayload();

            if ( ipv4.getProtocol().equals(IpProtocol.ICMP)){
                logger.warn ("ICMP Packet Received!:-)");
                ICMP icmp = (ICMP) ipv4.getPayload();
                logger.warn ("icmp.getIcmpType: "+icmp.getIcmpType());

                hacontroller.send("127.0.0.1:4243", "mHelloWorld");
                hacontroller.recv("127.0.0.1:4242");
            }
        }

        Long sourceMACHash = eth.getSourceMACAddress().getLong();
        if (!macAddresses.contains(sourceMACHash)) {
            macAddresses.add(sourceMACHash);
            logger.info("MAC Address: {} seen on switch: {}",
                    eth.getSourceMACAddress().toString(),
                    sw.getId().toString());
        }
        return Command.CONTINUE;
    }
}

但是在运行这段代码之后,当c1收到一个ICMP数据包时,我遇到了多个错误:

2018-09-13 00:39:56.716 WARN  [n.f.m.Mactracker] ICMP Packet Received!:-)
2018-09-13 00:39:56.716 WARN  [n.f.m.Mactracker] icmp.getIcmpType: 0
2018-09-13 00:39:56.716 INFO  [n.f.h.NetworkNode] [NetworkNode] Sending: mHelloWorld sent through port: 127.0.0.1:4243
2018-09-13 00:39:56.720 WARN  [i.n.c.DefaultChannelPipeline] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.lang.NullPointerException: null
    at net.floodlightcontroller.hasupport.NetworkNode.recv(NetworkNode.java:535) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.hasupport.HAController.recv(HAController.java:190) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.mactracker.Mactracker.receive(Mactracker.java:121) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.Controller.handleMessage(Controller.java:411) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchManager.handleMessage(OFSwitchManager.java:487) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.dispatchMessage(OFSwitchHandshakeHandler.java:1752) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.access(OFSwitchHandshakeHandler.java:1751) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler$MasterState.processOFPacketIn(OFSwitchHandshakeHandler.java:1488) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler$OFSwitchHandshakeState.processOFMessage(OFSwitchHandshakeHandler.java:839) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.processOFMessage(OFSwitchHandshakeHandler.java:1790) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.messageReceived(OFSwitchHandshakeHandler.java:1964) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFConnection.messageReceived(OFConnection.java:414) ~[floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFChannelHandler.sendMessageToConnection(OFChannelHandler.java:579) [floodlight.jar:1.2-SNAPSHOT]
    at net.floodlightcontroller.core.internal.OFChannelHandler.access(OFChannelHandler.java:578) [floodlight.jar:1.2-SNAPSHOT]

有什么问题吗? recv() 函数似乎有问题。这是内置发送()和接收功能的代码。

发送():

/**
 * Sends a message to a specified client IP:port, if possible.
 *
 * @return boolean value that indicates success or failure.
 */

@Override
public Boolean send(String clientPort, String message) {
    if (message.equals(null)) {
        return Boolean.FALSE;
    }

    clientSock = socketDict.get(clientPort);
    try {
        logger.info("[NetworkNode] Sending: "+message+" sent through port: "+clientPort.toString());
        clientSock.send(message);
        return Boolean.TRUE;

    } catch (Exception e) {
        if (clientSock.getSocketChannel() != null) {
            clientSock.deleteConnection();
        }
        logger.debug("[NetworkNode] Send Failed: " + message + " not sent through port: " + clientPort.toString());
        return Boolean.FALSE;
    }
}

recv():

/**
 * Receives a message from the specified IP:port, if possible.
 *
 * @return String containing the received message.
 */

@Override
public String recv(String receivingPort) {
    clientSock = socketDict.get(receivingPort);
    try {
        response = clientSock.recv();
        response.trim();
        logger.info("[NetworkNode] Recv on port:"+receivingPort.toString()+response);
        return response;
    } catch (Exception e) {
        if (clientSock.getSocketChannel() != null) {
            clientSock.deleteConnection();
        }
        logger.debug("[NetworkNode] Recv Failed on port: " + receivingPort.toString());
        return "";
    }

}

这个send()和recv()函数所在的NetworkNode模块的完整代码是here and the complete package of High availability support is here(以备不时之需)

根据您发布的日志和您提供的 Github 存储库,clientSock 似乎是 null。第一个 NullPointerException 在第 529 行抛出:

response = clientSock.recv();

被抓住了。

但是在 catch 块中,clientSock 仍然是 null,所以当您这样做时:

if (clientSock.getSocketChannel() != null) {

第二次 NullPointerException 被抛出(我们在日志中看到的那个)隐藏了之前抛出的那个。

你能调试你的代码来验证 socketDict 是否包含 receivingPort 吗?如果没有,请务必正确初始化它。

此外,捕获 Exception 通常不是一个好主意,因为它太大了。如果可以的话,我会建议您捕获更精确的异常(或异常)。如果你这样做了,你会更容易地找到这个错误的来源。

NetworkNode.recv()中抛出的第一个空指针异常是因为clientSock从未被初始化。无法检查第 535 行的 null 比较,因为 clientSock 不存在,因此无法调用方法 getSocketChannel()

多个类缺少初始化所有变量的构造方法。这似乎是大多数问题的基础。确保也调用了所有 init()preStart() 方法。看起来大部分变量初始化都是在这些方法中进行的。

NetworkNode.recv() 方法中存在多个问题:

  • 您应该在取消引用对象之前检查是否为 null。
  • 您不应在 String 对象上调用 toString。这是多余的。
  • 捕获和忽略异常时要小心。在这种情况下,您不需要捕获异常,因为 NioClient.recv() 不会抛出任何异常。该方法应该在何处抑制异常是另一个问题。
  • 避免使用成员变量代替局部变量。局部变量需要在每个方法中声明。这是代码中很多地方的常见问题。

接收方法可以重写如下:

NioClient receivingSock = socketDict.get(receivingPort);
if (receivingSock == null) {
    logger.debug("[NetworkNode] No receivingSock on receivingport: " + receivingPort);
    return "";
}
else {
    response = receivingSock.recv();
    if (response != null) {
        response.trim();
    }
    return response;
}

此代码的问题在于当交换机向控制器发送新数据包时调用 receive() 方法或(假设)处于活动状态。

这里当第一个控制器收到一个ICMP数据包时,它通过这段代码向第二个控制器发送一个hello消息:

 hacontroller.send("127.0.0.1:4243", "mHelloWorld");

但是由于第二个controller还没有收到来自switch的任何消息,所以它目前没有实现这段代码(receive()),也看不到:

hacontroller.recv("127.0.0.1:4242");

据我所知,这就是 clientSock 从未初始化的原因,所以我收到此错误。