Network Microservice SDK

About document

This document describes the Network Microservice Software Development Kit (SDK).

Overview

What is a network microservice microprogram?

A microprogram is a collection of instructions that performs action set computation when executed by a Network Microservice Processor.

Bayware’s approach to microprogram development satisfies the following requirements:

  • extremely fast execution - microprogram executable code is a collection of RISC-V instructions that are efficiently executed by the network microservice processor
  • excellent expressive power - any forwarding logic can be described
  • first-grade compactness - source-based routing implementation comprises three four- or two-byte instructions

What is the network microservice SDK?

The SDK is a tool for microprogram development allowing the generation of a collection of RISC-V instructions executable by a network microservice processor.

What can the network microservice SDK do?

The SDK allows developers to use a high-level language (Java) for microprogram development and automatically generate executable code. This eliminates the necessity to program in RISC-V and dive deep into the Network Microservice Execution Environment implementation.

Who can use the network microservice SDK?

Anyone who needs to introduce new packet forwarding logic and has basic knowledge of Java can use the SDK.

Getting Started

Development Kit

A service template is generated using the development kit library. The service template is used to form the IBFlow.

The library uses a Java-like syntax.

The development and execution life cycle consists of the following steps

The ENF Lifecycle

Fig. 55 Figure. The ENF Lifecycle

  • Develop - node execution rules formed (Java)
  • Template Generation - service template (signed JSON document) formed for created class
  • Deploy - generated service template deployed at controller
  • IBStream Generation - application forms IB Flows

Create “Hello world!”

Simple Forward (Source-based Routing)

Step1. Write Source Code

To start microprogram development, create Java Class. The class must be an extension of IceBuilder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package org.test;

import io.bayware.builder.*;
import io.bayware.builder.ExecMethod.ExecType;
import io.bayware.type.anno.*;
import io.bayware.type.*;

public class SimpleForward extends IceBuilder {

}

Create a method to add a package forwarding logic.

@Method(name = "frwrd")
public ExecMethod mForward() {
}

The class can contain one or several methods. Executable code will be generated for each method with the annotation @Method.

Add a collection of instructions to the method.

1
2
3
4
5
6
7
@Method(name = "frwrd")
public ExecMethod mForward() {
   return new ExecMethod(
           Op.forward(Path.egressRCI0),
           Op.end()
   );
}

Wherein:

Op.forward – send packet

Path.egressRCI0 – use RCI0 received after path parsing

Op.end – terminate code execution

As a result, the class will be.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package org.test;

import io.bayware.builder.*;
import io.bayware.type.anno.*;
import io.bayware.type.*;

public class SimpleForward extends IceBuilder {

   @Method(name = "frwrd")
   public ExecMethod mForward() {
       return new ExecMethod(
               Op.forward(Path.egressRCI0),
               Op.end()
       );
   }
}

That’s it! The logic of source-based routing is implemented.

Step 2. Generate Executable Code

To generate executable code, use the procedure ctx.makeHex().

Create a context using the class SimpleForward defined on the step 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package org.test;

import io.bayware.builder.*;

public class SimpleForwardTest {


   public static void main(String[] args) {
        Context ctx = Context.create(SimpleForward.class);
        ctx.makeHex();
   }
}

Run ctx.makeHex().

The resulting file SipmleForward.hex contains a hex-encoded executable code in the first line and a pointer to the first instruction of method in the second line.

00859F0301EFA62300000073
frwrd:0

Step 3 (optional). Generate Assembly Code

Assembly Code

To generate assembly code, use the procedure ctx.makeAsm().

The resulting file SipmleForward.s contains a collection of RISC-V instructions.

lh x30, 0x8(x11)
sw x30, 0xc(x31)
ecall

This assembly code can be compiled into machine code using any RISC-V compiler.

Dump

To generate dump for assembly code analysis, use the procedure ctx.makeDump().

The resulting file SipmleForward.dump contains a collection of RISC-V instructions with additional information.

1
2
3
4
5
6
-- Method:frwrd
-------  Op.forward
0x00859F03:lh x30, 0x8(x11)
0x01EFA623:sw x30, 0xc(x31)
-------  Op.end
0x00000073:ecall

Using Several Methods for Packet Forwarding

A class can hold several methods. Each method receives its own name defined in the annotation @Method.

An example is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.test;

import io.bayware.builder.*;
import io.bayware.type.anno.*;
import io.bayware.type.*;

public class SimpleForward extends IceBuilder {

   @Method(name = "frwrd1")
   public ExecMethod mForward1() {
       return new ExecMethod(
               Op.forward(Path.egressRCI0), Op.end()
       );
   }

   @Method(name = "frwrd2")
   public ExecMethod mForward2() {
       return new ExecMethod(
               Op.forward(Path.egressRCI1), Op.end()
       );
   }
}

Executing the procedure ctx.makeHex() provides the result shown below.

