Skip to content
AndreKutzleb edited this page Nov 9, 2015 · 17 revisions

Core-Modules

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

Other Modules

| Name | ModuleTypeID | Summary | --- | --- | --- | --- | ARPModule | 0x0003 | Answers ARP-Requests directly to reduce flooding in the network. | | SimpleForwardingModule | 0x0004 | Packets are forwarded to their destinations using PacketOut messages. (Layer 2 forwarding) | | ForwardingModule | 0x0009 | Installs routes based on layer-2 info(dst-mac). | | LoggingModule | 0x0010 | Logs all zmf pub/sub traffic | | RestAdminModule | 0x0011 | REST Interface and Web UI| | Reserved | 0x001* | Reserved for new ZSDN modules |

Module dependencies:

ZeroSDN StartUpSelector initial screen

 

Common Data Structures

Name Summary
CommonTopology Contains topology related data structures used by multiple topology discovery related modules.

Brief Architectural Summary

All Modules share a common pattern for MessageTypes and protobuf message formats. This is explained in more detail here:
MessageType and protobuf format

MessageTypes

General Information

The message type identifier is a 32 byte long field to identify a message. It is used for matching for pub/sub messages.

.topics files declare what Message Types a module supports.

From this file, factories for creating topics are created in C++ and Java.

Using these factories yields a MessageType. the MessageType denotes the type of a message sent in ZSDN.

Format of the File

Example file:

TO = 0x01
	EXAMPLE_MODULE_A = 0x0009
		SOME_EVENT = 0x00
			TO_INSTANCE_ID = 0x????????????????
		ANOTHER_EVENT = 0x01
		SOME_REQUEST = 0x02


# A Comment
FROM = 0x02
	EXAMPLE_MODULE_A = 0x0009
		RANDOM_NUMBER = 0x00
			ZERO_TO_TEN = 0x00
			GREATER_TEN = 0x01

REQUEST = 0x03
    EXAMPLE_MODULE_A = 0x0009
        SOME_REQUEST = 0x00

REPLY = 0x04
    EXAMPLE_MODULE_A = 0x0009
        SOME_REPLY = 0x00
  • MessageType definition is strictly hierarchical. The hierachy is repesented as a tree.
  • Indentation is done by using a tab or 4 spaces for each level.
  • All Topics with the same parent require a unique value or alternatively a variable value (using question marks).
  • All Topics with the same parent should use the same length - otherwise multiple factory paths may result in the same MessageType.

Details

If you are a Module of Type A and want to receive messages adressed at you, you have to create a Message Type that looks something like this:

"TO" "MODULE_A" ...

How is this represented internally? TO is represented by a byte with a value of 1, or 0x01 (hex) MODULE_A is represented by its module type id, which is 2 bytes long - lets assume that our module has the module type of 9 or 0x0009 in hex.

The actual messageId will therefore look like this internally:

 {1,0,9} or {0x01,0x00,0x09}
  • the first byte beeing "TO" and the next 2 bytes "MODULE_A".

Since creating topics like this is not exactly intuitive, the factories generated from a .topics will help you build topics easily and without having to worry about the underlying specifics.

In C++, creating a MessageType looks like this:

 MessageType t = TO().module_a().build();

which is equivalent of manually building a MessageType like this, if you want to do it manually:

 MessageType t;
 t.AppendMatch8(0x01);
 t.AppendMatch16(0x0009);

Topic definition is strictly hierarchical. On layer 0 (no indendation) top level topics are declared. These are:

 - GENERAL : TODO
 - TO      : Top level topic for all Messages to modules.                       (0x01 -> 1 byte)
 - FROM    : Top level topic for all Messages from modules.                     (0x02 -> 1 byte)
 - REQUEST : All requests a module supports                                     (0x03 -> 1 byte)
 - REPLY   : All reply topics the Module can send back for an incoming request.	(0x04 -> 1 byte)
 - SYSTEM  : TODO

On the second layer of these types, the name of the module together with its module-type-ID is written in hexadecimal notation.

Everything beyond that is module defined - no other module can have conflicting messages from here on, since each module type has a unique ID.

Data types

It is VERY IMPORTANT to write the numbers in hexadecimal notation - When the code is generated, the number of digits will determine the datatype for this topic part.

