KasperskyOS Community Edition 1.1
[Topic sc_using_new_endpoints]

Overview: IPC message structure

In KasperskyOS, all interactions between processes have statically defined types. The permissible structures of an IPC message are defined by the description of the interfaces of the process that receives the message (server).

A correct IPC message (request and response) contains a constant part and an arena.

Constant part of a message

The constant part of a message contains arguments of a fixed size, and the RIID and MID.

Fixed-size arguments can be arguments of any IDL types except the sequence type.

The RIID and MID identify the interface and method being called:

  • The RIID (Runtime Implementation ID) is the number of the process endpoint being called, starting at zero.
  • The MID (Method ID) is the number of the method within the interface that contains it, starting at zero.

The type of the constant part of the message is generated by the NK compiler based on the IDL description of the interface. A separate structure is generated for each interface method. Union types are also generated for storing any request to a process, component or interface. For more details, refer to Example generation of transport methods and types.

Arena

The arena is a buffer for storing variable-size arguments (sequence IDL type).

Message structure verification by the security module

Prior to calling message-related rules, the Kaspersky Security Module verifies that the sent message is correct. Requests and responses are both validated. If the message has an incorrect structure, it will be rejected without calling the security model methods associated with it.

Forming a message structure

KasperskyOS Community Edition includes the following tools that make it easier for the developer to create and package an IPC message:

Simple IPC message generation is demonstrated in the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Page top
[Topic ipc_message_structure_overview]

Finding an IPC handle

The client and server IPC handles must be found if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To independently work with IPC transport, you need to first initialize it by using the NkKosTransport_Init() method and pass the IPC handle of the utilized channel as the second argument.

For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/)

You do not need to find an IPC handle to utilize services that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations, including finding IPC handles.

See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Finding an IPC handle when statically creating a channel

When statically creating an IPC channel, both the client and server can find out their IPC handles immediately after startup by using the ServiceLocatorRegister() and ServiceLocatorConnect() methods and specifying the name of the created IPC channel.

For example, if the IPC channel is named server_connection, the following must be called on the client side:

#include <coresrv/sl/sl_api.h>

Handle handle = ServiceLocatorConnect("server_connection");

The following must be called on the server side:

#include <coresrv/sl/sl_api.h>

nk_iid_t iid;

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

For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h.

Finding an IPC handle when dynamically creating a channel

Both the client and server receive their own IPC handles immediately after dynamic creation of an IPC channel is successful.

The client IPC handle is one of the output (out) arguments of the KnCmConnect() method. The server IPC handle is an output argument of the KnCmAccept() method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h.

Page top
[Topic ipc_find_ipc_desc]

Finding an endpoint ID (riid)

The endpoint ID (riid) must be found on the client side if there are no ready-to-use transport libraries for the utilized endpoint (for example, if you wrote your own endpoint). To call methods of the server, you must first call the proxy object initialization method on the client side and pass the endpoint ID as the third argument. For example, for the Filesystem interface:

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

For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/)

You do not need to find the endpoint ID to utilize services that are implemented in executable files provided in KasperskyOS Community Edition. The provided transport libraries are used to perform all transport operations.

See the gpio_*, net_*, net2_* and multi_vfs_* examples (/opt/KasperskyOS-Community-Edition-<version>/examples/).

Finding a service ID when statically creating a channel

When statically creating an IPC channel, the client can find out the ID of the necessary endpoint by using the ServiceLocatorGetRiid() method and specifying the IPC channel handle and the fully qualified name of the endpoint. For example, if the OpsComp component instance contains the FS endpoint, the following must be called on the client side:

#include <coresrv/sl/sl_api.h>

nk_iid_t riid = ServiceLocatorGetRiid(handle, "OpsComp.FS");

For more details, see the echo and ping examples (/opt/KasperskyOS-Community-Edition-<version>/examples/), and the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h.

Finding a service ID when dynamically creating a channel

The client receives the endpoint ID immediately after dynamic creation of an IPC channel is successful. The client IPC handle is one of the output (out) arguments of the KnCmConnect() method. For more details, see the header file /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h.

Page top
[Topic ipc_find_riid]

Example generation of transport methods and types

When building a solution, the NK compiler uses the EDL, CDL and IDL descriptions to generate a set of special methods and types that simplify the creation, forwarding, receipt and processing of IPC messages.

As an example, we will examine the Server process class that provides the FS endpoint, which contains a single Open() method:

Server.edl

entity Server

/* OpsComp is the named instance of the Operations component */

components {

OpsComp: Operations

}

Operations.cdl

component Operations

/* FS is the local name of the endpoint implementing the Filesystem interface */

endpoints {

FS: Filesystem

}

Filesystem.idl

package Filesystem

interface {

Open(in string<256> name, out UInt32 h);

}

These descriptions will be used to generate the files named Server.edl.h, Operations.cdl.h, and Filesystem.idl.h, which contain the following methods and types:

Methods and types that are common to the client and server

  • Abstract interfaces containing the pointers to the implementations of the methods included in them.

    In our example, one abstract interface (Filesystem) will be generated:

    typedef struct Filesystem {

    const struct Filesystem_ops *ops;

    } Filesystem;

    typedef nk_err_t

    Filesystem_Open_fn(struct Filesystem *, const

    struct Filesystem_Open_req *,

    const struct nk_arena *,

    struct Filesystem_Open_res *,

    struct nk_arena *);

    typedef struct Filesystem_ops {

    Filesystem_Open_fn *Open;

    } Filesystem_ops;

  • Set of interface methods.

    When calling an interface method, the corresponding values of the RIID and MID are automatically inserted into the request.

    In our example, a single Filesystem_Open interface method will be generated:

    nk_err_t Filesystem_Open(struct Filesystem *self,

    struct Filesystem_Open_req *req,

    const

    struct nk_arena *req_arena,

    struct Filesystem_Open_res *res,

    struct nk_arena *res_arena)

