KasperskyOS Community Edition 1.0

Пример ping

Пример ping демонстрирует использование политики безопасности решения для управления взаимодействиями между сущностями.

В этом разделе

О примере ping

Реализация сущности Client в примере ping

Реализация сущности Server в примере ping

Файлы описаний в примере ping

Политика безопасности решения в примере ping

Сборка и запуск примера ping

В начало

О примере ping

Пример ping включает в себя две сущности: Client и Server.

Сущность Server предоставляет два идентичных метода Ping и Pong, которые получают число и возвращают измененное число:

Ping(in UInt32 value, out UInt32 result);

Pong(in UInt32 value, out UInt32 result);

Сущность Client вызывает оба этих метода в различной последовательности. Если вызов метода запрещен политикой безопасности решения, выводится сообщение Failed to call...

Транспортная часть примера ping практически аналогична таковой для примера echo. Единственное отличие состоит в том, что в примере ping используется два метода (Ping и Pong), а не один.

Поскольку использование IPC-транспорта подробно рассмотрено в комментариях к примеру echo, в примере ping оно рассматривается кратко.

В примере ping реализуется политика безопасности решения (security.psl) на базе модели безопасности модели безопасности Flow.

Состав примера ping

Пример ping состоит из следующих файлов:

  • 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
В начало

Реализация сущности Client в примере ping

Сущность Client для вызывает методы Ping и Pong в различной последовательности.

Инициализация транспорта до сервера, использование прокси-объекта и интерфейсных методов, а также назначение файла Connection.idl.h рассматриваются более подробно в комментариях к файлу client.c в примере echo.

client.c

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

/* Файлы, необходимые для инициализации транспорта. */

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

#include <coresrv/sl/sl_api.h>

/* Описание интерфейса сервера, по которому обращается клиентская сущность. */

#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)

{

/* Структуры запроса и ответа */

struct Connection_Ping_req req;

struct Connection_Ping_res res;

req.value = value;

/**

* Вызываем интерфейсный метод Connection_Ping.

* Серверу будет отправлен запрос для вызова метода Ping интерфейса

* control.connectionimpl с аргументом value. Вызывающий поток блокируется

* до момента получения ответа от сервера.

*/

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)

{

/* Структуры запроса и ответа */

struct Connection_Pong_req req;

struct Connection_Pong_res res;

req.value = value;

/**

* Вызываем интерфейсный метод Connection_Pong.

* Серверу будет отправлен запрос для вызова метода Pong интерфейса

* controlimpl.connectionimpl с аргументом value. Вызывающий поток блокируется

* до момента получения ответа от сервера.

*/

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;

}

/* Точка входа в клиентскую сущность. */

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

{

NkKosTransport transport;

uint32_t value;

int i;

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

/**

* Получаем клиентский IPC-дескриптор соединения с именем

* "server_connection".

*/

Handle handle = ServiceLocatorConnect("server_connection");

if (INVALID_HANDLE == handle)

{

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

return EXIT_FAILURE;

}

/* Инициализируем IPC-транспорт для взаимодействия с серверной сущностью. */

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

/* Получаем Runtime Interface ID (RIID) для интерфейса 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;

}

/**

* Инициализируем прокси-объект, указав транспорт (&transport)

* и идентификатор интерфейса сервера (riid). Каждый метод

* прокси-объекта будет реализован как отправка запроса серверу.

*/

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

/* Тестовый цикл. */

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;

}

В начало

Реализация сущности Server в примере ping

Инициализация транспорта до клиента, подготовка структур запроса и ответа, а также назначение файла Server.edl.h рассматриваются более подробно в комментариях к файлу server.c в примере echo.

server.c

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

/* Файлы, необходимые для инициализации транспорта. */

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

#include <coresrv/sl/sl_api.h>

/* Описания сущности-сервера на языках EDL, CDL, 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]";

/* Тип объекта реализующего интерфейс. */

typedef struct ObjectImpl

{

struct Connection base; /* базовый интерфейс объекта */

int step; /* дополнительные параметры */

} ObjectImpl;

/* Реализация метода Ping. */

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;

/**

* Значение value, пришедшее в запросе от клиента, инкрементируем на

* величину шага step и помещаем в аргумент result, который будет

* отправлен клиенту в составе ответа от сервера.

*/

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

return NK_EOK;

}

/* Реализация метода Pong. */

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;

/**

* Значение value, пришедшее в запросе от клиента, инкрементируем на

* величину шага step и помещаем в аргумент result, который будет

* отправлен клиенту в составе ответа от сервера.

*/

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

return NK_EOK;

}

/**

* Конструктор объекта Ping.

* step - шаг, то есть число, на которое будет увеличиваться входящее значение.

*/

static struct Connection *CreateObjectImpl(int step)

{

/* Таблица реализаций методов интерфейса Connection. */

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

/* Объект, реализующий интерфейс. */

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

impl.step = step;

return &impl.base;

}

/* Точка входа в сервер. */

int main(void)