Notation interpreted as
0x00 constant uint8_t (byte)
0x0000 constant uint16_t (short)
0x00000000 constant uint32_t (int)
0x0000000000000000 constant uint64_t (long)

If, instead of a constant, a variable should be set at a certain point, simply replace a concrete value by question marks:

Notation interpreted as
0x?? variable uint8_t (byte)
0x???? variable uint16_t (short)
0x???????? variable uint32_t (int)
0x???????????????? variable uint64_t (long)

This allows, just for example, the adressing of a specific switch:

 MessageType toSwitch = TO().switchadapter().switchId(42).openflow().packetOut().build();

You can also stop at any point in the factory and create a MessageType. instead of doing this:

 MessageType tcpPacketIns = FROM().switchadapter().openflow().packet_in().IPv4().TCP().build();

You can also simply do this:

 MessageType tcpPacketIns = FROM().switchadapter().openflow().packet_in().build();

which means that you will receive all packet-in messages, not only those of type IPv4+TCP.

Comparing MessageTypes

If you want to know whether a Message is of a certain type, you can compare them to other MessageTypes.

If, for example, you get a Message and don't know anything about it (usually you should at least know the general type of messages you are handling), you can do the following:

 MessageType fromSwitch = From().switchadapter().build(); // assume we dont know the exact topic

Now we want to find out what exactly received is. Using this notation, you can find out any relationship between two MesageTypes:

 MessageType fromSwitchOpenflow = FROM().switchadapter().openflow().build();
 
 fromSwitch.containsTopic(fromSwitchOpenflow) ->  false // fromSwitchOpenflow is more specific 
 fromSwitchOpenFlow.containsTopic(fromSwitch) ->  true  // fromSwitch is less specific than fromSwitchOpenFlow

Container

Protobuf messages relating to these top-level types always use a top-level protobuf message. for example, all Messages FROM a specific module are bundled up like this:

message From {
    oneof FromMsg {
        <FromMsg_1_Type> <fromMsg_1_Name> = 1;
        <FromMsg_2_Type> <fromMsg_2_Name> = 2;
        ...
        <FromMsg_n_Type> <fromMsg_n_Name> = n;
    }
}

The same is true for TO, REQUEST and REPLY. This pattern allows easy extensions which can be handled at runtime - modules can depend on parsing the respective top-level message first, then check the actual Message they are interested in.

Third party Modules are free to send their data in any format they want - however, all standard components of ZSDN use google protocol buffers for communication.

Each ZSDN module has its own .proto file containing all messages belonging to the module. The top level messages are equal to the top level topics - To, From, Request, Reply. Using a top level message as container for exactly one subtype of message allows easy runtime checks on the type of a message - for example, a Module a can parse all its messages it receives as a request as a "Request" msg - and then look up the specific subtype.

IMPORTANT: Not every topic (see .topics / MessageType) has to have an associated protobuf message - there is no point in having hundreds of submessages for something that can be expressed as a single integer in a top level message, for example. Modules have to specify the kind of protobuf message expected/sent out for each topic in the topics file.

package ExampleModuleA_Proto;

// Main message type for messages TO ExampleModuleA

message To {

    oneof ToMsg {
        SomeEvent someEvent = 1;
        AnotherEvent anotherEvent = 2;
        SomeRequest someRequest = 3;
    }

    // SubMessage definition of ExampleModuleA

    message SomeEvent {
        required string foo = 1;
        optional uint32 bar = 2;
    }

    message AnotherEvent {
        required string foo = 1;
        optional uint32 bar = 2;
    }

    message SomeRequest {
        repeated uint64 bar = 1;
        repeated SomePayload payload = 2;
    }
}

// Main message type for messages FROM ExampleModuleA

message From {

    oneof FromMsg {
        RandomNumber randomNumbers = 1;
    }

    message RandomNumber {
        required uint64 randomNumber = 1; 
        optional SomePayload payload = 2;
    }
}

message Request {
    oneof Req {
        SomeRequest request = 1;
    }
    message SomeRequest {}
}

message Reply {
    oneof Rep {
         SomeReply reply = 1;
    }
    message SomeReply{}
}




// Message types used in both FROM and TO messages

message SomePayload {
    required string something = 1;
    required bytes payload = 2;
}


Clone this wiki locally