# STCPv8 — runtime module parameters and socket options

This reference describes the public module parameters and SOL_STCP socket
options in the supplied Linux/Raspberry Pi implementation. Zephyr Kconfig is
a separate interface. An installed module may come from another revision:
check `modinfo stcp` and `/sys/module/stcp/parameters/` on the target.

## 1. Runtime module parameters

Parameters belong to the module instance on each machine. Configure Linux
and Raspberry Pi separately. All parameters below have sysfs permissions
`0644`: readable by other users and writable by root.

```bash
# Read the running module's parameter values.
for p in /sys/module/stcp/parameters/*; do
    printf '%s = ' "${p##*/}"
    cat "$p"
done

# Change settings without reloading the module.
printf '2\n' | sudo tee /sys/module/stcp/parameters/default_compression_level
printf 'Y\n' | sudo tee /sys/module/stcp/parameters/verbose_debug

# Disable additional diagnostics.
printf 'N\n' | sudo tee /sys/module/stcp/parameters/verbose_debug
```

Boolean parameters accept values such as `Y`/`N` and `1`/`0`. Runtime sysfs
changes do not survive a module reload or reboot.

| Parameter | Type / default | Accepted or recommended values | Effect |
|---|---|---|---|
| `default_compression_level` | unsigned int, `3` | `1..5`; other values rejected | Maximum compression effort for adaptive AUTO sockets. The selected level is recomputed from each payload, within this ceiling. Existing default-following sockets refresh the ceiling on their next `sendmsg()` call. Does not enable compression or override explicit socket levels. |
| `verbose_debug` | bool, `N` | `Y` / `N` | Enables additional RX/TX and hot-path diagnostics for subsequent events. Does not suppress all other STCP logging or reset finite trace budgets. Logging can affect CPU load and benchmark results. |
| `drop_percent` | unsigned int, `0` | Recommended `0..100`; higher values are accepted but treated as 100 | Deterministically drops a proportion of outgoing STCP-UDP DATA frames. `0` disables this drop policy. Changes also affect existing UDP connections. |
| `drop_first_data` | bool, `N` | `Y` / `N` | One-shot UDP DATA drop test. Its event budget is armed only during module initialization. |
| `duplicate_first_data` | bool, `N` | `Y` / `N` | One-shot UDP DATA duplication test. Its event budget is armed only during module initialization. |
| `reorder_first_pair` | bool, `N` | `Y` / `N` | Holds one UDP DATA frame and sends the next DATA frame first. Its event budget is armed only during module initialization. |
| `delay_first_data_ms` | unsigned int, `0` | Milliseconds; `0` means no delay. No separate small upper bound. | Delays one UDP DATA send with `msleep()`. The budget is armed at load time if the value is nonzero. Large values can exceed test or connection deadlines. |

### OFF, AUTO and a socket-specific level

- **OFF:** no compression attempt is made and outgoing content is not compressed. A stored compression level does not enable compression.
- **Adaptive AUTO:** the socket samples each eligible payload and selects no compression or a suitable level. The module default is an effort ceiling, not the level used for every payload. Peer support and the size threshold are checked before sampling.
- **AUTO with an explicit level:** the socket uses its own selected level. Changes to the module default do not override it. AUTO still sends the original content if compression would not reduce its size.
- Setting a level does not change OFF/AUTO mode. Switching from OFF to AUTO preserves an explicitly selected socket-specific level.

Even in OFF mode, the implementation may refresh the stored level metadata
of a default-following socket at its next send. This does not call the
compression function: `CompressionConfig::should_try()` always requires AUTO.

### Traffic adaptation in AUTO

With the AUTO adaptation update installed, each eligible outgoing payload is
sampled independently. Four distributed 32-byte slices are compressed with
level 1 to estimate compressibility. The sample selects no compression or a
level from L1 to L5. The module default sets the maximum permitted effort:
default 3 permits no compression or L1–L3; default 5 permits the full range.
This ceiling only applies to sockets without an explicit user-selected level.

An explicit socket level bypasses adaptive sampling and remains fixed. OFF
bypasses both sampling and compression. Peer support and the configured size
threshold are checked before any sample. Eligible payloads shorter than the
128-byte probe use the configured level directly. The final representation
must still be smaller than the original, including its length prefix.

The selection is recomputed for each frame payload, so changing traffic can
move from compression to no compression and back without reconnecting. It is
a compressibility heuristic, not RTT, bandwidth, energy or CPU-feedback control.
The sample's compressed-size bands are: >=126 bytes = skip; 109–125 = L1;
77–108 = L2; 39–76 = L3; 13–38 = L4; <13 = L5, before applying the ceiling.

**Deployment scope:** this behaviour requires the Linux/Raspberry AUTO
adaptation source update and a rebuild/reload of the module. Older revisions
may define `auto_sample_level()` without calling it from the send path.
The update does not automatically change Zephyr's separate source copy.

