Skip to content

GUACAMOLE-2300: Add IPMI Serial-over-LAN protocol support - #692

Open
ciroiriarte wants to merge 16 commits into
apache:mainfrom
ciroiriarte:feature/ipmi-protocol
Open

GUACAMOLE-2300: Add IPMI Serial-over-LAN protocol support#692
ciroiriarte wants to merge 16 commits into
apache:mainfrom
ciroiriarte:feature/ipmi-protocol

Conversation

@ciroiriarte

@ciroiriarte ciroiriarte commented Jul 13, 2026

Copy link
Copy Markdown

This adds a new ipmi protocol module to guacamole-server providing Serial-over-LAN (SOL) console access and chassis power management for BMCs (baseboard management controllers), built on FreeIPMI (libipmiconsole for SOL, libfreeipmi for chassis control).

JIRA: https://issues.apache.org/jira/browse/GUACAMOLE-2300

What's included

  • New libguac-client-ipmi protocol module — SOL console rendered through the shared guac_terminal.
  • Chassis control over a client-agnostic ipmi-control pipe channel (newline-delimited JSON): power on/off/cycle/reset, soft shutdown, diagnostic interrupt (NMI), chassis identify, System Event Log (SEL) viewer, and live power/SOL status.
  • In-terminal control menu (Ctrl+]) with an alternate-screen UI, run asynchronously so BMC round-trips never block the console.
  • Multivendor support: configurable cipher suite / privilege level / workaround flags, hex K_g, encryption policy. Verified end-to-end against Supermicro (ATEN), Lenovo XCC, and Dell iDRAC 9.
  • Connection parameters including persistent boot-device selection and keepalive interval.
  • CUnit tests for the control-channel JSON parser, wired into make check.

Testing

  • 12/12 unit tests pass.
  • AddressSanitizer clean on live SOL sessions. Two memory bugs were found and fixed during review: a NULL-deref on teardown, and a use-after-free that freed the terminal before joining the SOL worker.
  • Full-stack live regression (browser → guacamole-client → guacd → BMC): SOL console I/O, live status push, chassis identify, SEL read, and Send Break all verified non-destructively against a Supermicro BMC.

Related PRs

@necouchman necouchman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ciroiriarte This looks really great, I'm excited to have this in the Guacamole code! I do have some initial comments - don't despair, most of these are style and documentation clean-up, although some deal with implementation details. Generally:

  • In several places you've defined Guacamole-specific data structures or enums that mirror ones provided by the IPMI libraries. Is there any reason to do this? It seems like it would simplify things and cut down the amount of code to just reuse what is already there, unless there's a technical or implementation-specific reason why not?
  • Make sure you've fully-documented all of the functions and/or function prototypes. There are several places where thinks like @param and @return tags are missing, and a handful where there was no documentation.
  • There are a few places where I think some more verbose comments within the code would help people more easily understand what's being done and why.
  • I see what you're doing with the IPMI menu, but I wonder if there's a way to use Guacamole's built-in menu to handle these functions rather than implementing the text-based menu you have and then having to implement the JSON-related code around it? I haven't looked at this in any detail, it just seems to me that that's what our existing hidden Guacamole menu is meant for, and there ought to be a way to handle things like chassis commands and the like similar to how we handle file transfers, terminal parameters, etc.

Comment thread src/protocols/ipmi/tests/control/json_get.c
Comment thread src/protocols/ipmi/argv.c Outdated
Comment on lines +51 to +74
/**
* Maps a guac_ipmi_privilege_level to the corresponding libfreeipmi
* IPMI_PRIVILEGE_LEVEL_* constant.
*
* @param privilege_level
* The privilege level to map.
*
* @return
* The equivalent IPMI_PRIVILEGE_LEVEL_* constant.
*/
static uint8_t guac_ipmi_map_privilege_level(
guac_ipmi_privilege_level privilege_level) {

switch (privilege_level) {
case GUAC_IPMI_PRIVILEGE_USER:
return IPMI_PRIVILEGE_LEVEL_USER;
case GUAC_IPMI_PRIVILEGE_OPERATOR:
return IPMI_PRIVILEGE_LEVEL_OPERATOR;
case GUAC_IPMI_PRIVILEGE_ADMIN:
default:
return IPMI_PRIVILEGE_LEVEL_ADMIN;
}

}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason we need to define new values for these privilege levels as GUAC_ constants and shouldn't just re-use the IPMI_PRIVILEGE_LEVEL_ values throughout the code?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same privilege question — answered in full under the enum definition in settings.h (#692 (comment)). Short version: libipmiconsole and libfreeipmi disagree on the privilege values (0/1/2 vs 0x02/0x03/0x04), so reusing either is silently wrong on the other path. I'll keep an internal enum but give it arbitrary values and map explicitly on both paths.

Comment on lines +76 to +105
/**
* Maps a guac_ipmi_power_action to the corresponding libfreeipmi
* IPMI_CHASSIS_CONTROL_* constant.
*
* @param action
* The power action to map. Must not be GUAC_IPMI_POWER_NONE.
*
* @return
* The equivalent IPMI_CHASSIS_CONTROL_* constant.
*/
static uint8_t guac_ipmi_map_power_action(guac_ipmi_power_action action) {

switch (action) {
case GUAC_IPMI_POWER_ON:
return IPMI_CHASSIS_CONTROL_POWER_UP;
case GUAC_IPMI_POWER_OFF:
return IPMI_CHASSIS_CONTROL_POWER_DOWN;
case GUAC_IPMI_POWER_CYCLE:
return IPMI_CHASSIS_CONTROL_POWER_CYCLE;
case GUAC_IPMI_POWER_RESET:
return IPMI_CHASSIS_CONTROL_HARD_RESET;
case GUAC_IPMI_POWER_SOFT_SHUTDOWN:
return IPMI_CHASSIS_CONTROL_INITIATE_SOFT_SHUTDOWN;
case GUAC_IPMI_POWER_DIAGNOSTIC_INTERRUPT:
return IPMI_CHASSIS_CONTROL_PULSE_DIAGNOSTIC_INTERRUPT;
default:
return IPMI_CHASSIS_CONTROL_POWER_DOWN;
}

}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the privilege levels - is there a particular reason that we need to create and map them rather than just using the ones provided by FreeIPMI?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different rationale from the privilege levels here, and a weaker one — I'd rather be upfront about that than stretch the same argument to cover it. There's no conflicting-constants problem for power actions or boot devices; these enums are less a wrapper around the FreeIPMI constants than internal command state. Each carries a NONE member that has no IPMI equivalent (IPMI_CHASSIS_CONTROL_* can't express "no action requested"), and they're used well away from this mapping — parsing the client's command strings, the menu's display labels, and the pending-confirmation state in guac_ipmi_client. The translation to the FreeIPMI constant happens only here, at the boundary.

NONE could be modelled as optional/nullable state instead, which would let these collapse into the raw constants. I lean toward keeping them as internal state enums for the parsing and menu use, but if you'd prefer the nullable approach I'll switch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on this one so it isn't left hanging. Unlike the privilege enum, the power-action and boot-device enums have no conflicting-constants problem — they're internal command state, each with a NONE member that has no IPMI_CHASSIS_CONTROL_* equivalent, used in command-string parsing, the menu labels, and the pending-confirmation state, and translated to the FreeIPMI constant only at the boundary. I'm happy either way: keep them as internal state enums, or collapse them into the raw FreeIPMI constants with "no action" modeled as a nullable/optional value. Do you have a preference? I'll make the change if you'd rather drop them.

Comment thread src/protocols/ipmi/chassis.c
Comment thread src/protocols/ipmi/settings.c
Comment thread src/protocols/ipmi/settings.c Outdated
Comment thread src/protocols/ipmi/settings.h
Comment thread src/protocols/ipmi/ipmi.c Outdated
Comment thread src/protocols/ipmi/menu.c Outdated
ciroiriarte added a commit to ciroiriarte/guacamole-server that referenced this pull request Jul 17, 2026
Addresses the review on apache#692:

Behavior:
* Accept non-string JSON scalars (numbers, true/false/null) in the
  control-channel parser, so a valid message from a third-party client
  is no longer misread as a missing key.
* Return guac_ipmi_power_state directly from guac_ipmi_chassis_status()
  instead of a result code plus an out-parameter.
* Reject unknown control commands explicitly via a final else branch
  rather than falling through to the power-action path.
* Drop the vestigial libipmiconsole engine reference count and its
  mutex; guacd forks per connection, so the count could only ever be
  0->1. Init/teardown the engine directly.

Settings parsers:
* Make the privilege-level, power-action, boot-device, and
  encryption-policy parsers pure string-to-enum converters: no logging
  and no default policy. Each returns a new *_INVALID sentinel for
  NULL/empty/unrecognized input, and the default plus warning is applied
  by the caller. This also fixes a small leak where the parsed argument
  strings were never freed.
* Collapse the redundant "administrator" privilege alias to "admin".
* Remove the unused RMCP "port" setting entirely; both libipmiconsole
  and libfreeipmi hardwire 623/UDP, so it was read but never honored.

Documentation and style:
* Complete @param/@return documentation across control.c and settings.c,
  document the workaround struct members and each enum value, and drop
  the doc blocks duplicated from control.h.
* Remove the now-redundant "config.h" includes (auto-included since
  GUACAMOLE-2182).
* One case label per line, move file-level defines/typedefs to the top
  of chassis.c, and inline the menu.c static function rather than
  forward-declaring it.
* Add DEBUG log lines when no workaround flags or K_g key are given.
@ciroiriarte

Copy link
Copy Markdown
Author

Thanks again for the thorough review, @necouchman — I've pushed 86f6677 addressing the feedback, and replied inline on each thread. A summary of what changed:

Documentation

  • Completed @param/@return across control.c and settings.c, documented the workaround struct members and each enum value, and removed the doc blocks that duplicated control.h.

Style

  • Dropped the now-redundant config.h includes (auto-included since GUACAMOLE-2182), one case label per line, moved file-level #define/typedef declarations to the top of chassis.c, and inlined the menu.c static function instead of forward-declaring it.

Settings parsers

  • The privilege / power-action / boot-device / encryption-policy parsers are now pure string-to-enum converters — no logging, no defaults. Each returns a new *_INVALID sentinel and the default plus warning is applied by the caller (this also fixed a small leak where the parsed argument strings were never freed). Collapsed the administrator alias to admin.
  • Removed the unused port setting entirely — both libipmiconsole and libfreeipmi hardwire 623/UDP, so it was read but never honored.

Behavior (discussed inline)

  • Accept non-string JSON scalars in the control parser, return the power state directly from guac_ipmi_chassis_status(), reject unknown control commands explicitly, and drop the vestigial engine reference count/lock.

Still to come, from the enum discussion above: giving the privilege enum library-independent values and adding the symmetric guac_ipmi_map_privilege_level_ipmiconsole() mapper so neither back-end receives the raw enum value. I'll follow up with that shortly. Happy to iterate on anything here.

@ciroiriarte

Copy link
Copy Markdown
Author

@necouchman — following up on your suggestion to handle the chassis functions through Guacamole's built-in menu rather than the in-terminal text menu plus the JSON around it. I dug into how far that can go, and I'd like to agree the target before doing a large rework.

Where I landed: the strongest version of the point is really about the JSON, not just the text menu — guacd bundles no JSON parser, and the idiomatic control channel is argv name=value streams with GUAC_ARGV_OPTION_ECHO (which is what already backs the built-in menu for e.g. the ssh color-scheme/font params). So the hand-rolled json_get/json_escape is the non-idiomatic surface, and a good chunk of the control channel can move onto argv.

Fits the built-in argv/menu model cleanly:

  • boot-device, boot-persistent — genuine settings → argv params; surface in the built-in menu with no custom client code.
  • power-state, SOL health, identify state — push via OPTION_ECHO for display.

Doesn't fit argv:

  • The momentary chassis actions (power on/off/cycle/reset/soft-shutdown/diagnostic-interrupt, identify, send-break) and their per-command results ("sent" / "failed" / "operation already in progress"). argv has only ONCE/ECHO — no action or request→response semantics — and nothing in guacd exposes action-buttons via argv today (terminal params are settings; file-transfer uses file streams).
  • SEL is a multi-KB log; it fits a standard outbound pipe/download stream better than an embedded JSON blob.

Proposed plan:

  1. Move settings + state (boot-device, boot-persistent, power-state, health, identify) to argv — this deletes json_escape and most of the pushed-state JSON.
  2. Deliver SEL as a pipe/download stream rather than JSON.
  3. Keep a small, non-JSON channel only for the momentary actions + their results.

Two calls I'd like your read on before I start:

  • The momentary-action channel: OK to keep a minimal dedicated channel for the actions/results (argv can't express them), or would you rather I model actions as an argv trigger and drop the per-command result text, reflecting outcome only via the pushed power-state?
  • The in-terminal text menu (~446 lines): it's currently the only client-agnostic path — it works with any client or a plain terminal, no custom UI, whereas the built-in menu covers only custom-UI clients. Happy to drop it, but wanted to confirm you're comfortable requiring a custom client for chassis control rather than assume it.

Once you pick on those two, I'll rework accordingly.

…management

Implement a native `ipmi` protocol module (src/protocols/ipmi/) providing
IPMI 2.0 Serial-over-LAN console access via FreeIPMI libipmiconsole, modeled
on the terminal-based telnet module (guac_terminal for rendering, scrollback,
input; recording and typescript support).

Add standard chassis power management via libfreeipmi: power on/off/cycle/
reset/soft-shutdown/diagnostic-interrupt, next-boot device override, chassis
identify LED, and power status. These are exposed as pre-connect parameters
(power-on-connect, boot-device) applied after the SOL session attaches so the
user can watch the system boot, and on demand via an in-terminal control menu
(Ctrl+]) with y/N confirmation for destructive actions.

Wire the module into the autotools build behind --with-ipmi (ENABLE_IPMI),
detecting libipmiconsole and libfreeipmi, and add it to the configure summary.

Companion web-UI work is tracked in ciroiriarte/guacamole-client#1.

Implements #1.
Move @IPMI_LIBS@ (-lipmiconsole -lfreeipmi) from _LDFLAGS to _LIBADD so the
libraries follow the object files on the link line. With the GNU linker's
--as-needed behavior (default on openSUSE, and modern Debian/Ubuntu/Fedora),
libraries listed in LDFLAGS precede the objects that reference them and are
dropped, leaving them out of DT_NEEDED. The .so still builds, but guacd's
dlopen() then fails at runtime with 'undefined symbol:
tmpl_cmd_set_system_boot_options_rs' and reports the protocol as not installed.
…ine refcount, hex K_g

M1 hardening for real-world multivendor BMC support:

- workaround-flags parameter: comma-separated FreeIPMI workaround tokens and
  vendor presets (supermicro/intel/sun/dell/hpe/lenovo), resolved into separate
  libipmiconsole (SOL) and libfreeipmi (chassis) masks and applied to both
  sessions, replacing the hardcoded 0.
- encryption-policy parameter (required default / preferred / none): validates
  that the configured cipher suite provides confidentiality before connecting;
  aborts under 'required' for non-encrypting suites (0,1,2,6,7,8,11,15).
- Reference-counted, mutex-guarded libipmiconsole engine init/teardown so
  concurrent IPMI connections no longer tear down the shared engine out from
  under each other.
- K_g now accepts a 0x-prefixed hex value decoded to a raw byte buffer with an
  explicit length, so binary BMC keys (which may contain NUL bytes) are no
  longer truncated by strlen().
…al parameters

- boot-device-persistent: applies a boot device override persistently across
  all subsequent boots rather than only the next boot (the underlying chassis
  call already supported persistence; it was previously hardcoded to one-time).
- keepalive-interval: sets the SOL keepalive interval (seconds), mapped to
  libipmiconsole's keepalive_timeout_len. Prevents BMCs that silently drop idle
  sessions (e.g. Supermicro after ~60s) from tearing down the console.
…n control menu

- SEL viewer: guac_ipmi_chassis_sel() reads the most recent System Event Log
  entries via the libfreeipmi SEL parse API and formats them as text; exposed
  in the Ctrl+] control menu as [e] View Event Log. Invaluable for diagnosing
  crash/no-boot conditions while on the serial console.
- Alternate screen: the control menu now renders on the terminal's alternate
  screen buffer (CSI ?1049h/l), so it no longer pollutes the serial console's
  scrollback and session recording, nor corrupts a full-screen application
  drawn on the primary screen. Command output is held on the alternate screen
  until dismissed with a keypress, then the console is restored intact.
Chassis operations (power, status, identify, SEL) opened a fresh IPMI session
synchronously from the user input thread, freezing that user's input for the
3-5s RMCP+ handshake. They now run on a background worker thread: the menu
prints a 'working...' message immediately and renders the result when the
worker completes, keeping the console responsive.

Only one operation runs at a time (guarded by chassis_busy under menu_lock);
control-menu keystrokes are ignored while an operation is in flight. The worker
thread is joined in the free handler before the terminal is destroyed, so an
abrupt disconnect mid-operation cannot cause a use-after-free (verified by a
disconnect-during-op stress test). Serial break remains synchronous as it is
fast and operates on the existing SOL context.
…ic control/status

Implements the out-of-band control/status channel (#28)
that keeps chassis control and telemetry off the SOL console stream, so any
client (browser panel, native TUI) can render it natively. The in-terminal
Ctrl+] menu remains a portable fallback.

A client opens an inbound pipe named 'ipmi-control' and sends newline-delimited
JSON commands; the server pushes JSON state/result/SEL messages back on
outbound 'ipmi-control' pipes:

  {"id":"..","type":"command","command":"power-cycle"}
  {"type":"state","power":"on","health":"sol-connected"}
  {"id":"..","type":"result","ok":true,"message":".."}
  {"type":"sel","text":".."}