Methods and types used only on the client

  • Types of proxy objects.

    A proxy object is used as an argument in an interface method. In our example, a single Filesystem_proxy proxy object type will be generated:

    typedef struct Filesystem_proxy {

    struct Filesystem base;

    struct nk_transport *transport;

    nk_iid_t iid;

    } Filesystem_proxy;

  • Functions for initializing proxy objects.

    In our example, the single initializing function Filesystem_proxy_init will be generated:

    void Filesystem_proxy_init(struct Filesystem_proxy *self,

    struct nk_transport *transport,

    nk_iid_t iid)

  • Types that define the structure of the constant part of a message for each specific method.

    In our example, two such types will be generated: Filesystem_Open_req (for a request) and Filesystem_Open_res (for a response).

    typedef struct __nk_packed Filesystem_Open_req {

    __nk_alignas(8)

    struct nk_message base_;

    __nk_alignas(4) nk_ptr_t name;

    } Filesystem_Open_req;

    typedef struct Filesystem_Open_res {

    union {

    struct {

    __nk_alignas(8)

    struct nk_message base_;

    __nk_alignas(4) nk_uint32_t h;

    };

    struct {

    __nk_alignas(8)

    struct nk_message base_;

    __nk_alignas(4) nk_uint32_t h;

    } res_;

    struct Filesystem_Open_err err_;

    };

    } Filesystem_Open_res;

Methods and types used only on the server

  • Type containing all endpoints of a component, and the initializing function. (For each server component.)

    If there are embedded components, this type also contains their instances, and the initializing function takes their corresponding initialized structures. Therefore, if embedded components are present, their initialization must begin with the most deeply embedded component.

    In our example, the Operations_component structure and Operations_component_init function will be generated:

    typedef struct Operations_component {

    struct Filesystem *FS;

    };

    void Operations_component_init(struct Operations_component *self,

    struct Filesystem *FS)

  • Type containing all endpoints provided directly by the server; all instances of components included in the server; and the initializing function.

    In our example, the Server_entity structure and Server_entity_init function will be generated:

    #define Server_entity Server_component

    typedef struct Server_component {

    struct : Operations_component *OpsComp;

    } Server_component;

    void Server_entity_init(struct Server_entity *self,

    struct Operations_component *OpsComp)

  • Types that define the structure of the constant part of a message for any method of a specific interface.

    In our example, two such types will be generated: Filesystem_req (for a request) and Filesystem_res (for a response).

    typedef union Filesystem_req {

    struct nk_message base_;

    struct Filesystem_Open_req Open;

    };

    typedef union Filesystem_res {

    struct nk_message base_;

    struct Filesystem_Open_res Open;

    };

  • Types that define the structure of the constant part of a message for any method of any endpoint of a specific component.

    If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.

    In our example, two such types will be generated: Operations_component_req (for a request) and Operations_component_res (for a response).

    typedef union Operations_component_req {

    struct nk_message base_;

    Filesystem_req FS;

    } Operations_component_req;

    typedef union Operations_component_res {

    struct nk_message base_;

    Filesystem_res FS;

    } Operations_component_res;

  • Types that define the structure of the constant part of a message for any method of any endpoint of a specific component whose instance is included in the server.

    If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.

    In our example, two such types will be generated: Server_entity_req (for a request) and Server_entity_res (for a response).

    #define Server_entity_req Server_component_req

    typedef union Server_component_req {

    struct nk_message base_;

    Filesystem_req OpsComp_FS;

    } Server_component_req;

    #define Server_entity_res Server_component_res

    typedef union Server_component_res {

    struct nk_message base_;

    Filesystem_res OpsComp_FS;

    } Server_component_res;

  • Dispatch methods (dispatchers) for a separate interface, component, or process class.

    Dispatchers analyze the received query (the RIID and MID values), call the implementation of the corresponding method, and then save the response in the buffer. In our example, three dispatchers will be generated: Filesystem_interface_dispatch, Operations_component_dispatch, and Server_entity_dispatch.

    The process class dispatcher handles the request and calls the methods implemented by this class. If the request contains an incorrect RIID (for example, an RIID for a different endpoint that this process class does not have) or an incorrect MID, the dispatcher returns NK_EOK or NK_ENOENT.

    nk_err_t Server_entity_dispatch(struct Server_entity *self,

    const

    struct nk_message *req,

    const

    struct nk_arena *req_arena,

    struct nk_message *res,

    struct nk_arena *res_arena)

    In special cases, you can use dispatchers of the interface and the component. They take an additional argument: interface implementation ID (nk_iid_t). The request will be handled only if the passed argument and RIID from the request match, and if the MID is correct. Otherwise, the dispatchers return NK_EOK or NK_ENOENT.

    nk_err_t Operations_component_dispatch(struct Operations_component *self,

    nk_iid_t iidOffset,

    const

    struct nk_message *req,

    const

    struct nk_arena *req_arena,

    struct nk_message *res,

    struct nk_arena *res_arena)

    nk_err_t Filesystem_interface_dispatch(struct Filesystem *impl,

    nk_iid_t iid,

    const

    struct nk_message *req,

    const

    struct nk_arena *req_arena,

    struct nk_message *res,

    struct nk_arena *res_arena)

Page top
[Topic transport_code_overview]