Project for building unikernels that uses region based memory management.
Below is a small example of a Unikernel service. The port 8080 is bound to a
callback function which here is the identity function i.e. an echo service. It
then listens for any incomming messages and sends back the payload for that
message.
open Service
fun udpService handlerRequest =
case handlerRequest of
(8080, payload) => payload
| (_, _) => ""
structure TL = TransportLayerSingle(UdpHandler(val service = udpService))
structure Net = Network(IPv4Handle( structure FragAssembler = FragAssemblerList;
structure TransportLayer = TL))
local
in
val _ = Net.listen ()
endThe transport layer UDP can be swapped with a TCP implementation and here
one has to choice between STREAM and FULL. In STREAM mode the service
reply to each TCP segments as it receives them where in FULL mode the service
collects all segments before processing them. Below is an example where
8080 is set to STREAM and 8081 is set to FULL.
open Service
fun tcpService handlerRequest =
case handlerRequest of
(8080, SETUP) => SETUP_STREAM
| (8080, REQUEST payload) => REPLY payload
| (8081, SETUP) => SETUP_FULL
| (8081, REQUEST payload) => REPLY payload
| _ => IGNORE
structure TL = TransportLayerSingle(TcpHandler(val service = tcpService))
structure Net = Network(IPv4Handle( structure FragAssembler = FragAssemblerList;
structure TransportLayer = TL))
local
in
val _ = Net.listen ()
endUDP and TCP can be combined using the TransportLayerComb combinator functor.
An example of UDP and TCP in unison is seen below:
open Service
fun tcpService handlerRequest =
case handlerRequest of
(8080, SETUP) => SETUP_STREAM
| (8080, REQUEST payload) => REPLY payload
| (8081, SETUP) => SETUP_FULL
| (8081, REQUEST payload) => REPLY payload
| _ => IGNORE
fun udpService handlerRequest =
case handlerRequest of
(8082, payload) => payload
| (_, _) => ""
structure TL =
TransportLayerComb(
structure tl = TransportLayerSingle(TcpHandler(val service = tcpService))
structure tlh = UdpHandler(val service = udpService))
structure Net = Network(IPv4Handle( structure FragAssembler = FragAssemblerList;
structure TransportLayer = TL))
local
in
val _ = Net.listen ()
endThe project include five small examples (these run on both Unix and Unikraft):
-
echo: a simple echo server that mirrors exactly what it receives. -
echoReverse: reverses it's input and returns it. -
facfib: serves two ports with the factorial and fibonacci functions respectively. -
monteCarlo: estimates$\pi$ using the sml-sobol library (runsmlpkg syncbefore use). -
sort: sorts its given integers using mergesort.
Logging can be turned on with the Logging.enable function and the service will
print and log useful information depending on the specified protocol. For instance
logging TCP will result in:
-- TCP INFO --
Source port: 54346
Destination port: 8080
Sequence number: 3328429351
Acknowledgement number: 1
DOffset: 5
Reserved: 0
Control bits: 24
Flags: PSH | ACK
Window: 64240
Checksum: 63769
Urgent pointer: 0
Payload: Hello, worldFurthermore one has to specify a level of logging. Level 1 prints the header of
the each segment and level 2 prints the header and the payload. An example of
TCP with level 2 is seen below.
val _ = Logging.enable {protocols=[Protocols.TCP], level = 2};First, run the setup command to setup a tuntap device:
$ make setupThe make rule for compiling an app to unix:
$ make <application name>-ex-appRun the application as an executable with the argument ip=10.0.0.5, with any valid IP address:
$ ./<application name>.exe ip=10.0.0.5Once the unikernel is running one can send UDP packets to the unikernel via netcat:
$ echo -n $'Hello, World!\n' | nc -u -Nw1 10.0.0.2 8080or TCP packets via netcat with:
$ echo -n $'Hello, World!\n' | nc -N 10.0.0.2 8080First, run the setup command to update and build external dependencies:
$ make t=uk setupThe make rule for compiling an app to Unikraft:
$ make t=uk <application name>-ex-appRun the application with QEMU (this will set up a virtual bridge):
$ make run-ukOnce the unikernel is running one can send UDP packets to the unikernel via netcat:
$ echo -n $'Hello, World!\n' | nc -u -nw1 172.44.0.2 8080To create an application create a new directory and include two files in the examples folder:
main.smlwhich contains the code for the applicationmain.mlbwhich is the ML basis file containing the applications dependencies
For montoring network traffic on unix:
$ sudo tshark -i tap0Or unikraft:
$ sudo tshark -i virbr0