ruvnet/RuView · error

EIO

EIO

Error message

veil: genl_connect failed\n

What it means

`genl_connect()` connects the allocated socket to the kernel's NETLINK_GENERIC subsystem; any non-zero return is flattened by this code to `-EIO`. In practice the underlying causes are environmental: AF_NETLINK blocked by a container/seccomp policy, insufficient privileges in a user namespace, or fd exhaustion (EMFILE) at startup — the daemon deliberately does not distinguish them.

Source

Thrown at firmware/privshield/openwrt/veil_shieldd.c:75

    size_t          passes; /* Givens passes                                */
    volatile sig_atomic_t running;
};

static struct veil_ctx g_ctx;

static void on_signal(int sig) { (void)sig; g_ctx.running = 0; }

/* ---------------------------------------------------------------------- */
/* nl80211 bring-up — all REAL libnl-genl-3 API names.                     */
/* ---------------------------------------------------------------------- */
static int veil_nl_connect(struct veil_ctx *c) {
    c->sock = nl_socket_alloc();
    if (!c->sock) {
        fprintf(stderr, "veil: nl_socket_alloc failed\n");
        return -ENOMEM;
    }
    if (genl_connect(c->sock)) {
        fprintf(stderr, "veil: genl_connect failed\n");
        return -EIO;
    }
    c->family = genl_ctrl_resolve(c->sock, "nl80211");
    if (c->family < 0) {
        fprintf(stderr, "veil: genl_ctrl_resolve(nl80211) failed: %d\n",
                c->family);
        return c->family;
    }
    /* Observe MLME events (auth/assoc, and — where the driver forwards them —
     * action-frame notifications). Real multicast group name is "mlme". */
    int grp = genl_ctrl_resolve_grp(c->sock, "nl80211", "mlme");
    if (grp >= 0) {
        (void)nl_socket_add_membership(c->sock, grp);
    }
    return 0;
}

/* ---------------------------------------------------------------------- */

View on GitHub (pinned to 4685618388)

Solutions

  1. Run on the target (or container) with host netns and privilege: `docker run --net=host --cap-add NET_ADMIN ...`.
  2. Raise/inspect fd limits: `ulimit -n`, `ls /proc/$(pidof veil_shieldd)/fd | wc -l`.
  3. Confirm generic netlink works for other tools on the same host (`genl-ctrl-list` or `iw list`); if they also fail, fix the kernel/policy, not the daemon.

Example fix

# before
docker run openwrt/veil_shieldd -i 2        # veil: genl_connect failed
# after
docker run --net=host --cap-add NET_ADMIN openwrt/veil_shieldd -i 2
Defensive patterns

Strategy: validation

Validate before calling

/* connectivity pre-check before starting the daemon loop */
static int netlink_available(void) {
    struct nl_sock *t = nl_socket_alloc();
    if (!t) return -1;
    int rc = genl_connect(t);
    nl_socket_free(t);
    return rc == 0 ? 0 : -1;
}

Try / catch

int rc = veil_nl_connect(&g_ctx);
if (rc == -EIO) {
    /* environment blocks AF_NETLINK: container policy, caps, or fd limits */
    fprintf(stderr, "veil: netlink blocked — run with host netns + CAP_NET_ADMIN\n");
    exit(rc);
}

Prevention

When it happens

Trigger: Running inside Docker/LXC with a default seccomp profile blocking netlink; unprivileged user namespace without CAP_NET_ADMIN; fd limit exhausted by a leaked loop before the daemon starts; kernel built without generic netlink.

Common situations: Smoke-testing the daemon in a container instead of on the router; systemd unit with restrictive `RestrictAddressFamilies`; long-running hosts with fd leaks from other processes under the same limits.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/f8ae06661360d8c0. Report an issue: GitHub.