Sleds/K2Client/K2IPC.h

294 lines
12 KiB
C

/*
Copyright (c) 2013, 2014 IOnU Security Inc. All rights reserved
Created August 2013 by Kendrick Webster
K2IPC.h - header for commmon IPC declarations for K2 socket
notifier UDP protocol. This header is shared with the client
and the daemon.
Protocol overview
-----------------
The messaging protocol uses UDP packets with ping-pong ACK,
retries, and sequence numbers for reliable in-order delivery.
*/
#pragma once
/*
Packets consist of the following fields:
octets description
--------------------------------------------------------------------------
7 nonce (random initialization vector)
2 protocol version
3 client instance (for multiple clients sharing a UDP port)
1 sequence number (echoed in ACK)
1 opcode
<n> body, per opcode, NUL-terminated string
6 message integrity code (MIC)
All fields after the nonce are encrypted using an auto-hashing stream
cipher with a hard-coded key. The decrypted MIC will match only for
properly encrypted packets. Spurious packets fail the MIC check. This
'encryption' is used only for spurious packet rejection (not security).
Multi-byte fields are in network byte order (big endian).
*/
#define K2IPC_UDP_OVERHEAD 28 /* 20 bytes IPv4 header, 8 bytes UDP header */
#define K2IPC_MTU 576 /* IPv4 minimum is 576 bytes, IPv6 is 1280 */
#define K2IPC_PROTOCOL_VERSION 1
#define K2IPC_NONCE_SIZE 7
#define K2IPC_MIC_SIZE 6
#define K2IPC_CRYPT_SIZE (5 + 3 + K2IPC_MIC_SIZE) /* 3 octets for seq_num, opcode, NUL */
#define K2IPC_OVERHEAD_SIZE (K2IPC_CRYPT_SIZE + K2IPC_NONCE_SIZE)
/*
Packets are smaller than legacy ethernet MTU to avoid fragmentation,
minimize latency, and keep the memory footprint small.
*/
#define K2IPC_MAX_PACKET_SIZE 1440
#define K2IPC_MAX_STRING_LENGTH (K2IPC_MAX_PACKET_SIZE - K2IPC_OVERHEAD_SIZE)
/*
Some networks (i.e. 4G) can only handle small packets (dropping any that
exceed their size limit). The safest minimum size is 512 bytes since
this is used by DNS and other well-established protocols that have to be
supported everywhere.
Currently (6/17/14) there are two packet types that may exceed 512 bytes:
1) SUBSCRIBE_OFFICE (sent from client to daemon), and 2) ACK_QUERY_STATS
(sent from daemon to client). Since SUBSCRIBE_OFFICE packets may also
exceed 1440 bytes, code is already in place to handle fragmenting and
reassembling these messages. The ACK_QUERY_STATS messages, however, are
just small enough to fit in a 1440 byte packet. No fragmentation code
is in place for them (or for any daemon-to-client packet).
The ACK_QUERY_STATS messages are only used for monitoring and testing,
and only by bots and clients that are connected via a wired network
(never 4G). There is no need to use the smaller packet size for them.
To avoid having to write packet fragmentation and re-assembly code for
the daemon-to-client direction, a different size limit is being added
for client-to-daemon packets.
*/
#define K2IPC_MAX_PACKET_SIZE_FROM_CLIENTS 512
#define K2IPC_MAX_STRING_LENGTH_FROM_CLIENTS (K2IPC_MAX_PACKET_SIZE_FROM_CLIENTS - K2IPC_OVERHEAD_SIZE)
#define K2IPC_PACKET_OPCODES \
X(CONNECT) /* client --> daemon, body = ClientURN */ \
X(ACK_CONNECT) /* daemon --> client, body = ClientURN */ \
X(NAK_CONNECT) /* daemon --> client, body = ClientURN */ \
X(MESSAGE) /* daemon --> client, see comments below */ \
X(ACK_MESSAGE) /* client --> daemon, body = ClientURN */ \
X(LOGOUT) /* client --> daemon, body = ClientURN */ \
X(ACK_LOGOUT) /* daemon --> client, body = ClientURN */ \
X(POLL_DB) /* client --> daemon, body = ClientURN */ \
X(ACK_POLL_DB) /* daemon --> client, body = ClientURN */ \
X(HEARTBEAT) /* client --> daemon, body = ClientURN */ \
X(ACK_HEARTBEAT) /* daemon --> client, body = ClientURN */ \
X(SUBSCRIBE_OFFICE) /* client --> daemon, see comments below */ \
X(ACK_SUBSCRIBE_OFFICE) /* daemon --> client, body = ClientURN */ \
X(DEVICE_STATUS) /* daemon --> client, see comments below */ \
X(ACK_DEVICE_STATUS) /* client --> daemon, body = ClientURN */ \
X(QUERY_STATS) /* client --> daemon, body = ClientURN */ \
X(ACK_QUERY_STATS) /* daemon --> client, see comments below */ \
X(PING) /* daemon --> client, body = ClientURN */ \
X(ACK_PING) /* client --> daemon, body = ClientURN */
enum
{
#define X(op) K2IPC_##op,
K2IPC_PACKET_OPCODES
#undef X
};
/*
Common packet body types:
-------------------------
ClientURN
Device URN for the client. It must be included in all packets sent to the
daemon since the daemon uses it to lookup the client's state data. It is
also included in all ACK packets as a sanity check (to quickly draw
attention to coding errors).
Packet types:
-------------------------
CONNECT (client --> daemon)
Initial connection from client, clears subscription lists (as a failsafe
in case a logout packet is dropped) and resets sequence numbers. Normally
the daemon replies with ACK_CONNECT. If the URN is already in use (is
associated with a different IP/port), the daemon replies with NAK_CONNECT
and sends a PING to the old IP/port for the URN. In response, the client
receiving the NAK_CONNECT should retry the CONNECT after enough time has
elapsed for the PING to result in success or a timeout. If the retry also
results in a NAK_CONNECT, this indicates that the URN is already in use on
another device. If the client that is attempting the CONNECT merely had
its IP/port changed (i.e. switching network connections on a mobile device),
the PING will timeout and the 2nd CONNECT will succeed.
ACK_CONNECT (daemon --> client)
Normal response to CONNECT (acknowledges receipt).
NAK_CONNECT (daemon --> client)
Exception response to CONNECT -- Indicates that URN may already be in use by
another client. Client should retry CONNECT once after waiting enough time
for the daemon's PING attempt to timeout. If it receives another
NAK_CONNECT, the URN is in use elsewhere.
MESSAGE (daemon --> client)
Body is a NUL-terminated string (ASCII or UTF-8) copied from the 'info' field
in the MongoDB notification queue.
ACK_MESSAGE (client --> daemon)
Acknowledges receipt of MESSAGE.
LOGOUT (client --> daemon)
Client disconnect. Sent without waiting for an ACK (to avoid keeping the
client awake when it is entering sleep or shutting down). If lost, the
disconnect occurs upon heartbeat timeout or failed message delivery attempt.
ACK_LOGOUT (daemon --> client)
Acknowledges receipt of LOGOUT.
POLL_DB (client --> daemon)
Only sent by the CloudGuard client. This message tells the daemon to poll
the notification queue in MongoDB. Repeated requests arriving within a 100ms
window are consolidated into a single poll.
ACK_POLL_DB (daemon --> client)
Acknowledges receipt of POLL_DB.
HEARTBEAT (client --> daemon)
Keepalive for "online" client status. Also keeps NAT forwarding open.
Other message types can substitute for a heartbeat. A heartbeat packet is
sent after an absence of any traffic for the heartbeat timer interval.
ACK_HEARTBEAT (daemon --> client)
Acknowledges receipt of HEARTBEAT.
SUBSCRIBE_OFFICE (client --> daemon)
Body consists of ClientURN string followed by K2IPC_URN_DELIMITER followed
by FragmentationDescriptor followed by OfficeURN_list. OfficeURN_list is a
comma-delimited list of office URNs. This string may be fragmented across
multiple packets as indicated by FragmentationDescriptor. When a complete
list of offices is received by the daemon, it "subscribes" the client to that
list of offices (replacing any previous subscriptions). An empty list
cancels subscriptions.
ACK_SUBSCRIBE_OFFICE (daemon --> client)
Acknowledges receipt of SUBSCRIBE_OFFICE.
DEVICE_STATUS (daemon --> client)
Body is a StatusString + K2IPC_STATUS_DELIMITER + DeviceURN, for example
"online:urn:sl:000000:F4685A6B:8F4D:". The status string may be either
"online" or "offline". Sent when the status of a device within a subscribed
office changes.
ACK_DEVICE_STATUS (client --> daemon)
Acknowledges receipt of DEVICE_STATUS.
QUERY_STATS (client --> daemon)
Requests daemon CPU and memory usage statistics.
ACK_QUERY_STATS (daemon --> client)
Acknowledges receipt of QUERY_STATS. Body is a JSON string containing daemon
CPU and memory usage statistics.
PING (daemon --> client)
Checks whether client is reachable.
ACK_PING (client --> daemon)
Acknowledges receipt of PING.
*/
/* FragmentationDescriptor */
enum
{
K2IPC_FRAGMENT_FIRST = 'A', /* first of multiple packets */
K2IPC_FRAGMENT_MIDDLE, /* one of multiple packets */
K2IPC_FRAGMENT_LAST, /* last of multiple packets */
K2IPC_FRAGMENT_SINGLE /* first and last packet (not fragmented) */
};
/* K2IPC_SUBSCRIBE_OFFICE */
#define K2CLI_CHAR_CONSTANTS_EXTRACT
#define K2IPC_URN_DELIMITER ','
#define K2IPC_OFFICE_DELIMITER ','
#undef K2CLI_CHAR_CONSTANTS_EXTRACT
/* K2IPC_DEVICE_STATUS */
#define K2CLI_CHAR_CONSTANTS_EXTRACT
#define K2IPC_STATUS_DELIMITER ':'
#undef K2CLI_CHAR_CONSTANTS_EXTRACT
#define K2CLI_STRING_CONSTANTS_EXTRACT
#define K2IPC_STATUS_ONLINE "online"
#define K2IPC_STATUS_OFFLINE "offline"
#undef K2CLI_STRING_CONSTANTS_EXTRACT
/*
---------------------------
Timing, retries, heartbeats
*/
/* how often the client sends a 'heartbeat' packet or other traffic */
#define K2CLI_HEARTBEAT_INTERVAL_SECONDS 27
/* for de-synchronization, heartbeats are sent at the nominal rate +/= <RANGE> chosen randomly */
#define K2CLI_HEARTBEAT_RANGE_SECONDS 2
/* how long the daemon waits for traffic before assuming that the client is gone */
#define K2CLI_HEARTBEAT_TIMEOUT_SECONDS 45
/*
tenths of seconds the client waits for an ACK before retrying
xxx_RETRY1_xxx is the delay from original packet to the first retry
xxx_RETRY2_xxx is the delay from original packet to the second retry
xxx_RETRY3_xxx is the delay from original packet to the third and final retry
*/
#define K2CLI_RETRY1_TENTHS_OF_SECONDS 7
#define K2CLI_RETRY2_TENTHS_OF_SECONDS 13
#define K2CLI_RETRY3_TENTHS_OF_SECONDS 25
/* how long the client waits for an ACK before reporting a lost connection */
#define K2CLI_RETRY3_TIMEOUT_TENTHS_OF_SECONDS 25
/*
tenths of seconds the daemon waits for an ACK before retrying
xxx_RETRY1_xxx is the delay from original packet to the first retry
xxx_RETRY2_xxx is the delay from original packet to the second retry
xxx_RETRY3_xxx is the delay from original packet to the third and final retry
*/
#define K2DMN_RETRY1_TENTHS_OF_SECONDS 7
#define K2DMN_RETRY2_TENTHS_OF_SECONDS 13
#define K2DMN_RETRY3_TENTHS_OF_SECONDS 25
/* how long the daemon waits for an ACK before assuming that the client is gone */
#define K2DMN_RETRY3_TIMEOUT_TENTHS_OF_SECONDS 25
/*
Default server port number
There is nothing special about this port number.
It can be overridden at runtime via command-line.
*/
#define K2CLI_INT_CONSTANTS_EXTRACT
#define K2IPC_DEFAULT_UDP_PORT 32000
#define K2IPC_DEFAULT_HTTP_PORT 8888
#define K2IPC_DEFAULT_HTTPS_PORT 8443
#undef K2CLI_INT_CONSTANTS_EXTRACT
/*
packet encryption/validation key and MIC
(for rejecting spurious packets from port scanners, etc)
*/
#define K2IPC_KEY_MIX_CYCLES 32
#define K2IPC_KEY_SIZE 32
#define K2IPC_KEY \
169, 208, 1, 214, 130, 153, 218, 176, 187, 115, 186, 84, 117, 25, 162, 197, \
173, 73, 161, 180, 68, 224, 62, 241, 192, 96, 152, 79, 148, 239, 20, 132
#define K2IPC_MIC 17, 44, 195, 238, 248, 76