KasperskyOS Community Edition 1.0
[Topic ping_example]

About the ping example

The ping example includes two entities: Client and Server.

The Server entity provides two identical Ping and Pong methods that receive a number and return a modified number:

Ping(in UInt32 value, out UInt32 result);

Pong(in UInt32 value, out UInt32 result);

The Client entity calls both of these methods in a different sequence. If the method call is denied by the solution security policy, the Failed to call... message is displayed.

The transport part of the ping example is virtually identical to its counterpart in the echo example. The only difference is that the ping example uses two methods (Ping and Pong) instead of one.

Use of IPC transport is briefly examined in the ping example because it was already examined in detail in the comments to the echo example.

The ping example implements a solution security policy (security.psl) based on the Flow security model.

Components of the ping example

The ping example consists of the following files:

  • client/src/client.c
  • resources/edl/Client.edl
  • server/src/server.c
  • resources/edl/Server.edl, resources/cdl/Control.cdl, resources/idl/Connection.idl
  • init.yaml
  • security.psl
Page top
[Topic about_ping_example]

Implementation of the Client entity in the ping example

The Client entity calls the Ping and Pong methods in a varying sequence.

For more details on initialization of transport to the server, use of a proxy object and interface methods, and the purpose of the Connection.idl.h file, see the comments to the client.c file in the echo example.

client.c

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

/* Files required for transport initialization. */

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

/* Description of the server interface used by the client entity. */

#include <ping/Connection.idl.h>

#define EXAMPLE_VALUE_TO_SEND 777

static const char *Tag = "[Client]";

static struct Connection_proxy proxy;

static uint32_t ping(uint32_t value)

{

/* Request and response structures */

struct Connection_Ping_req req;

struct Connection_Ping_res res;

req.value = value;

/**

/* Call Connection_Ping interface method.

* Server will be sent a request for calling Ping interface method

* control.connectionimpl with the value argument. Calling thread is locked

* until a response is received from the server.

*/

if (Connection_Ping(&proxy.base, &req, NULL, &res, NULL) == rcOk)

{

fprintf(stderr, "%s Ping(%d), result = %d\n", Tag, value, res.result);

value = res.result;

}

else

{

fprintf(stderr, "%s Ping(%d), failed\n", Tag, value);

}

return value;

}

static uint32_t pong(uint32_t value)

{

/* Request and response structures */

struct Connection_Pong_req req;

struct Connection_Pong_res res;

req.value = value;

/**

/* Call Connection_Pong interface method.

* Server will be sent a request for calling Pong interface method

* controlimpl.connectionimpl with the value argument. Calling thread is locked

* until a response is received from the server.

*/

if (Connection_Pong(&proxy.base, &req, NULL, &res, NULL) == rcOk)

{

fprintf(stderr, "%s Pong(%d), result = %d\n", Tag, value, res.result);

value = res.result;

}

else

{

fprintf(stderr, "%s Pong(%d), failed\n", Tag, value);

}

return value;

}

/* Client entity entry point. */

int main(int argc, const char *argv[])

{

NkKosTransport transport;

uint32_t value;

int i;

fprintf(stderr, "%s Entity started\n", Tag);

/**

* Get the client IPC handle of the connection named

* "server_connection".

*/

Handle handle = ServiceLocatorConnect("server_connection");

if (INVALID_HANDLE == handle)

{

fprintf(stderr, "%s ServiceLocatorConnect failed\n", Tag);

return EXIT_FAILURE;

}

/* Initialize IPC transport for interaction with the server entity. */

NkKosTransport_Init(&transport, handle, NK_NULL, 0);

/* Get Runtime Interface ID (RIID) for interface ping.Control.connectionimpl. */

nk_iid_t riid = ServiceLocatorGetRiid(handle, "ping.Control.connectionimpl");

if (INVALID_RIID == riid)

{

fprintf(stderr, "%s ServiceLocatorGetRiid failed\n", Tag);

return EXIT_FAILURE;

}

/**

* Initialize proxy object by specifying transport (&transport)

* and ID of the server interface (riid). Each method

* of the proxy object will be implemented by sending a request to the server.

*/

Connection_proxy_init(&proxy, &transport.base, riid);

/* Test loop. */

value = EXAMPLE_VALUE_TO_SEND;

for (i = 0; i < 5; ++i)

{

value = ping(value);

value = pong(value);

}

value = ping(value);

value = ping(value);

value = pong(value);

value = pong(value);

return EXIT_SUCCESS;

}