{

NkKosTransport transport;

ServiceId iid;

/* Регистрируем соединение и получаем дескриптор для него. */

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;

}

/* Инициализируем транспорт до клиента. */

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

/* Подготавливаем структуры запроса: фиксированную часть и арену. */

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));

/* Подготавливаем структуры ответа: фиксированную часть и арену. */

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));

/**

* Инициализируем диспетчер компонента Control. INCREMENT_STEP – значение "шага",

* которое будет прибавляться к входному аргументу value.

**/

struct Control_component component;

Control_component_init(&component, CreateObjectImpl(INCREMENT_STEP));

/* Инициализируем диспетчер сущности Server. */

struct Server_entity entity;

Server_entity_init(&entity, &component);

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

/* Реализация цикла обработки запросов. */

do

{

/* Сбрасываем буферы с запросом и ответом. */

nk_req_reset(&req);

nk_arena_reset(&req_arena);

nk_arena_reset(&res_arena);

/* Ожидаем запрос от клиента. */

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

{

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

}

else

{

/**

* Обрабатываем полученный запрос, вызывая реализацию connectionimpl

* запрошенного интерфейсного метода Ping.

*/

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

}

/* Отправляем ответ. */

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;

}

В начало

Файлы описаний в примере ping

Описание сущности Client

Сущность Client не реализует ни одного интерфейса, поэтому в EDL-описании достаточно объявить имя сущности.

Client.edl

/* Описание сущности Client. */

entity ping.Client

Описание сущности Server

Сущность Server реализует интерфейс Connection, содержащий два метода – Ping и Pong. Как и в примере echo, требуется объявить отдельный компонент (например Control), содержащий реализацию интерфейса Connection.

В EDL-описании сущности Server необходимо указать, что она содержит экземпляр компонента Control:

Server.edl

/* Описание сущности Server. */

entity ping.Server

/* controlimpl - именованный экземпляр компонента ping.Control. */

components {

controlimpl : ping.Control

}

В CDL-описании компонента Control необходимо указать, что он содержит реализацию интерфейса Connection:

Control.cdl

/* Описание компонента Control. */

component ping.Control

/* connectionimpl - реализация интерфейса ping.Connection. */

interfaces {

connectionimpl : ping.Connection

}

В пакете Connection необходимо объявить интерфейс Connection, содержащий два метода – Ping и Pong:

Connection.idl

/* Описание пакета Connection. */

package ping.Connection

interface {

Ping(in UInt32 value, out UInt32 result);

Pong(in UInt32 value, out UInt32 result);

}

Init-описание

Чтобы сущность Client могла вызвать метод сущности Server, между ними требуется создать IPC-канал.

init.yaml

entities:

- name: ping.Client

connections:

- target: ping.Server

id: server_connection

- name: ping.Server

Канал имеет имя server_connection. С помощью локатора сервисов можно получить дескриптор этого канала.

В начало

Политика безопасности решения в примере ping

Политика безопасности решения в этом примере разрешает запуск всех сущностей и позволяет любой сущности обращаться к сущностям Core и Server. При этом обращениями к сущности Server управляют методы модели безопасности Flow.

Конечный автомат, описанный в конфигурации объекта request_state модели безопасности Flow, имеет два состояния: ping_next и pong_next. Исходное состояние – ping_next. Разрешены только переходы из ping_next в pong_next и обратно.

При вызове методов Ping и Pong проверяется текущее состояние объекта request_state. В состоянии ping_next разрешен только вызов Ping, при этом состояние изменится на pong_next. Аналогично, в состоянии pong_next разрешен только вызов Pong, при этом состояние изменится на ping_next.

Таким образом, методы Ping и Pong разрешено вызывать только по очереди.

security.psl

/* Политика безопасности решения для демонстрации использования модели

* безопасности Flow в примере ping */

/* Включение PSL-файлов с формальными представлениями моделей безопасности

* Base и Flow */

use nk.base._

use nk.flow._

/* Создание объекта модели безопасности Flow */

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"]

}

}

}

/* Запуск любых сущностей разрешен. */

execute {

grant ()

}

/* Любые запросы разрешены. */

request {

grant ()

}

/* Любые ответы разрешены. */

response {

grant ()

}

/* Включение EDL-файлов */

use EDL kl.core.Core

use EDL ping.Client

use EDL ping.Server

use EDL Einit

/* При запуске сущности Server инициировать эту сущность с конечным автоматом */

execute dst=ping.Server {

request_state.init {sid: dst_sid}

}

/* При вызове метода Ping проверить, что конечный автомат находится в состоянии ping_next.

Если это так, разрешить вызов метода Ping и перевести конечный автомат в состояние pong_next. */

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"}

}

/* При вызове метода Pong проверить, что конечный автомат находится в состоянии pong_next.

Если это так, разрешить вызов метода Pong и перевести конечный автомат в состояние ping_next. */

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"}

}

В начало

Сборка и запуск примера ping

См. "Сборка и запуск примеров".

В начало