### Compression levels

| Value | Name |
|---|---|
| 1 | VeryFast |
| 2 | Fast |
| 3 | Default |
| 4 | High |
| 5 | VeryHigh |

These names describe compression effort, not guaranteed throughput or
compression ratios. Measure CPU cost and byte counts with representative data.

Once an application sets `STCP_SO_COMPRESSION_LEVEL`, that socket stops
following the module default. Explicitly setting `3` also pins level 3;
it does not mean “follow the default.” This implementation has no separate
socket option to restore default-following mode.
`getsockopt()` reads the socket's cached level, so a changed module default
may only become visible there after the next send.

### UDP fault injection: writable does not always mean re-armable

The one-shot drop, duplicate, reorder and delay budgets are shared by the
whole module, not allocated per connection. A budget armed at load time is
consumed by the first eligible DATA send, which may belong to another socket.
These controls do not affect TCP-carrier traffic or handshake frames.

Writing `Y` to `drop_first_data` after loading does not re-arm an exhausted
budget. Toggling `N → Y` does not reset it either. The percentage-drop sequence
counter continues across runtime changes and resets at module initialization.
The drop condition is `((sequence * 37) % 100) < percent`, not a random generator.

Injection order is drop, delay, reorder, then duplicate. A dropped frame does
not consume budgets for the later stages. Boolean parameters control entry
into the shared test path, while individual events consume atomic budgets.
Consequently, disabling one boolean does not necessarily prevent its still
armed event if another injection keeps the shared path active. Disable all
injections to return to the normal send path. This does not reset a frame
already held for reordering.

Run one-shot tests through a controlled module reload with no active STCP
sockets, for example in an isolated test session:

```bash
sudo modprobe -r stcp
sudo modprobe stcp drop_first_data=1
# Run the UDP test, then restore normal module load settings.
```

A continuous loss policy can be changed at runtime:

```bash
printf '10\n' | sudo tee /sys/module/stcp/parameters/drop_percent
# Run the test, then disable this drop policy.
printf '0\n' | sudo tee /sys/module/stcp/parameters/drop_percent
```

To persist normal defaults, add `/etc/modprobe.d/stcp.conf`:

```text
options stcp default_compression_level=3 verbose_debug=0
```

This file applies to the next `modprobe` load; a direct `insmod` does not read it.

## 2. Socket interface

Linux/Raspberry Pi IPv4 sockets:

```c
socket(45 /* AF_STCP */, SOCK_STREAM, 253 /* STCP-TCP */);
socket(45 /* AF_STCP */, SOCK_STREAM, 254 /* STCP-UDP */);
```

STCP-UDP also uses `SOCK_STREAM` in this module's public socket API.
Addresses use IPv4 `sockaddr_in` with `sin_family=AF_INET`.
Socket options use **SOL_STCP = 45**. The option level and socket family
are distinct identifiers even though their numeric values match.
Strace may label family 45 as `AF_MCTP`; also check socket type and protocol.

### Options

| Option | Number | Access | Values / default | Purpose |
|---|---:|---|---|---|
| `STCP_SO_COMPRESSION` | 1 | set/get | int: `0` = OFF, `1` = AUTO; default `0` | Allows compression attempts on future outgoing frames. Other values are rejected. |
| `STCP_SO_COMPRESSION_THRESHOLD` | 2 | set/get | int: `0..INT_MAX` bytes; default `1024` | Minimum plaintext size of an internal frame payload for a compression attempt. `0` allows an attempt for every outgoing payload chunk. |
| `STCP_SO_COMPRESSION_STATS` | 3 | get only | 10 × u64, 80 bytes total | Snapshot of this socket's compression counters. No reset/set option. |
| `STCP_SO_COMPRESSION_LEVEL` | 4 | set/get | int: `1..5`; default is adaptive AUTO with the current module effort ceiling | Selects a fixed level for this socket and bypasses adaptive level selection. |

Integer options use a native C `int` in a buffer at least `sizeof(int)` bytes
long. They are neither strings nor network-byte-order integers. An unsupported
level/option identifier returns `ENOPROTOOPT`; an invalid value or undersized
buffer returns `EINVAL`; an invalid user-memory address can return `EFAULT`.

Settings are socket-specific. An accepted child inherits compression enablement,
threshold and explicit/default-following level policy from its listener at
accept time. A default-following child takes the module default current at
accept. Later listener changes do not update already accepted children;
configure those child sockets separately when needed.

Changing settings on a connected socket affects future compression choices,
not frames already queued. Separate option calls do not form an atomic
configuration update. Set them before starting the sending thread when an
intermediate configuration would be undesirable.