00859F0301EFA6230000007300A59F0301EFA62300000073
frwrd1:0
frwrd2:12

This code comprises the two methods. An offset for the first instruction of method `frwrd1 is 0. Whereas an offset for the first instruction of method frwrd2 is 12.

Variables

Variable Types

A microprogram developer can use the following variable types:

  • Word – 4 bytes
  • HalfWord – 2 bytes
  • Byte – 1 byte
  • Bit – flags (from 0 to 31)

Class for Variables

Record Definition

To declare variables, define the class for a record. The record holds a collection of variables with their types.

As an example, the class for the record SimpleVar<T> holds the variables var1, var2, var3.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package org.test;

import io.bayware.type.record.FieldRecord;
import io.bayware.type.fields.*;
import io.bayware.type.fields.Byte;

public class SimpleVar<T> extends FieldRecord {
   public Field<Word> var1;
   public Field<HalfWord> var2;
   public Field<Byte> var3;
}

Declaration of a bit-type variable requires specifying the number of bits to read @Bit(read = ). An example is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package org.somebody.model;

import io.bayware.type.record.FieldRecord;
import io.bayware.type.fields.Field;
import io.bayware.type.fields.Byte;
import io.bayware.type.fields.Bit;

public class my_field_rec<T> extends FieldRecord {

   @Bits(read = 4)
   public Field<Bit> var3;

   @Bits(read = 2)
   public Field<Bit> var4;

   @Bits(read = 2)
   public Field<Bit> var5;
}

To holds the values of the variables var3, var4, var5, one byte is to be allocated.

If one more variable is to be declared, for example –

@Bits(read = 4)
public Field<Bit> var6;

an additional byte will be allocated wherein 4 bits to be assigned for the new variable.

Using Several Records

More than one record can be declared. For example, to implement an advanced trace-route logic the microprogram has to collect en route: hop identifiers, timestamps, ingress and egress connections. To hold the values of this variables, the structure shown below is to be defined.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package org.somebody.sample;

import io.bayware.type.record.FieldRecord;
import io.bayware.type.fields.*;
import io.bayware.type.anno.*;

@RecordCount(maxCount = 30)
public class TraceRecord<T> extends FieldRecord {
   public Field<Word> hopIdLo;
   public Field<Word> hopIdHi;
   public Field<Word> timeStamp;
   public Field<HalfWord> inRCI;
   public Field<HalfWord> outRCI;
}

Note, that the class annotation defines the maximum number of records - @RecordCount(maxCount = 30).

Record-in-Record

Record can be a field of another record. In this case, Segment is specified as a field type.

As an example, TraceRecord is a filed of TraceInfo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package org.somebody.sample;

import io.bayware.type.record.GeneralRecord;
import io.bayware.type.fields.Byte;
import io.bayware.type.fields.*;

public class TraceInfo extends GeneralRecord {
   public Field<Word> passedHopCounter;
   public TraceRecord<Segment> hopInfo;
   public Field<Byte> test;
}
An example of record-in-record

Fig. 56 Figure. An example of record-in-record

Using Variables in Microprogram

As a new record class is created, it can be used in a microprogram. An example is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.test;

import io.bayware.builder.*;
import io.bayware.type.anno.*;
import io.bayware.type.*;

public class SimpleForward extends IceBuilder {

   @Var(type = VarType.packet)
   SimpleVar packVar;

   @Method(name = "frwrd")
   public ExecMethod method1() {
       return new ExecMethod(Op.forward(Path.egressRCI0),
                             Op.assign(packVar.var1, 23),
                             Op.assign(packVar.var2, 34),
                             Op.assign(packVar.var3, packVar.var1),
                             Op.plus(packVar.var3, 3),
                             Op.end()
       );
   }
}

The variable var1 receives the value of 23.

The variable var2 receives the value of 34.

The variable var3 receives the value of the variable var1.

Add the value of 3 to var3 and store the result in var3.

Pre-defined Variables

A few variables are already pre-defined. They can be used in every microprogram.

Record: Switch

The record Node holds the basic information about the node.

switchId
This variable holds the Switch Identifier calculated as a 20-bit MD5 hash of switch’s CGA.
switchIPAddr0
This variable holds the most-significant word of the switch’s IPv6 CG address. It is the upper four bytes of the netprefix.
switchIPAddr1
This variable holds the second word of the switch’s IPv6 CG address. It is the lower four bytes of the netprefix.
switchIPAddr2
This variable holds the third word of the switch’s IPv6 CG address. It is the upper four bytes of the cryptographically generated interface identifier.
switchIPAddr3
This variable holds the least-significant word of the switch’s IPv6 CG address. It is the lower four bytes of the cryptographically generated interface identifier.
switchType
Switch Type
timeStamp
Current Time

Record: Path

The record Path holds the path parsing results.

pathCreationTime
The Path Creation Time parsed from the Path Chunk.
topicSwitchTag
The Topic Switch Tag extracted from the Topic Policy Table.
pktType
8’b0–Type II; 8’b1–Type III; 8’b2–Type I packet without optional PLD_DATA Chunk; 8’b3–Type I packet with optional PLD_DATA Chunk; Else–undefined.
ingressRCI
The Segment Identifier of the ingress link, on which the packet received.
egressRCI0
The potential egress RCI parsed from the Path Chunk.
egressRCI1
The potential egress RCI parsed from the Path Chunk.
egressRCI3
The potential egress RCI parsed from the Path Chunk.
egressRCI4
The potential egress RCI parsed from the Path Chunk.
egressRCI5
The potential egress RCI parsed from the Path Chunk.
egressRCI6
The potential egress RCI parsed from the Path Chunk.
egressRCI7
The potential egress RCI parsed from the Path Chunk.

Record: Connection

The record Connection holds descriptions for each egress connection.

connectionCreationTime
The timestamp for the connection creation time.
connectionQualityCredits
Connection aggregated quality credits. This variable characterizes connection quality.
connectionStatus
The connection is up or down.
connectionUtilizationCredits
Connection aggregated utilization credits. This variable characterizes connection utilization.
connId
This variable contains a 12-bit RCI. Note that values between 0 and 0xF are not valid connection IDs. A few examples: 0x0 – a “dummy” hop; 0x1 – a subnet in a dynamic path description; 0xF – a broadcast request.
remoteNodeId
This variable holds the 32-bit identifier of the remote node assigned to the node by the controller.
remoteNodeIPAddr0
This variable holds the most-significant word of the remote node’s IPv6 CG address. It is the upper four bytes of the netprefix.
remoteNodeIPAddr1
This variable holds the second word of the remote node’s IPv6 CG address. It is the lower four bytes of the netprefix.
remoteNodeIPAddr2
This variable holds the third word of the remote node’s IPv6 CG address. It is the upper four bytes of the cryptographically generated interface identifier.
remoteNodeIPAddr3
This variable holds the least-significant word of the remote node’s IPv6 CG address. It is the lower four bytes of the cryptographically generated interface identifier.
remoteNodeNetRole
The network role (switch or host) of the remote node.

Record: Tag

The record Tag holds a set of connections with a given tag.

RCI0
A RCI associated with the tag used during lookup.
RCI1
A RCI associated with the tag used during lookup.
RCI2
A RCI associated with the tag used during lookup.
RCI3
A RCI associated with the tag used during lookup.
RCI4
A RCI associated with the tag used during lookup.
RCI5
A RCI associated with the tag used during lookup.
RCI6
A RCI associated with the tag used during lookup.
RCI7
A RCI associated with the tag used during lookup.

Record: ActionSet

The record ActionSet holds information on actions required by the microprogram.

executeActionSetType
As defined in the Execution Environment document.
updateFlowDefaultActionSetType
As defined in the Execution Environment document.
updatePacketPathPointer
As defined in the Execution Environment document.
updatePacketProgramData
As defined in the Execution Environment document.
newFlowActionSetConnectionFlags
As defined in the Execution Environment document.
newFlowActionSetConnection
As defined in the Execution Environment document.
newTopicActionSetConnection
As defined in the Execution Environment document.

Record: *

The owner may pre-define its owner record or a plurality of records.

any
Variable defined by the owner of Overlay.

Scope of Variables

Variables have the following scopes:

  • local – the variable is initialized during microprogram execution; after execution the variable is to be destroyed,
  • packet – the variable is defined for a packet and passed with the packet from node to node,
  • flow – the variable is defined for a given flow and visible for all packets of the flow,
  • topic – the variable is defined for a given topic and visible for all packets of the topic.

The scope is specified at the time of defining the variable within a microprogram class.

For example:

1
2
3
4
5
6
7
8
9
public class SimpleForward extends IceBuilder {

   @Var(type = VarType.packet)
   SimpleVar packVar;

   @Var(type = VarType.local)
   SimpleVar localVar;

...

Statements

From a developer point of view, the method of microprogram is a collection of operators: operator1, operaror2, operator3.

The class ExecMethod holds a set of operators (i.e. statements) that are to be executed.

new ExecMethod(IStatement...)

Wherein IStatement… - a set of operators for execution.

For example:

1
2
3
4
5
6
7
8
9
new ExecMethod(
       Op.plus(tracePacket.passedHopCounter,1),
       Op.novel(tracePacket.hopInfo),
       Op.assign(tracePacket.hopInfo.hopId, Node.switchIPAddr0),
       Op.assign(tracePacket.hopInfo.timeStamp, Node.timeStamp),
       Op.assign(tracePacket.hopInfo.inRCI,Path.ingressRCI),
       Op.assign(tracePacket.hopInfo.outRCI,Path.egressRCI0),
       Op.forward(Path.egressRCI0),
       Op.end());

All the operators are defined in the class io.bayware.builder.Op.