mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 13:37:55 +00:00
Merge #2671
Clean up docs/design/cc-protocol.txt Conflicts: doc/design/cc-protocol.txt
This commit is contained in:
@@ -1,296 +1,185 @@
|
|||||||
protocol version 0x536b616e
|
The CC protocol
|
||||||
|
===============
|
||||||
|
|
||||||
DATA 0x01
|
We use our home-grown protocol for IPC between modules. There's a
|
||||||
HASH 0x02
|
central daemon routing the messages.
|
||||||
LIST 0x03
|
|
||||||
NULL 0x04
|
|
||||||
TYPE_MASK 0x0f
|
|
||||||
|
|
||||||
LENGTH_32 0x00
|
Addressing
|
||||||
LENGTH_16 0x10
|
|
||||||
LENGTH_8 0x20
|
|
||||||
LENGTH_MASK 0xf0
|
|
||||||
|
|
||||||
|
|
||||||
MESSAGE ENCODING
|
|
||||||
----------------
|
|
||||||
|
|
||||||
When decoding, the entire message length must be known. If this is
|
|
||||||
transmitted over a raw stream such as TCP, this is usually encoded
|
|
||||||
with a 4-byte length followed by the message itself. If some other
|
|
||||||
wrapping is used (say as part of a different message structure) the
|
|
||||||
length of the message must be preserved and included for decoding.
|
|
||||||
|
|
||||||
The first 4 bytes of the message is the protocol version encoded
|
|
||||||
directly as a 4-byte value. Immediately following this is a HASH
|
|
||||||
element. The length of the hash element is the remainder of the
|
|
||||||
message after subtracting 4 bytes for the protocol version.
|
|
||||||
|
|
||||||
This initial HASH is intended to be used by the message routing system
|
|
||||||
if one is in use.
|
|
||||||
|
|
||||||
|
|
||||||
ITEM TYPES
|
|
||||||
----------
|
----------
|
||||||
|
|
||||||
There are four basic types encoded in this protocol. A simple data
|
Each connected client gets an unique address, called ``l-name''. A
|
||||||
blob (DATA), a tag-value series (HASH), an ordered list (LIST), and
|
message can be sent directly to such l-name, if it is known to the
|
||||||
a NULL type (which is used internally to encode DATA types which are
|
sender.
|
||||||
empty and can be used to indicate existance without data in a hash.)
|
|
||||||
|
|
||||||
Each item can be of any type, so a hash of hashes and hashes of lists
|
A client may subscribe to a group of communication. A message can be
|
||||||
are typical.
|
broadcasted to a whole group instead of a single client. There's also
|
||||||
|
an instance parameter to addressing, but we didn't find any actual use
|
||||||
|
for it and it is not used for anything. It is left in the default `*`
|
||||||
|
for most of our code and should be done so in any new code. It wasn't
|
||||||
|
priority to remove it yet.
|
||||||
|
|
||||||
All multi-byte integers which are encoded in binary are in network
|
Wire format
|
||||||
byte order.
|
-----------
|
||||||
|
|
||||||
|
Each message on the wire looks like this:
|
||||||
|
|
||||||
ITEM ENCODING
|
<message length><header length><header><body>
|
||||||
-------------
|
|
||||||
|
|
||||||
Each item is preceeded by a single byte which describes that item.
|
The message length is 4-byte unsigned integer in network byte order,
|
||||||
This byte contains the item type and item length encoding:
|
specifying the number of bytes of the rest of the message (eg. header
|
||||||
|
length, header and body put together).
|
||||||
|
|
||||||
Thing Length Description
|
The header length is 2-byte unsigned integer in network byte order,
|
||||||
---------------- -------- ------------------------------------
|
specifying the length of the header.
|
||||||
TyLen 1 byte Item type and length encoding
|
|
||||||
Length variable Item data blob length
|
|
||||||
Item Data variable Item data blob
|
|
||||||
|
|
||||||
The TyLen field includes both the item data type and the item's
|
The header is a string representation of single JSON object. It
|
||||||
length. The length bytes are encoded depending on the length of data
|
specifies the type of message and routing information.
|
||||||
portion, and the smallest data encoding type supported should be
|
|
||||||
used. Note that this length compression is used just for data
|
|
||||||
compactness. It is wasteful to encode the most common length (8-bit
|
|
||||||
length) as 4 bytes, so this method allows one byte to be used rather
|
|
||||||
than 4, three of which are nearly always zero.
|
|
||||||
|
|
||||||
|
The body is the payload of the message. It takes the whole rest of
|
||||||
|
size of the message (so its length is message length - 2 - header
|
||||||
|
length). The content is not examined by the routing daemon, but the
|
||||||
|
clients expect it to be valid JSON object.
|
||||||
|
|
||||||
HASH
|
The body may be empty in case the message is not to be routed to
|
||||||
----
|
client, but it is instruction for the routing daemon. See message
|
||||||
|
types below.
|
||||||
|
|
||||||
This is a tag/value pair where each tag is an opaque unique blob and
|
The message is sent in this format to the routing daemon, the daemon
|
||||||
the data elements are of any type. Hashes are not encoded in any
|
optionally modifies the headers and delivers it in the same format to
|
||||||
specific tag or item order.
|
the recipient(s).
|
||||||
|
|
||||||
The length of the HASH's data area is processed for tag/value pairs
|
The headers
|
||||||
until the entire area is consumed. Running out of data prematurely
|
-----------
|
||||||
indicates an incorrectly encoded message.
|
|
||||||
|
|
||||||
The data area consists of repeated items:
|
The header object can contain following information:
|
||||||
|
|
||||||
Thing Length Description
|
|====================================================================================================
|
||||||
---------------- -------- ------------------------------------
|
|Name |type |Description
|
||||||
Tag Length 1 byte The length of the tag.
|
|====================================================================================================
|
||||||
Tag Variable The tag name
|
|from |string|Sender's l-name
|
||||||
Item Variable Encoded item
|
|type |string|Type of the message. The routed message is "send".
|
||||||
|
|group |string|The group to deliver to.
|
||||||
The Tag Length field is always one byte, which limits the tag name to
|
|instance |string|Instance in the group. Purpose lost in history. Defaults to "*".
|
||||||
255 bytes maximum. A tag length of zero is invalid.
|
|to |string|Override recipient (group/instance ignored).
|
||||||
|
|seq |int |Tracking number of the message.
|
||||||
|
|reply |int |If present, contains a seq number of message this is a reply to.
|
||||||
LIST
|
|want_answer|bool |If present and true, the daemon generates error if there's no matching recipient.
|
||||||
----
|
|====================================================================================================
|
||||||
|
|
||||||
A LIST is a list of items encoded and decoded in a specific order.
|
|
||||||
The order is chosen entirely by the source curing encoding.
|
|
||||||
|
|
||||||
The length of the LIST's data is consumed by the ITEMs it contains.
|
|
||||||
Running out of room prematurely indicates an incorrectly encoded
|
|
||||||
message.
|
|
||||||
|
|
||||||
The data area consists of repeated items:
|
|
||||||
|
|
||||||
Thing Length Description
|
|
||||||
-------------- ------ ----------------------------------------
|
|
||||||
Item Variable Encoded item
|
|
||||||
|
|
||||||
|
|
||||||
DATA
|
|
||||||
----
|
|
||||||
|
|
||||||
A DATA item is a simple blob of data. No further processing of this
|
|
||||||
data is performed by this protocol on these elements.
|
|
||||||
|
|
||||||
The data blob is the entire data area. The data area can be 0 or more
|
|
||||||
bytes long.
|
|
||||||
|
|
||||||
It is typical to encode integers as strings rather than binary
|
|
||||||
integers. However, so long as both sender and recipient agree on the
|
|
||||||
format of the data blob itself, any blob encoding may be used.
|
|
||||||
|
|
||||||
|
|
||||||
NULL
|
|
||||||
----
|
|
||||||
|
|
||||||
This data element indicates no data is actually present. This can be
|
|
||||||
used to indicate that a tag is present in a HASH but no data is
|
|
||||||
actually at that location, or in a LIST to indicate empty item
|
|
||||||
positions.
|
|
||||||
|
|
||||||
There is no data portion of this type, and the encoded length is
|
|
||||||
ignored and is always zero.
|
|
||||||
|
|
||||||
Note that this is different than a DATA element with a zero length.
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE
|
|
||||||
-------
|
|
||||||
|
|
||||||
This is Ruby syntax, but should be clear enough for anyone to read.
|
|
||||||
|
|
||||||
Example data encoding:
|
|
||||||
|
|
||||||
{
|
|
||||||
"from" => "sender@host",
|
|
||||||
"to" => "recipient@host",
|
|
||||||
"seq" => 1234,
|
|
||||||
"data" => {
|
|
||||||
"list" => [ 1, 2, nil, "this" ],
|
|
||||||
"description" => "Fun for all",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Wire-format:
|
|
||||||
|
|
||||||
In this format, strings are not shown in hex, but are included "like
|
|
||||||
this." Descriptions are written (like this.)
|
|
||||||
|
|
||||||
Message Length: 0x64 (100 bytes)
|
|
||||||
Protocol Version: 0x53 0x6b 0x61 0x6e
|
|
||||||
(remaining length: 96 bytes)
|
|
||||||
|
|
||||||
0x04 "from" 0x21 0x0b "sender@host"
|
|
||||||
0x02 "to" 0x21 0x0e "recipient@host"
|
|
||||||
0x03 "seq" 0x21 0x04 "1234"
|
|
||||||
0x04 "data" 0x22
|
|
||||||
0x04 "list" 0x23
|
|
||||||
0x21 0x01 "1"
|
|
||||||
0x21 0x01 "2"
|
|
||||||
0x04
|
|
||||||
0x21 0x04 "this"
|
|
||||||
0x0b "description" 0x0b "Fun for all"
|
|
||||||
|
|
||||||
|
|
||||||
MESSAGE ROUTING
|
|
||||||
---------------
|
|
||||||
|
|
||||||
The message routing daemon uses the top-level hash to contain routing
|
|
||||||
instructions and additional control data. Not all of these are
|
|
||||||
required for various control message types; see the individual
|
|
||||||
descriptions for more information.
|
|
||||||
|
|
||||||
Tag Description
|
|
||||||
------- ----------------------------------------
|
|
||||||
msg Sender-supplied data
|
|
||||||
from sender's identity
|
|
||||||
group Group name this message is being sent to
|
|
||||||
instance Instance in this group
|
|
||||||
repl if present, this message is a reply.
|
|
||||||
seq sequence number, used in replies
|
|
||||||
to recipient or "*" for no specific receiver
|
|
||||||
type "send" for a channel message
|
|
||||||
|
|
||||||
|
|
||||||
"type" is a DATA element, which indicates to the message routing
|
|
||||||
system what the purpose of this message is.
|
|
||||||
|
|
||||||
|
Types of messages
|
||||||
|
-----------------
|
||||||
|
|
||||||
Get Local Name (type "getlname")
|
Get Local Name (type "getlname")
|
||||||
--------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Upon connection, this is the first message to be sent to the control
|
Upon connection, this is the first message to be sent to the daemon.
|
||||||
daemon. It will return the local name of this client. Each
|
It will return the local name of this client. Each connection gets
|
||||||
connection gets its own unique local name, and local names are never
|
its own unique local name, and local names are never repeated. They
|
||||||
repeated. They should be considered opaque strings, in a format
|
should be considered opaque strings, in a format useful only to the
|
||||||
useful only to the message routing system. They are used in replies
|
message routing system. They are used in replies or to send to a
|
||||||
or to send to a specific destination.
|
specific destination.
|
||||||
|
|
||||||
To request the local name, the only element included is the
|
To request the local name, the only element included is the
|
||||||
"type" => "getlname"
|
{"type": "getlname"}
|
||||||
tuple. The response is also a simple, single tuple:
|
tuple. The response is also a simple, single tuple:
|
||||||
"lname" => "UTF-8 encoded local name blob"
|
{"lname" => "Opaque utf-8 string"}
|
||||||
|
|
||||||
Until this message is sent, no other types of messages may be sent on
|
Until this message is sent, no other types of messages may be sent on
|
||||||
this connection.
|
this connection.
|
||||||
|
|
||||||
|
|
||||||
Regular Group Messages (type "send")
|
Regular Group Messages (type "send")
|
||||||
------------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
When sending a message:
|
Message routed to other client. This one expects the body to be
|
||||||
|
non-empty.
|
||||||
|
|
||||||
"msg" is the sender supplied data. It is encoded as per its type.
|
Expected headers are:
|
||||||
It is a required field, but may be the NULL type if not needed.
|
|
||||||
In OpenReg, this was another wire format message, stored as an
|
|
||||||
ITEM_DATA. This was done to make it easy to decode the routing
|
|
||||||
information without having to decode arbitrary application-supplied
|
|
||||||
data, but rather treat this application data as an opaque blob.
|
|
||||||
|
|
||||||
"from" is a DATA element, and its value is a UTF-8 encoded sender
|
* from
|
||||||
identity. It MUST be the "local name" supplied by the message
|
* group
|
||||||
routing system upon connection. The message routing system will
|
* instance (set to "*" if no specific instance desired)
|
||||||
enforce this, but will not add it. It is a required field.
|
* seq (should be unique for the sender)
|
||||||
|
* to (set to "*" if not directed to specific client)
|
||||||
"group" is a DATA element, and its value is the UTF-8 encoded group
|
* reply (optional, only if it is reply)
|
||||||
name this message is being transmitted to. It is a required field for
|
* want_answer (optional, only when not a reply)
|
||||||
all messages of type "send".
|
|
||||||
|
|
||||||
"instance" is a DATA element, and its value is the UTF-8 encoded
|
|
||||||
instance name, with "*" meaning all instances.
|
|
||||||
|
|
||||||
"repl" is the sequence number being replied to, if this is a reply.
|
|
||||||
|
|
||||||
"seq" is a unique identity per client. That is, the <lname, seq>
|
|
||||||
tuple must be unique over the lifetime of the connection, or at least
|
|
||||||
over the lifetime of the expected reply duration.
|
|
||||||
|
|
||||||
"to" is a DATA element, and its value is a UTF-8 encoded recipient
|
|
||||||
identity. This must be a specific recipient name or "*" to indicate
|
|
||||||
"all listeners on this channel." It is a required field.
|
|
||||||
|
|
||||||
When a message of type "send" is received by the client, all the data
|
|
||||||
is used as above. This indicates a message of the given type was
|
|
||||||
received.
|
|
||||||
|
|
||||||
A client does not see its own transmissions. (XXXMLG Need to check this)
|
|
||||||
|
|
||||||
|
A client does not see its own transmissions.
|
||||||
|
|
||||||
Group Subscriptions (type "subscribe")
|
Group Subscriptions (type "subscribe")
|
||||||
--------------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A subscription requires the "group", "instance", and a flag to
|
Indicates the sender wants to be included in the given group.
|
||||||
indicate the subscription type ("subtype"). If instance is "*" the
|
|
||||||
instance name will be ignored when deciding to forward a message to
|
|
||||||
this client or not.
|
|
||||||
|
|
||||||
"subtype" is a DATA element, and contains "normal" for normal channel
|
Expected headers are:
|
||||||
subscriptions, "meonly" for only those messages on a channel with the
|
|
||||||
recipient specified exactly as the local name, or "promisc" to receive
|
|
||||||
all channel messages regardless of other filters. As its name
|
|
||||||
implies, "normal" is for typical subscriptions, and "promisc" is
|
|
||||||
intended for channel message debugging.
|
|
||||||
|
|
||||||
There is no response to this message.
|
* group
|
||||||
|
* instance (leave at "*" for default)
|
||||||
|
|
||||||
|
There is no response to this message and the client is subscribed to
|
||||||
|
the given group and instance.
|
||||||
|
|
||||||
|
The group can be any utf-8 string and the group doesn't have to exist
|
||||||
|
before (it is created when at least one client is in it). A client may
|
||||||
|
be subscribed in multiple groups.
|
||||||
|
|
||||||
Group Unsubscribe (type "unsubscribe")
|
Group Unsubscribe (type "unsubscribe")
|
||||||
-------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The fields to be included are "group" and "instance" and have the same
|
The headers to be included are "group" and "instance" and have the same
|
||||||
meaning as a "subscribe" message.
|
meaning as a "subscribe" message. Only, the client is removed from the
|
||||||
|
group.
|
||||||
|
|
||||||
There is no response to this message.
|
Transmitted messages
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
These are the messages generally transmitted in the body of the
|
||||||
|
message.
|
||||||
|
|
||||||
Statistics (type "stats")
|
Command
|
||||||
-------------------------
|
~~~~~~~
|
||||||
|
|
||||||
Request statistics from the message router. No other fields are
|
It is a command from one process to another, to do something or send
|
||||||
inclued in the request.
|
some information. It is identified by a name and can optionally have
|
||||||
|
parameters. It'd look like this:
|
||||||
|
|
||||||
The response contains a single element "stats" which is an opaque
|
{"command": ["name", <parameters>]}
|
||||||
element. This is used mostly for debugging, and its format is
|
|
||||||
specific to the message router. In general, some method to simply
|
The parameters may be omitted (then the array is 1 element long). If
|
||||||
dump raw messages would produce something useful during debugging.
|
present, it may be any JSON element. However, the most usual is an
|
||||||
|
object with named parameter values.
|
||||||
|
|
||||||
|
It is usually transmitted with the `want_answer` header turned on to
|
||||||
|
cope with the situation the remote end doesn't exist, and sent to a
|
||||||
|
group (eg. `to` with value of `*`).
|
||||||
|
|
||||||
|
Success reply
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When the command is successful, the other side answers by a reply of
|
||||||
|
the following format:
|
||||||
|
|
||||||
|
{"result": [0, <result>]}
|
||||||
|
|
||||||
|
The result is the return value of the command. It may be any JSON
|
||||||
|
element and it may be omitted (for the case of ``void'' function).
|
||||||
|
|
||||||
|
This is transmitted with the `reply` header set to the `seq` number of
|
||||||
|
the original command. It is sent with the `to` header set.
|
||||||
|
|
||||||
|
Error reply
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
In case something goes wrong, an error reply is sent. This is similar
|
||||||
|
as throwing an exception from local function. The format is similar:
|
||||||
|
|
||||||
|
{"result": [ecode, "Error description"]}
|
||||||
|
|
||||||
|
The `ecode` is non-zero error code. Most of the current code uses `1`
|
||||||
|
for all errors. The string after that is mandatory and must contain a
|
||||||
|
human-readable description of the error.
|
||||||
|
|
||||||
|
The negative error codes are reserved for errors from the daemon.
|
||||||
|
Currently, only `-1` is used and it is generated when a message with
|
||||||
|
`reply` not included is sent, it has the `want_answer` header set to
|
||||||
|
`true` and there's no recipient to deliver the message to. This
|
||||||
|
usually means a command was sent to a non-existent recipient.
|
||||||
|
Reference in New Issue
Block a user