sending a message from controller1 to controller2
Asked Answered
N

4

6

I have a topology in mininet which consists of 2 floodlight controllers (c1 and c2), a switch (s1) which is connected to c1 and 2 hosts (h1 and h2) that are connected to this switch. I'm writing a program in which when c1 receives an ICMP packet from s1, it will send a Hello message to c2.

I'm using this tutorial for this purpose which says:

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.

Here is the complete code of my module:

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;
    }
}

But after running this code, when c1 receives an ICMP packet, I encounter multiple errors:

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$24(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$9(OFChannelHandler.java:578) [floodlight.jar:1.2-SNAPSHOT]

What's the problem? There seems to be something wrong with recv() function. Here is the code of in-built send() and receive functions.

send():

/**
 * 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 "";
    }

}

The complete code of NetworkNode module where this send() and recv() function are located, is here and the complete package of High availability support is here (In case it's needed)

Namhoi answered 12/9, 2018 at 20:22 Comment(2)
Please post the full class code for Controller. Based on the error log, it looks like a null pointer exception is being thrown in this class.Jackquelinejackrabbit
@Jackquelinejackrabbit I added the full code.Namhoi
N
0

The problem with this code is that receive() method is called or (let's say) is active when the switch sends a new packet to the controller.

Here when the first controller receives an ICMP packet, it sends a hello message to the second controller through this piece of the code:

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

But since the second controller has not received any message from the switch, it is not implementing this piece of the code (receive()) at the moment and does not see:

hacontroller.recv("127.0.0.1:4242");

And as far as I can understand, this is the reason why clientSock is never initialized and so I'm receiving this error.

Namhoi answered 22/9, 2018 at 17:31 Comment(0)
N
0

From the logs you posted and the Github repository you made available, it seems that clientSock is null. A first NullPointerException is being thrown in line 529:

response = clientSock.recv();

and being catch.

But then in the catch block, clientSock is still null, so when you do:

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

a second NullPointerException is thrown (the one we see in the logs) hiding the one that was thrown before.

Can you debug your code to verify if socketDict contains receivingPort? If not, be sure to initialize it properly.

Also, it is usually not a good idea to catch Exception cause it is too large. I would advice you to catch a more precise exception (or exceptions) if you can. If you did that you would have seen the source of this bug more easily.

Neutralization answered 15/9, 2018 at 21:28 Comment(0)
J
0

The first null pointer exception being thrown in NetworkNode.recv() is because clientSock is never initialized. The null comparison on line 535 can't be checked because clientSock doesn't exist and therefore the method getSocketChannel() cannot be called.

Multiple classes are lacking constructor methods to initialize all variables. This seems to be where most of the issues are based. Make sure all init() and preStart() methods are called too. It looks like most variable initialization is taking place in these methods.

Jackquelinejackrabbit answered 15/9, 2018 at 21:30 Comment(0)
A
0

There are multiple issues in NetworkNode.recv() method:

  • You should check for null before dereferencing an object.
  • You should not call toString on a String object. It is redundant.
  • You should be careful while catching and ignoring exceptions. In this case, you do not need to catch Exception as NioClient.recv() does not throw any. Where that method should suppress exception, is another issue.
  • Avoid using member variables instead of local variables. local variables need to be declared in each method. This is a common issue in many places in the code.

The receive method may be rewritten as follows:

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;
}
Aristides answered 15/9, 2018 at 21:50 Comment(0)
N
0

The problem with this code is that receive() method is called or (let's say) is active when the switch sends a new packet to the controller.

Here when the first controller receives an ICMP packet, it sends a hello message to the second controller through this piece of the code:

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

But since the second controller has not received any message from the switch, it is not implementing this piece of the code (receive()) at the moment and does not see:

hacontroller.recv("127.0.0.1:4242");

And as far as I can understand, this is the reason why clientSock is never initialized and so I'm receiving this error.

Namhoi answered 22/9, 2018 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.