Commands: power on/off/cycle/hard-reset/soft-shutdown/diagnostic-interrupt,
identify, send-break, refresh-status, read-sel. Chassis operations serialize
with the Ctrl+] menu via the shared chassis lock, and the channel is only
available to non-read-only users (the pipe handler is not registered for
read-only). Validated end-to-end against a Supermicro BMC (state query,
identify, SEL read, error handling).

Follow-ups: structured per-entry SEL, async command dispatch, periodic state
polling / push-on-change, passive state broadcast to read-only viewers.
The out-of-band control channel reported SOL health as a one-shot
snapshot taken when the panel first opened the ipmi-control pipe. Since
the browser connects and opens that pipe while the SOL session is still
being established (the blocking ipmiconsole_engine_submit_block on the
client thread), the snapshot captured console_ctx == NULL and the panel
showed "SOL DISCONNECTED" even while the console streamed live output —
correcting only after a manual Refresh.

Track SOL liveness in a dedicated, mutex-guarded sol_connected flag
(separate from console_ctx, which stays non-NULL until teardown) and
broadcast the state to all connected users via guac_client_foreach_user()
immediately after the session establishes and again when the read loop
exits. The push is health-only (omits power) so it refreshes the
connection badge without regressing the last-known power state to
"unknown"; an actively-queried power value is still sent for
refresh-status.

