amboar.github.io

Software development notes

View My GitHub Profile

31 March 2023

Exploiting obmc-console service units to expose multiple host consoles to the BMC network

by Andrew

Overview

obmc-console provides the plumbing to expose one or more host consoles onto BMC’s network interfaces. It comes in two parts:

  1. obmc-console-server
  2. obmc-console-client

TTY devices do not have sensible semantics when opened across multiple processes. obmc-console-server is a system daemon that acts as a client multiplexer - it is the single process that opens an upstream TTY along with a listening AF_UNIX socket on which other applications can connect. Data is shuffled bi-directionally between the upstream TTY and the connections accepted on the AF_UNIX socket.

obmc-console-client is the typical process that connects to the AF_UNIX socket exposed by obmc-console-server. We pair obmc-console-client with dropbear to expose the upstream TTY on the BMC’s network.

Instantiations of obmc-console-server

obmc-console ships systemd service units that cater to systems with multiple upstream TTYs. Instantiation of the obmc-console-server daemons is done in two parts:

  1. A systemd service unit to start the daemon, and
  2. udev rules to trigger the service unit

The udev rules and the templated systemd service unit work in concert to determine what TTYs should have an obmc-console-server instance started. Whether or not an instance is started depends on:

  1. The kernel exposing the TTY device to trigger one of the obmc-console udev rules, and
  2. The existence of an obmc-console-server configuration file for the TTY

As a concrete example of the udev rules involved, the following triggers the instantiation of the obmc-console-server process for /dev/ttyVUART0, so long as both conditions listed above hold:

SUBSYSTEM=="tty", ATTRS{iomem_base}=="0x1E787000", ENV{SYSTEMD_WANTS}="obmc-console@ttyVUART0" SYMLINK+="ttyVUART0", TAG+="systemd"

We can see here that the template parameter for the service unit is the name of the TTY device of interest - the ttyVUART0 in ENV{SYSTEMD_WANTS}="obmc-console@ttyVUART0".

The gate on the existence of the configuration file is implemented in the obmc-console@.service unit as follows:

ConditionPathExists=/etc/obmc-console/server.%i.conf

Where ConditionPathExists is documented as:

ConditionPathExists=

Check for the existence of a file. If the specified absolute path name does not exist, the condition will fail. If the absolute path name passed to ConditionPathExists= is prefixed with an exclamation mark (“!”), the test is negated, and the unit is only started if the path does not exist.

Again, the systemd unit documentation specifies the behaviour of conditions as:

Before the unit is started, systemd will verify that the specified conditions and asserts are true. If not, the starting of the unit will be (mostly silently) skipped (in case of conditions), or aborted with an error message (in case of asserts). Failing conditions or asserts will not result in the unit being moved into the “failed” state.

By the documentation for template unit specifiers, the path specified in the template service unit’s condition is transformed by substition of %i to:

ConditionPathExists=/etc/obmc-console/server.ttyVUART0.conf

Finally, the ExecStart= line then passes the configuration file path as a command-line argument to obmc-console-server, allowing us to provide configurations that are specific to the instance for the TTY:

ExecStart=/usr/sbin/obmc-console-server --config /etc/obmc-console/server.%i.conf %i

Reified with our example unit template parameter ttyVUART0, this becomes:

ExecStart=/usr/sbin/obmc-console-server --config /etc/obmc-console/server.ttyVUART0.conf ttyVUART0

Instantiations of obmc-console-client

As mentioned in the Overview, obmc-console-client is paired with dropbear to expose console data to the network. Wrapping obmc-console-client up in dropbear gives us authentication, authorisation and encryption for data in motion all in one fell swoop, and keeps the details out of the implementation of obmc-console-client itself. This separation of concerns allows us to invoke obmc-console-client directly from a shell prompt on the BMC if we wish!

By the obmc-console-ssh@.service template unit, we invoke obmc-console-client from dropbear by configuring dropbear to force obmc-console-client as the session’s command:

ExecStart=/usr/sbin/dropbear -K 5 -r ${DROPBEAR_RSAKEY_DIR}/dropbear_rsa_host_key -c "/usr/bin/obmc-console-client -c /etc/obmc-console/client.%i.conf" -p %i -F $DROPBEAR_EXTRA_ARGS

From this we see we also configure the port on which a dropbear instance listens using the template parameter for the unit (-p %i). Essentially, this allows us configure new network console servers by running e.g. DESIRED_PORT=2210; systemctl enable obmc-console-ssh@${DESIRED_PORT}.service

Again by the “force command” (-c) option to dropbear in the ExecStart line, we find that obmc-console-client is configured to look for its configuration file at /etc/obmc-console/client.%i.conf, subject to template substitution. Reified with our example unit template parameter, this becomes /etc/obmc-console/client.2210.conf

Connecting Clients and Servers

Above we’ve explored how each of the clients and servers are instantiated, but the missing piece is how an obmc-console-client knows the obmc-console-server to which it should connect. This comes down to a correlation key, known currently as socket-id, which is specfied in both the client and server configuration files:

2 16:34:33 andrew@fedora:~/src/openbmc/openbmc ((e6970b4909a2...)) $ \
> git grep socket-id meta-ibm/ | grep p10bmc
meta-ibm/recipes-phosphor/console/obmc-console/p10bmc/client.2201.conf:socket-id = hypervisor
meta-ibm/recipes-phosphor/console/obmc-console/p10bmc/server.ttyVUART1.conf:socket-id = hypervisor

In terms of the current implementation, the value of socket-id is used to construct the abstract socket name published by the server and connected to by the client in the common code between the client and the server.

Conclusion

Combined, the template service units for obmc-console-server and obmc-console-client allow us to expose multiple upstream TTYs to the network in an arbitrary arrangement. All that is required to do so is to:

  1. Place a server configuration file at /etc/obmc-console/server.${TTY}.conf
  2. Place a client configuration file at /etc/obmc-console/client.${PORT}.conf
  3. Ensure both the server and client configuration files specify a common socket-id
  4. systemctl enable obmc-console-ssh@${PORT}
  5. systemctl start obmc-console-ssh@${PORT}
tags: