Skip to content
jgrunert edited this page Oct 31, 2015 · 105 revisions

Welcome to the ZSDN-Controller wiki!

{{toc}}

h1. User Documentation

h2. What is ZSDN?

Zero Software Defined Networking(ZSDN) is a component based SDN controller. The software is fully distributed which means there are no central instances that control the whole network. Instead it consists of multiple modules that are connected by a ZMQ (see http://zeromq.org) message BUS. The communication between modules is handled with google protobuffers. The excecution of each module is controlled by an instance of the Zero Module Framework. Currently there exist two versions of this framework: The ZMF which was written in C++ and the JMF which was written in Java. Both of them support the OpenFlow versions 1.0 und 1.3.

ZSDN is published under the Apache License Version 2.0.

h2. Overview

The ZSDN ModuleFramework controls the execution of a ZSDN module and connects it to the ZSDN system via ZMQ. The ZMF is used by controller modules and switch adapter modules.

The following ZSDN modules are currently available: |Name|Summary|Dependencies on modules| |SwitchAdapter | Wraps an OpenFlow switch as a module. | - | |SwitchRegistryModule | Provides detailed information about all switches in the network | - | |DeviceModule | Knows about all devices in the network (excluding switches). | - | |ARPModule | Answers ARP-Requests directly to reduce flooding in the network. | DeviceModule | |SimpleForwardingModule | Packets are forwarded to their destinations using PacketOut messages. (Layer 2 forwarding) | DeviceModule | |LinkDiscoveryModule | Finds out about links between switches. | SwitchRegistryModule | |TopologyModule | Provides a full topology of the network, including switches and links between them. | SwitchRegistryModule, LinkDiscoveryModule | |SwitchAdapterServer | A module which listens on the openFlow port in order to start SwitchAdapter modules from incoming switch connections. | - | |StatisticsModule | Collects port- and tablestatistics from Switches | - | |ForwardingModule | Installs routes based on layer-2 info(dst-mac). | TopologyModule, DeviceModule | |FlowRegistryModule | Knows what flows are currently installed on all switches. | | |LoggingModule| Logs all zmf pub/sub traffic. | - | |RestAdminModule| provides REST-interface for enabling and disabling modules in the network| - |

As a developer, you may also be interested in the implementation details of these ZSDN Core-Modules.

h2. Installation

h3. Prerequisites

  • Install cmake ** sudo apt-get install software-properties-common ** sudo add-apt-repository ppa:george-edison55/cmake-3.x ** sudo apt-get update ** sudo apt-get install cmake build-essential

  • Install Java 8 ** sudo add-apt-repository ppa:webupd8team/java ** sudo apt-get update ** sudo apt-get install oracle-java8-installer

  • Install Maven ** sudo apt-get install maven

  • Install Mininet ** sudo apt-get install mininet

  • Install Git ** sudo apt-get install git

  • Clone Git Repository ** git clone

h3. Setup

Once you have downloaded or cloned the ZSDN repository to your local machine, switch to its root directory and run the init-zsdn.sh script. You can do this by opening an console in this folder and type the following:

./init-zsdn.sh 

This starts the installation process of ZSDN which may take some time. You will need to confirm several steps and to enter your sudo-password in the second half of the process. When the installation was succesfull you may run also the build-all.sh script at any time, which rebuilds the executables of the most important project and stores them to the /build directory. They can be used afterwards either by starting them manually via console oder by using our quick start tool.

h3. Cross Compilation

See [[Crosscompiling_for_ARMRaspberry_Pi|Cross Compiling for ARM RaspberryPi]]

h2. Quick Start

We provide a start-up tool with which you can easily start and stop instances of ZSDN modules. You can find the executable under /util/StartUpSelector Once the program is started you should see the UI with the following functions:

!{width:70%}start1.png!

1. Load Modules Here you can specify a path to a folder from which then all executables of ZSDN Modules are loaded into the application recursively (lists also modules in subdirectories).

2. Add If you click the button 'add' you also have the possibility to add another module to the list manually. You need to add the additional information on your own. For details see also how to edit an existing module.

!{width:70%}start2.png!

3. Edit In the picture above you can see the windows that appears if you want to edit existing module. It looks the same if you want to add a new one. As you can see you can add/change here the following configurations:

  • Module Name: The name the module should carry, for example 'SwitchAdapter'.
  • Description: Here you may add a description of this module that is displayed in the tool afterwards.
  • Path: The path to the folder containing the compiled executable of the module. If you add modules via 'Load Modules', this is filled in automatically.
  • Config File: Every modules needs a config text file which contains the specified configuration the module should be started with. You can find a default config file under /examples/example-configs/zmf_module.config.
  • Additional Parameters: Your module may need to be started with some additional parameters. You can add theme here, each seperated by ' '(space). If you wish no additional parameters, you can leave this field empty.

4. Remove Here you can remove an existing module from the list.

5. Start All Press this button to start all modules that are currently in the list. If starting was successful, they will change their state from 'Stopped' to 'Running'.

6. Stop All This shuts down every module that is currently running. Their state should then change from 'Running' to 'Stopped'.

7. Start This starts the specific module that you have selected from the list on the left side. Once it is running, you can see it's console- and logging output in the area above.

8. Stop This shuts down the selected module. You should see a message in the output area that the module has been stopped after you pressed this button.

Saving the list Additionally you can save the current module list over the File menu on top. It will be saved as an xml file which can be loaded again the next time you start the application.

We provide a default list containing all currently available core modules with their configurations named exampleSet.xml.

h2. Working With ZSDN

h3. Structure Of The Framework

!{width:50%}ZMF.png!

As the figure shows, the framework consists of several units: the ZMFCore, which encapsulates the main functionality of the framework such as starting and stopping of modules. Also it provides an interface that modules can use for accessing the framework and for connecting to other modules through it. These modules are always derived from the AbstractModule-class.

Furthermore, the ZMFCore controls the execution of two more essential services that are started and stopped with an instance of the ZMF. The first is called the PeerDiscoveryService which subscribes to a multicast group in the network to which all other ZMF instances also subscribe. The service frequently sends out so called heartbeats in form of udp-multicast messages. This way every instance of the ZMF knows about all other module instances that are available at this time and what state the currently have.

The ZMQMessagingService provides functionality to modules that can be used for sending direct ZMQ request messages to other modules and for receiving their replies in the same way. It can also be used to publish ZMQ messages to a certain topic or to receive messages from other modules through a preceding subscription to this topic.

Module applications call the run method of the ZMF framework (eg. from the main method) to start the ZMF instance.

For further information about ZMF see ZMF_Design.

Module Implementations

Currently there exist module implementations for the following ZSDN Core-Modules:

|/2.Name|\2=.ModuleTypeID|/2=.Summary| |HEX|Decimal| |SwitchAdapter |0x0000|0| Wraps an OpenFlow switch as a module. | |SwitchRegistryModule |0x0001|1| Provides detailed information about all switches in the network | |DeviceModule |0x0002|2| Knows about all devices in the network (excluding switches). | |LinkDiscoveryModule |0x0005|5| Finds out about links between switches. | |TopologyModule |0x0006|6| Provides a full topology of the network, including switches and links between them. | |StatisticsModule |0x0008|8| Collects port- and tablestatistics from Switches |

Furthermore we created these ZSDN Additional and Example Modules:

ZSDN C++ Modules:

|/2.Name|\2=.ModuleTypeID|/2=.Summary| |HEX|Decimal| |ARPModule |0x0003|3| Answers ARP-Requests directly to reduce flooding in the network. | |SimpleForwardingModule |0x0004|4| Packets are forwarded to their destinations using PacketOut messages. (Layer 2 forwarding) | |ForwardingModule |0x0009|9| Installs routes based on layer-2 info(dst-mac). |

Other Modules:

|/2.Name|\2=.ModuleTypeID|/2=.Summary| |HEX|Decimal| |Administration_and_Logging_Design|0x0010|16| Logs all zmf pub/sub traffic. | |Administration_and_Logging_Design|0x0011|17| provides REST-interface for enabling and disabling modules in the network | | Reserved for TeamZero |0x001*|16-31|Reserved for TeamZero | | Reserved for HPMOM |0x002*|32-47|Reserved for HPMOM |

h3. How To Write Your Own Module

All modules are derived from the AbstractModule-class. It provides a basic frame for each module. In the /util folder you find the script createNewModule.sh which you can run to create a new raw implementation of a module which you can extend afterwards. The script will ask you for the name the module should carry and the unique id it should have, which must be given as hex, e.g. 0x0042. Afterwards a folder with the provided module name will be created in the /modules/default folder. This folder will already contain a buildable project for your previously created module.

Every module can define prototype-topics, information can be found Module_Interface

There will be some methods you may want to overwrite to achieve a desired behaviour. Here are some proposals and examples how to do this:

h4. startup (enable())

First there is the enable()-method. This method is called when the module is enabled, meaning here is where code for start-up will have to be inserted. Here you can subscribe to another module if you want to react to their (specific) messages. As an example the subscription to an IPV4 message from an SwitchAdapter:

getZmf()->subscribe(switchadapter_topics::FROM().switch_adapter().openflow().packet_in().multicast_group(
                    i).ipv4().build(),
                                [this](const ZmfMessage& message, const ModuleUniqueId& sender) {
                                    // Do something in here (e.g. call a specific method like below)
                                    // handleIPv4SubscriptionMethod(message, sender);
                                });

Do not forget to include the specific topics file. For example for the switchadapter_topics used above you would have to include "#include <zsdn/topics/SwitchAdapter_topics.hpp>".

Another possibility is to request all existing modules of a certain kind that are enabled within ZMF, e.g. all SwitchAdapter-Instances

std::list> moduleList = getZmf()->getPeerRegistry()->getPeersWithType(
            zsdn::MODULE_TYPE_ID_SwitchAdapter, true);

h4. stopping the module (disable())

The disable()-method is called when the instance is disabled or stopped permanently. Every used memory space should be released in this method and running threads should be terminated for a clean shutdown.

h3. reacting to other modules

h4. publish/subscribe

You can subscribe to certain messages of other modules. The method you define will then be called every time u receive the message type you subscribed to. For an example on how to subscribe refer to Public Documentation#startup (enable()). The following example will show how to process an IPv4 message:

void YourModule::handleIPv4SubscriptionMethod(const zmf::data::ZmfMessage& message, const zmf::data::ModuleUniqueId& id) {
    // Unpack ZmfMessage which contains OpenFlow packet
    of_object_t* ofPacketIn = zsdn::of_object_new_from_data_string_copy(message.getData());

    of_octets_t ofPayload;
    of_packet_in_data_get(ofPacketIn, &ofPayload);
  
    uint8_t* payloadData = ofPayload.data;
    uint16_t payloadLength = ofPayload.bytes;
   
    Tins::EthernetII ethPacket(payloadData, payloadLength);
    of_packet_in_delete(ofPacketIn);
    
    //extract info from ethernetII
    Tins::PDU* innerPdu = ethPacket.inner_pdu();
    if (innerPdu == nullptr) {
        getLogger().warning("Received EthernetII Packet with invalid inner PDU.");
        return;
    }
    Tins::PDU::PDUType pduType = ethPacket.inner_pdu()->pdu_type();
    
    if(pduType==Tins::PDU::PDUType::IP) {//to be really safe that its ipv4
            Tins::IP* ipv4Packet = (Tins::IP*) ethPacket.inner_pdu();
            //now process the ipv4 the way you want
     }
}

h5. state changes (handleModuleStateChange())

The handleModuleStateChange()-method is called, when an other modules changes LifecycleState (meaning it is disabled, enabled or stopped). In this method one can define how to react to other modules. As an example we print a message every time any instance of SwitchAdapter changes its LifecycleState.

void YourModule::handleModuleStateChange(std::shared_ptr changedModule,
                                                   zmf::data::ModuleState newState,
                                                   zmf::data::ModuleState lastState) {
    if (changedModule.get()->UniqueId.TypeId == zsdn::MODULE_TYPE_ID_SwitchAdapter) {
            printf("A SwitchAdapter changed State")
     }
}

h5. request/reply (handleRequest())

The handleRequest()-method is called when an other module sends a request to your module. Here you have to parse the request and act depending on the request type. As an example here the processing of a dummy request:

zmf::data::ZmfOutReply YourModule::handleRequest(const zmf::data::ZmfMessage& message,
                                                           const zmf::data::ModuleUniqueId& sender) {
    YourModule_Proto::Request request;
    bool parseSuccess = request.ParseFromArray(message.getDataRaw(), message.getDataLength());
    if (!parseSuccess) {
        return zmf::data::ZmfOutReply::createNoReply();
    }
    switch (request.RequestMsg_case()) {
        case YourModule_Proto::Request::kThisIsARequest: {
            YourModule_Proto::Reply reply;
            //set the reply-data if necessary
            zmf::data::MessageType topicsdReply_ = yourmodule_topics::REPLY().your_module().this_is_a_request().build();
            return zmf::data::ZmfOutReply::createImmediateReply(
                    zmf::data::ZmfMessage(topicsReply_, reply.SerializeAsString()));
            break;
        }
    }
    return zmf::data::ZmfOutReply::createNoReply();
}

h4. communicating with other modules

h5. publish/subscribe

You can publish messages through the ZMF. Every module that is subscribed to your message type under which the message is published will automatically receive the message. Here an example of a module publishing a message:

    YourModule_Proto::From messageContainer;
    YouryModule_Proto::From_YourModuleMessage* yourModuleMessage = new YourModule_Proto::From_YourModuleMessage();

    //set the data in yourModuleMessage if necessary
    messageContainer.set_allocated_your_module_message(yourModuleMessage);

    zmf::data::MessageType topic_ = yourmodule_topics::FROM().your_module().your_module_message().build();
    getZmf()->publish(
            zmf::data::ZmfMessage(topic_, messageContainer.SerializeAsString()));

h5. request/reply

You can use requests to get information directly from a specific other module. Therefore you have to create a request and check read the data from the reply, which you receive. Here an example of a module doing a request:

    zmf::data::MessageType topic_ = othermodule_topics::REQUEST().ther_module().a_request_type().build();
    OtherModule_Proto::Request request;

    OtherModule_Proto::Request_ARequestType* aRequestType = new OtherModule_Proto::Request_ARequestType();
    request.set_allocated_a_request_type(aRequestType);


    OtherModule_Proto::Reply reply;
    zsdn::RequestUtils::RequestResult requestResult =
            zsdn::RequestUtils::sendRequest(*getZmf(), request, reply, topic_,
                                            zsdn::MODULE_TYPE_ID_OtherModule,
                                            0);

    switch (requestResult) {
        case zsdn::RequestUtils::SUCCESS: 
            // read data from reply and process it however needed
            break;

        case zsdn::RequestUtils::NO_MODULE_INSTANCE_FOUND:
            //what you want to do on this failure type
            break;

        case zsdn::RequestUtils::TIMEOUT:
            //what you want to do on this failure type
            break;

        case zsdn::RequestUtils::REQUEST_SERIALIZATION_FAILED:
            //what you want to do on this failure type
            break;

        case zsdn::RequestUtils::RESPONSE_PARSE_FAILED:
            //what you want to do on this failure type
            break;
    }

h2. Support

If you need help, don't hesitate to contact us: contact.zsdn@gmail.com

h2. Participate

You have own ideas or plans concerning ZSDN? Share them with us! Under the following links you may commit your pull requests:

ZMF: https://github.com/zeroSDN/ZMF/compare

JMF: https://github.com/zeroSDN/JMF/compare

ZeroSDN: https://github.com/zeroSDN/ZSDN-Controller/compare

If you are new to github, read here about how to use a pull request:

https://help.github.com/articles/using-pull-requests/

Clone this wiki locally