AUTO only attempts compression when the peer advertised support and the frame
payload reaches the threshold. The compressed representation is selected only
if it is smaller than the original, including its original-length prefix.
Otherwise the original content is sent. Compression and encryption are
separate: OFF does not disable STCP encryption. Disabling outgoing compression
does not prevent decompression of compressed frames received from the peer.

### C example

```c
#include <stdint.h>
#include <sys/socket.h>

/* fd is an STCP socket. Check the return value of every call. */
int threshold = 128, level = 3, enabled = 1;
if (setsockopt(fd, 45, 2, &threshold, sizeof threshold) < 0) { /* handle error */ }
if (setsockopt(fd, 45, 4, &level, sizeof level) < 0) { /* handle error */ }
if (setsockopt(fd, 45, 1, &enabled, sizeof enabled) < 0) { /* handle error */ }
/* The level call pins level 3. Omit it to follow the module default. */

int current_level;
socklen_t len = sizeof current_level;
if (getsockopt(fd, 45, 4, &current_level, &len) < 0) { /* handle error */ }
```

### Python example

```python
import socket
import struct

SOL_STCP = 45
with socket.socket(45, socket.SOCK_STREAM, 253) as sock:
    sock.setsockopt(SOL_STCP, 2, 128)  # threshold
    sock.setsockopt(SOL_STCP, 4, 3)    # explicit socket-specific level
    sock.setsockopt(SOL_STCP, 1, 1)    # AUTO
    print('Compression level:', sock.getsockopt(SOL_STCP, 4))

    fields = (
        'tx_attempts', 'tx_compressed_frames', 'tx_fallback_frames',
        'tx_input_bytes', 'tx_wire_bytes', 'tx_errors',
        'rx_compressed_frames', 'rx_wire_bytes', 'rx_output_bytes', 'rx_errors',
    )
    raw = sock.getsockopt(SOL_STCP, 3, 80)
    stats = dict(zip(fields, struct.unpack('=10Q', raw)))
    print(stats)
```

This illustrates options only. Add connect/bind/listen and actual traffic as
required. Counters on a new unused socket are zero. Python's custom-family
bind/accept needs the libc handling provided by FreeSDK; this example does
not perform address operations.

## 3. Compression counters and interpretation

The ABI field order is shown below. Each field is a native unsigned 64-bit
integer (`unsigned long long`), not a packed network-format value.

| Field | Meaning |
|---|---|
| `tx_attempts` | Full payload compression attempts after capability, mode, threshold and adaptive selection. Probe-only rejections are not included. |
| `tx_compressed_frames` | Attempts that selected a smaller compressed representation. |
| `tx_fallback_frames` | Attempts that used the original content. |
| `tx_input_bytes` | Original payload bytes for frames on which compression was attempted. |
| `tx_wire_bytes` | Selected representation bytes for those attempts: compressed content including its length prefix, or original fallback content. |
| `tx_errors` | Compression-function errors; sending can still continue using the original content. |
| `rx_compressed_frames` | Successfully decompressed compressed frames. |
| `rx_wire_bytes` | Compressed content bytes for those frames, including the length prefix. |
| `rx_output_bytes` | Original bytes recovered from those frames. |
| `rx_errors` | Errors handling compressed content. Not a general counter of every socket or network failure. |

`tx_wire_bytes` and `rx_wire_bytes` do not include all bytes on the network:
STCP headers, encryption tags and lower-layer protocol headers are outside
these counters. OFF-mode sends and payloads below the threshold do not increment
TX compression-attempt byte counters. RX byte counters cover compressed frames,
so the populations counted by TX and RX are not generally symmetric.

For TX data on which compression was attempted, when the denominator is > 0:

```text
content_saving_percent = 100 × (1 − tx_wire_bytes / tx_input_bytes)
```

This is not the whole connection's network-byte saving or a direct throughput
change. Later send failures do not necessarily roll back attempt counters
already updated.

## 4. Source basis and scope

Checked against the supplied Linux/Raspberry Pi source tree:

- `kernel/module/src/stcp_module.c`: parameters, validation and injection budgets.
- `kernel/module/src/stcp_carrier.c`: verbose diagnostics and UDP injection send path.
- `kernel/module/include/stcp.h`: option identifiers, defaults and counter layout.
- `kernel/module/src/stcp_ops.c`: set/get validation, level refresh and accept inheritance.
- `kernel/module/src/stcp_proto.c`: socket initialization.
- `kernel/module/rust/src/session.rs` and `compression.rs`: compression selection and counters.

The reported socket level is the configured AUTO effort ceiling or the explicit fixed level, not the last sampled selection. Sample-only rejections do not increment TX attempt/input/wire/fallback counters.

This reference is based on source inspection. Sysfs writes and socket calls
were not executed on hardware during documentation work. If the current
checkout differs from the supplied source, review these locations before
publication. Constants such as the 5000 ms handshake timeout are compile-time
settings in this source, not module parameters or SOL_STCP options listed here.
