Contents
ping example
The ping example demonstrates the use of a solution security policy to control interactions between entities.
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
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
/* Files required for transport initialization. */
/* Description of the server interface used by the client entity. */
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;
}
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
/* Files required for transport initialization. */
/* Server entity descriptions in EDL, CDL, and IDL. */
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;
}
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.
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"}
}