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