Page top
[Topic ping_client_implementation]

Implementation of the Server entity in the ping example

For more details on initialization of transport to the client, preparation of the request and response structures, and the purpose of the Server.edl.h file, see the comments to the server.c file in the echo example.

server.c

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

/* Files required for transport initialization. */

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

/* Server entity descriptions in EDL, CDL, and IDL. */

#include <ping/Connection.idl.h>

#include <ping/Control.cdl.h>

#include <ping/Server.edl.h>

#define INCREMENT_STEP 3

static const char *Tag = "[Server]";

/* Type of interface implementing object. */

typedef struct ObjectImpl

{

struct Connection base; /* base interface of object */

int step; /* additional parameters */

} ObjectImpl;

/* Implementation of the Ping method. */

static nk_err_t Ping_impl(

struct Connection *self,

const struct Connection_Ping_req *req,

const struct nk_arena *req_arena,

struct Connection_Ping_res *res,

struct nk_arena *res_arena)

{

ObjectImpl *impl = (ObjectImpl *)self;

/**

* Increment value in the client request by

* one step and include into result argument that will be

* sent to the client in the server response.

*/

res->result = req->value + (unsigned int)impl->step;

return NK_EOK;

}

/* Implementation of the Pong method. */

static nk_err_t Pong_impl(

struct Connection *self,

const struct Connection_Pong_req *req,

const struct nk_arena *req_arena,

struct Connection_Pong_res *res,

struct nk_arena *res_arena)

{

ObjectImpl *impl = (ObjectImpl *)self;

/**

* Increment value in the client request by

* one step and include into result argument that will be

* sent to the client in the server response.

*/

res->result = req->value + (unsigned int)impl->step;

return NK_EOK;

}

/**

* Ping object constructor.

* step is the number by which the input value is increased.

*/

static struct Connection *CreateObjectImpl(int step)

{

/* Table of implementations of Connection interface methods. */

static const struct Connection_ops ops = {.Ping = Ping_impl, .Pong = Pong_impl};

/* Object implementing the interface. */

static struct ObjectImpl impl = {.base = {&ops}};

impl.step = step;

return &impl.base;

}

/* Server entry point. */

int main(void)

{

NkKosTransport transport;

ServiceId iid;

/* Register the connection and get its handle. */

Handle handle = ServiceLocatorRegister("server_connection", NULL, 0, &iid);

if (INVALID_HANDLE == handle)

{

fprintf(stderr, "%s Failed to register service locator\n", Tag);

return EXIT_FAILURE;

}

/* Initialize transport to client. */

NkKosTransport_Init(&transport, handle, NK_NULL, 0);

/* Prepare request structures: constant part and arena. */

union Server_entity_req req;

char req_buffer[Server_entity_req_arena_size];

struct nk_arena req_arena = NK_ARENA_INITIALIZER(req_buffer, req_buffer + sizeof(req_buffer));

/* Prepare response structures: constant part and arena. */

union Server_entity_res res;

char res_buffer[Server_entity_res_arena_size];

struct nk_arena res_arena = NK_ARENA_INITIALIZER(res_buffer, res_buffer + sizeof(res_buffer));

/**

/* Initialize Control component dispatcher. INCREMENT_STEP is the value of the "step",

* which will be added to the "value" input argument.

**/

struct Control_component component;

Control_component_init(&component, CreateObjectImpl(INCREMENT_STEP));

/* Initialize server entity dispatcher. */

struct Server_entity entity;

Server_entity_init(&entity, &component);

fprintf(stderr, "Hello I'm server\n");

/* Dispatch loop implementation. */

do

{

/* Reset request/response buffers. */

nk_req_reset(&req);

nk_arena_reset(&req_arena);

nk_arena_reset(&res_arena);

/* Wait for the request from client. */

if (nk_transport_recv(&transport.base, &req.base_, RTL_NULL) == NK_EOK)

{

fprintf(stderr, "%s nk_transport_recv error\n", Tag);

}

else

{

/**

* Process received request by calling connectionimpl implementation

* of the requested Ping interface method.

*/

Server_entity_dispatch(&entity, &req.base_, &req_arena, &res.base_, &res_arena);

}

/* Send response. */

if (nk_transport_reply(&transport.base, &res.base_, &res_arena) != NK_EOK)

{

fprintf(stderr, "%s nk_transport_reply error\n", Tag);

}

} while (true);

return EXIT_SUCCESS;

}

Page top
[Topic ping_server_implementation]

Description files in the ping example

Description of the Client entity

The Client entity does not implement an interface, so all you need to do is declare the entity name in the EDL description.

Client.edl

/* Description of the Client entity. */

entity ping.Client

Server entity description

The Server entity implements a Connection interface, which contains two methods: Ping and Pong. As in the echo example, you must declare a separate component, such as Control, which contains a Connection interface implementation.

In the EDL description of the Server entity, you must indicate that it contains an instance of the Control component:

Server.edl

/* Description of the Server entity. */

entity ping.Server

/* controlimpl is a named instance of the ping.Control component. */

components {

controlimpl : ping.Control

}

In the CDL description of the Control component, you must indicate that it contains a Connection interface implementation:

Control.cdl

/* Description of the Control component. */

component ping.Control

/* connectionimpl is the ping.Connection interface implementation. */

interfaces {

connectionimpl : ping.Connection

}

In the Connection package, you must declare the Connection interface, which contains two methods: Ping and Pong:

Connection.idl

/* Description of the Connection package. */

package ping.Connection

interface {

Ping(in UInt32 value, out UInt32 result);

Pong(in UInt32 value, out UInt32 result);

}

Init description

To enable the Client entity to call a Server entity method, an IPC channel must be created between them.

init.yaml

entities:

- name: ping.Client

connections:

- target: ping.Server

id: server_connection

- name: ping.Server

The channel is named server_connection. You can obtain the handle of this channel by using the Service Locator.

Page top
[Topic ping_description_files]

Solution security policy in the ping example

The solution security policy in this example allows you to start all entities, and allows any entity to query the Core and Server entities. Calls to the Server entity are managed by methods of the Flow security model.

The finite-state machine described in the configuration of the request_state Flow security model object has two states: ping_next and pong_next. The initial state is ping_next. Only transitions from ping_next to pong_next and the reverse are allowed.

When the Ping and Pong methods are called, the current state of the request_state object is checked. In the ping_next state, only a Ping call is allowed, in which case the state changes to pong_next. Likewise, in the pong_next state, only a Pong call is allowed, in which case the state changes to ping_next.

Therefore, the Ping and Pong methods can be called only in succession.

security.psl

/* Solution security policy for demonstrating use of the

* Flow security model in the ping example */

/* Include PSL files containing formal representations of

* Base and Flow security models */

use nk.base._

use nk.flow._

/* Create Flow security model object */

policy object request_state : Flow {

type States = "ping_next" | "pong_next"

config = {

states : ["ping_next" , "pong_next"],

initial : "ping_next",

transitions : {

"ping_next" : ["pong_next"],

"pong_next" : ["ping_next"]

}

}

}

/* Startup of all entities is allowed. */

execute {

grant ()

}

/* All requests are allowed. */

request {

grant ()

}

/* All responses are allowed. */

response {

grant ()

}

/* Including EDL files */

use EDL kl.core.Core

use EDL ping.Client

use EDL ping.Server

use EDL Einit

/* When the Server entity is started, initiate this entity with the finite-state machine */

execute dst=ping.Server {

request_state.init {sid: dst_sid}

}

/* When the Ping method is called, verify that the finite-state machine is in the ping_next state.

If it is, allow the Ping method call and switch the finite-state machine to the pong_next state. */

request dst=ping.Server, endpoint=controlimpl.connectionimpl, method=Ping {

request_state.allow {sid: dst_sid, states: ["ping_next"]}

request_state.enter {sid: dst_sid, state: "pong_next"}

}

/* When the Pong method is called, verify that the finite-state machine is in the pong_next state.

If it is, allow the Pong method call and switch the finite-state machine to the ping_next state. */

request dst=ping.Server, endpoint=controlimpl.connectionimpl, method=Pong {

request_state.allow {sid: dst_sid, states: ["pong_next"]}

request_state.enter {sid: dst_sid, state: "ping_next"}

}

Page top
[Topic ping_flow]

Building and running the ping example

See the Building and running examples section.

Page top
[Topic ping_example_build]