Server-only change; the existing client handler already ignores an
absent power field. Verified against a Supermicro BMC: the badge reaches
SOL CONNECTED on its own with power intact, no manual refresh.
…nd EAGAIN

__guac_ipmi_write_all() treated any write() result <= 0 as fatal and
broke the input-forwarding loop, which would silently stop forwarding
terminal keystrokes to the SOL session on a transient condition. Handle
partial writes by continuing with the remainder, and, should the
libipmiconsole descriptor ever be non-blocking, wait for POLLOUT and
retry on EAGAIN/EWOULDBLOCK rather than dropping input. Any real error
or EOF remains fatal. No behavioural change for the current blocking
descriptor.
Add CUnit tests (wired into 'make check' like the Kubernetes/RDP module
tests) for the two pure helpers that parse untrusted, browser-supplied
control-channel messages: guac_ipmi_control_json_get() and
guac_ipmi_control_json_escape(), which are now exposed via control.h for
testing. Coverage includes key-vs-value matching (a token appearing only
as a value must not be read as a key), whitespace tolerance, escape
handling/unescaping, dropping of non-printable control characters, and
output-buffer bounds. 12 cases, all passing.
…terminal

guac_ipmi_client_free_handler() unconditionally called guac_terminal_free()
on ipmi_client->term, but the terminal is created by the client thread; if
the connection is torn down before that thread reaches terminal creation,
term is still NULL and guac_terminal_free() -> guac_terminal_stop()
dereferences it, crashing the connection process. Guard the free with a
NULL check. Found via an AddressSanitizer run over a rapid connect/teardown
of the ipmi-control channel; fix confirmed clean under ASAN.
…fore freeing the terminal

The connection free handler freed the terminal before joining the SOL
worker thread, and the join was gated on console_ctx being non-NULL. The
worker's read loop writes SOL output to the terminal (holding term->lock),
so freeing the terminal first could destroy the lock/buffers while the
worker was mid-write -> use-after-free. The conditional join also leaked
the joinable worker thread on every session-setup failure path (terminal
init, encryption-required abort, engine-ref failure, SOL failure), which
all leave console_ctx NULL.

