CHIRP#

Notes on sockets#

Since CHIRP requires a fixed port and we might have multiple programs running CHIRP on one machine, it is important to ensure that the port is not blocked by one program. Networking libraries like ZeroMQ and NNG do this by default when binding to a wildcard address. To ensure that a socket can be used by more than one program, the SO_REUSEADDR socket option has to be enabled. Further, to send broadcasts the SO_BROADCAST socket option has to be enabled.

Notes on broadcast addresses#

CHIRP uses an UDP broadcast over port 7123, which means that it send a message to all participants in a network. However, “all” in this context depends the broadcast address.

For example, if you have a device with a fixed IP (e.g. 192.168.1.17) in a subnet (e.g. 255.255.255.0), the general broadcast address (255.255.255.255) does not work. Instead, the broadcast address for the specified subnet has to be used (e.g. 192.168.1.255). On Linux, the broadcast IP for a specific network interface can found for example by running ip a, it is the IP shown after brd.

To opposite to the broadcast address is the “any” address, which accepts incoming traffic from any IP. In general it can be deduced from the broadcast address by replacing all 255s with 0s. However, the default any address (0.0.0.0) is enough since message filtering has to be done anyway.

If no network (with DHCP) is available, the default broadcast address (255.255.255.255) does not work. As a workaround, the default any address (0.0.0.0) can be used to broadcast over localhost.

CHIRP Manager#

To run the CHIRP manager, run:

./build/cxx/constellation/tools/chirp_manager [CONSTELLATION_GROUP] [NAME] [BRD_ADDR] [ANY_ADDR]

The following commands are available:

  • list_registered_services: list of services registered by the user in the manager

  • list_discovered_services [SERVICE]: list of services discovered by the manager and are in the same group

  • register_service [SERVICE] [PORT]: register a service in the manager

  • register_callback [SERVICE]: register a discover callback for a service that prints the discovered service

  • request [SERVICE]: send a CHIRP request for a given service

  • unregister_service [SERVICE] [PORT]: unregister a service in the manager

  • unregister_callback [SERVICE]: unregister a discover callback for a service

  • reset: unregister all services and callbacks, and forget discovered services

  • quit

constellation::chirp Namespace#

enum class constellation::chirp::MessageType : std::uint8_t#

CHIRP message type

Values:

enumerator REQUEST#

A message with REQUEST type indicates that CHIRP hosts should reply with an OFFER

enumerator OFFER#

A message with OFFER type indicates that service is available

enumerator DEPART#

A message with DEPART type indicates that a service is no longer available

enum class constellation::chirp::ServiceIdentifier : std::uint8_t#

CHIRP service identifier

Values:

enumerator CONTROL#

The CONTROL service identifier indicates a CSCP (Constellation Satellite Control Protocol) service

enumerator HEARTBEAT#

The HEARTBEAT service identifier indicates a CHP (Constellation Heartbeat Protocol) service

enumerator MONITORING#

The MONITORING service identifier indicates a CMDP (Constellation Monitoring Distribution Protocol) service

enumerator DATA#

The DATA service identifier indicates a CDTP (Constellation Data Transmission Protocol) service

using constellation::chirp::Port = std::uint16_t#

Port number for a network connection

Note that since ports are allocated dynamically, the port number should range between 49152 and 65535, as these are reserved by the IANA for dynamic allocation or temporary use (see also https://en.wikipedia.org/wiki/Ephemeral_port).

using constellation::chirp::DiscoverCallback = void(DiscoveredService service, bool depart, std::any user_data)#

Function signature for user callback

The first argument (service) contains the discovered service, the second argument is a bool (depart) that is false when the service is newly and true when the service is departing, and the third argument (user_data) is arbitrary user data passed to the callback (done via Manager::RegisterDiscoverCallback).

It is recommended to pass the user data wrapped in an atomic std::shared_ptr since the callback is launched asynchronously in a detached std::thread. If the data is modified, it is recommended to use atomic types when possible or a std::mutex for locking to ensure thread-safe access.

constexpr std::string_view constellation::chirp::CHIRP_IDENTIFIER = "CHIRP"#

Protocol identifier for CHIRP

constexpr std::uint8_t constellation::chirp::CHIRP_VERSION = '\x01'#

Version of CHIRP protocol

constexpr Port constellation::chirp::CHIRP_PORT = 7123#

Port number of the CHIRP protocol

constexpr std::size_t constellation::chirp::CHIRP_MESSAGE_LENGTH = 42#

CHIRP Message length in bytes

struct BroadcastMessage#
#include <constellation/core/chirp/BroadcastRecv.hpp>

Incoming broadcast message

Public Functions

std::string to_string() const#

Convert the content of the broadcast message to a string

Public Members

std::vector<std::byte> content#

Content of the broadcast message in bytes

asio::ip::address_v4 address#

Address from which the broadcast message was received

class BroadcastRecv#
#include <constellation/core/chirp/BroadcastRecv.hpp>

Broadcast receiver for incoming CHIRP broadcasts on CHIRP_PORT

Public Functions

BroadcastRecv(const asio::ip::address_v4 &any_address, asio::ip::port_type port)#

Construct broadcast receiver

Parameters:
  • any_address – Address for incoming broadcasts (e.g. asio::ip::address_v4::any())

  • port – Port for outgoing broadcasts

BroadcastRecv(std::string_view any_ip, asio::ip::port_type port)#

Construct broadcast receiver using human readable IP address

Parameters:
  • any_ip – String containing the IP for incoming broadcasts (e.g. 0.0.0.0)

  • port – Port for outgoing broadcasts

BroadcastMessage recvBroadcast()#

Receive broadcast message (blocking)

Returns:

Received broadcast message

std::optional<BroadcastMessage> asyncRecvBroadcast(std::chrono::steady_clock::duration timeout)#

Receive broadcast message (asynchronously)

Parameters:

timeout – Duration for which to block function call

Returns:

Broadcast message if received

class BroadcastSend#
#include <constellation/core/chirp/BroadcastSend.hpp>

Broadcast sender for outgoing broadcasts

Public Functions

BroadcastSend(const asio::ip::address_v4 &brd_address, asio::ip::port_type port)#

Construct broadcast sender

Parameters:
  • brd_address – Broadcast address for outgoing broadcasts (e.g. asio::ip::address_v4::broadcast())

  • port – Port for outgoing broadcasts

BroadcastSend(std::string_view brd_ip, asio::ip::port_type port)#

Construct broadcast sender using human readable IP address

Parameters:
  • brd_ip – String containing the broadcast IP for outgoing broadcasts (e.g. 255.255.255.255)

  • port – Port for outgoing broadcasts

void sendBroadcast(std::string_view message)#

Send broadcast message from string

Parameters:

message – String with broadcast message

void sendBroadcast(std::span<const std::byte> message)#

Send broadcast message

Parameters:

message – View of message in bytes

struct DiscoverCallbackEntry#
#include <constellation/core/chirp/Manager.hpp>

Entry for a user callback in the Manager for newly discovered or departing services

Public Functions

bool operator<(const DiscoverCallbackEntry &other) const#

Public Members

DiscoverCallback *callback#

Function pointer to a callback

ServiceIdentifier service_id#

Service identifier of the service for which callbacks should be received

std::any user_data#

Arbitrary user data passed to the callback function

For information on lifetime and thread-safety see DiscoverCallback.

struct DiscoveredService#
#include <constellation/core/chirp/Manager.hpp>

A service discovered by the Manager

Public Functions

std::string to_uri() const#

Convert service information to a URI

bool operator<(const DiscoveredService &other) const#

Public Members

asio::ip::address_v4 address#

Address of the discovered service

message::MD5Hash host_id#

Host ID of the discovered service

ServiceIdentifier identifier#

Service identifier of the discovered service

utils::Port port#

Port of the discovered service

class Manager#
#include <constellation/core/chirp/Manager.hpp>

Manager for CHIRP broadcasting and receiving

Public Functions

void setAsDefaultInstance()#

Set this CHIRP manager as the default instance

Manager(const asio::ip::address_v4 &brd_address, const asio::ip::address_v4 &any_address, std::string_view group_name, std::string_view host_name)#
Parameters:
  • brd_address – Broadcast address for outgoing broadcast messages

  • any_address – Any address for incoming broadcast messages

  • group_name – Group name of the group to join

  • host_name – Host name for outgoing messages

Manager(std::string_view brd_ip, std::string_view any_ip, std::string_view group_name, std::string_view host_name)#
Parameters:
  • brd_ip – Broadcast IP for outgoing broadcast messages

  • any_ip – Any IP for incoming broadcast messages

  • group_name – Group name of the group to join

  • host_name – Host name for outgoing messages

virtual ~Manager()#
inline constexpr message::MD5Hash getGroupID() const#
Returns:

Group ID (MD5Hash of the group name)

inline constexpr message::MD5Hash getHostID() const#
Returns:

Host ID (MD5Hash of the host name)

void start()#

Start the background thread of the manager

bool registerService(ServiceIdentifier service_id, utils::Port port)#

Register a service offered by the host in the manager

Calling this function sends a CHIRP broadcast with OFFER type, and registers the service such that the manager responds to CHIRP broadcasts with REQUEST type and the corresponding service identifier.

Parameters:
  • service_id – Service identifier of the offered service

  • port – Port of the offered service

Return values:
  • true – if the service was registered

  • false – if the service was already registered

bool unregisterService(ServiceIdentifier service_id, utils::Port port)#

Unregister a previously registered service offered by the host in the manager

Calling this function sends a CHIRP broadcast with DEPART type and removes the service from manager. See also RegisterService.

Parameters:
  • service_id – Service identifier of the previously offered service

  • port – Port of the previously offered service

Return values:
  • true – If the service was unregistered

  • false – If the service was never registered

void unregisterServices()#

Unregisters all offered services registered in the manager

Equivalent to calling UnregisterService for every registered service.

std::set<RegisteredService> getRegisteredServices()#

Get the list of services currently registered in the manager

Returns:

Set with all currently registered services

bool registerDiscoverCallback(DiscoverCallback *callback, ServiceIdentifier service_id, std::any user_data)#

Register a user callback for newly discovered or departing services

Note that a callback function can be registered multiple times for different services.

Parameters:
  • callback – Function pointer to a callback

  • service_id – Service identifier of the service for which callbacks should be received

  • user_data – Arbitrary user data passed to the callback function (see DiscoverCallback)

Return values:
  • true – If the callback/service/user_data combination was registered

  • false – If the callback/service/user_data combination was already registered

bool unregisterDiscoverCallback(DiscoverCallback *callback, ServiceIdentifier service_id)#

Unegister a previously registered callback for newly discovered or departing services

Parameters:
  • callback – Function pointer to the callback of registered callback entry

  • service_id – Service identifier of registered callback entry

Return values:
  • true – If the callback entry was unregistered

  • false – If the callback entry was never registered

void unregisterDiscoverCallbacks()#

Unregisters all discovery callbacks registered in the manager

Equivalent to calling UnregisterDiscoverCallback for every discovery callback.

void forgetDiscoveredServices()#

Forgets all previously discovered services

std::vector<DiscoveredService> getDiscoveredServices()#

Returns list of all discovered services

Returns:

Vector with all discovered services

std::vector<DiscoveredService> getDiscoveredServices(ServiceIdentifier service_id)#

Returns list of all discovered services with a given service identifier

Parameters:

service_id – Service identifier for discovered services that should be listed

Returns:

Vector with all discovered services with the given service identifier

void sendRequest(ServiceIdentifier service_id)#

Send a discovery request for a specific service identifier

This sends a CHIRP broadcast with a REQUEST type and a given service identifier. Other hosts might reply with a CHIRP broadcast with OFFER type for the given service identifier. These can be retrieved either by registering a user callback (see RegisterDiscoverCallback) or by getting the list of discovered services shortly after (see GetDiscoveredServices).

Parameters:

service_id – Service identifier to send a request for

Public Static Functions

static Manager *getDefaultInstance()#

Return the default CHIRP Manager (requires to be set via setAsDefaultInstance)

Returns:

Pointer to default CHIRP Manager (might be a nullptr)

struct RegisteredService#
#include <constellation/core/chirp/Manager.hpp>

A service offered by the host and announced by the Manager

Public Functions

bool operator<(const RegisteredService &other) const#

Public Members

ServiceIdentifier identifier#

Service identifier of the offered service

utils::Port port#

Port of the offered service