Mirror the SSH module's ordering: close the SOL fd and guac_terminal_stop()
to unblock both threads without freeing the terminal, join the worker
unconditionally (tracked by a new client_thread_valid flag, like
chassis_thread_valid), then destroy the SOL context and finally free the
terminal. Found by independent code + security review; verified clean
under AddressSanitizer.
…ak, bounded printf

Address remaining code/security review findings:
- guac_ipmi_settings_free() now explicit_bzero()s the password and the
  decoded K_g BMC key before freeing, so these secrets do not linger in
  freed heap.
- send-break (control channel and Ctrl+] menu) now tests sol_connected
  under state_lock before dereferencing console_ctx, closing a narrow
  race with SOL teardown.
- argv.c uses snprintf() instead of sprintf() into the fixed font-size
  buffer.

Menu-state cross-user locking, alt-screen-vs-live-SOL suppression, and
the dead identify 'force' parameter are noted as follow-ups.
Thread-safety and cleanup items surfaced by the code/security review:

- Serialize the shared control-menu state (menu_open, menu_pending_action,
  menu_awaiting_dismiss) under menu_lock: the key handler now snapshots it
  in one short critical section and the menu open/close/confirm paths mutate
  it under the lock, eliminating the data race across concurrent user key
  threads and the chassis worker. Critical sections are kept short (no lock
  held across thread creation or terminal writes) to avoid deadlock.
- Suppress SOL console output while the control menu owns the alternate
  screen, so live serial data no longer scrambles the rendered menu.
- Serialize status/SEL reads with the other chassis operations (they now
  take the chassis lock too) so a single connection cannot open many
  concurrent RMCP+ sessions and exhaust the BMC's session slots.
- guac_ipmi_chassis_status() now outputs a decoded guac_ipmi_power_state
  enum; the control channel uses it instead of substring-matching the
  human-readable status string.
- Drop the always-false 'force' parameter from guac_ipmi_chassis_identify().

Built, unit-tested (12/12), functionally verified (status/identify/SEL),
and re-checked clean under AddressSanitizer (no UAF/leak/deadlock).
Addresses the review on apache#692:

Behavior:
* Accept non-string JSON scalars (numbers, true/false/null) in the
  control-channel parser, so a valid message from a third-party client
  is no longer misread as a missing key.
* Return guac_ipmi_power_state directly from guac_ipmi_chassis_status()
  instead of a result code plus an out-parameter.
* Reject unknown control commands explicitly via a final else branch
  rather than falling through to the power-action path.
* Drop the vestigial libipmiconsole engine reference count and its
  mutex; guacd forks per connection, so the count could only ever be
  0->1. Init/teardown the engine directly.

Settings parsers:
* Make the privilege-level, power-action, boot-device, and
  encryption-policy parsers pure string-to-enum converters: no logging
  and no default policy. Each returns a new *_INVALID sentinel for
  NULL/empty/unrecognized input, and the default plus warning is applied
  by the caller. This also fixes a small leak where the parsed argument
  strings were never freed.
* Collapse the redundant "administrator" privilege alias to "admin".
* Remove the unused RMCP "port" setting entirely; both libipmiconsole
  and libfreeipmi hardwire 623/UDP, so it was read but never honored.

Documentation and style:
* Complete @param/@return documentation across control.c and settings.c,
  document the workaround struct members and each enum value, and drop
  the doc blocks duplicated from control.h.
* Remove the now-redundant "config.h" includes (auto-included since
  GUACAMOLE-2182).
* One case label per line, move file-level defines/typedefs to the top
  of chassis.c, and inline the menu.c static function rather than
  forward-declaring it.
* Add DEBUG log lines when no workaround flags or K_g key are given.
guac_ipmi_privilege_level previously took the same numeric values as
libipmiconsole's IPMICONSOLE_PRIVILEGE_* constants, and the SOL path
passed the enum straight into the libipmiconsole config struct. That was
correct only by coincidence: libfreeipmi (used by the chassis path)
numbers its privilege constants differently, and any future reordering
of the enum or renumbering by the library would have silently sent the
wrong privilege level.

Give the enum library-independent values (INVALID = 0, so a
zero-initialized field is safe) and add guac_ipmi_map_privilege_level_
ipmiconsole(), mirroring the existing chassis-side mapper, so each
back-end is handed its own constant via an explicit, compiler-checked
switch rather than the raw enum value.
@ciroiriarte
ciroiriarte force-pushed the feature/ipmi-protocol branch from c29f725 to b53217e Compare August 9, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants