Code Flow and System Interaction

Code Flow and System Interaction

From CLI/API to SoC Layer: End-to-End Data Flow

Flow:

  • CLI/API: User issues uci set or LuCI command.
  • UCI: Updates /etc/config/ files.
  • UBUS: Notifies netifd or other services.
  • Kernel: Applies changes (e.g., interface configuration) via drivers.
  • SoC: Hardware (e.g., Ethernet, Wi-Fi) executes changes.

Network Interface Bring-Up Flow (ifup, hotplug, netifd)

Flow:

  • Hotplug: Detects interface events (e.g., cable plugged in).
  • netifd: Reads UCI config and applies settings via ifup.
  • Kernel: Configures network stack (e.g., IP addresses, routes).
  • UBUS: Notifies subscribers of interface status.

Firewall and Packet Filtering (nftables integration)

Integration:

  • OpenWRT uses nftables (since 22.03) for firewall rules.
  • Configured via UCI (/etc/config/firewall).
  • fw4 (firewall4) manages rules, replacing fw3/iptables.
  • Supports NAT, filtering, and QoS.

Boot Sequence and Init Scripts (Procd rc.d)

Sequence:

  • U-Boot: Loads kernel and device tree.
  • Kernel: Mounts filesystems, initializes drivers.
  • procd: Runs /etc/init.d/ scripts in order (e.g., boot, network).
  • Services: Starts netifd, dnsmasq, etc., based on UCI settings.

Code Walkthrough: A Sample UBUS Application

Example: A C program registering a UBUS object to expose system status.


Sample UBUS Application Code

#include <libubus.h>
#include <libubox/uloop.h>
#include <libubox/blobmsg_json.h>
#include <uci.h>
#include <stdio.h>
#include <unistd.h>

// UBUS method to get system status
static int system_status(struct ubus_context *ctx, struct ubus_object *obj,
                        struct ubus_request_data *req, const char *method,
                        struct blob_attr *msg) {
    struct blob_buf b = {0};
    blob_buf_init(&b, 0);

    // Add system info to response
    blobmsg_add_string(&b, "hostname", "openwrt-router");
    blobmsg_add_u32(&b, "uptime", (uint32_t)time(NULL));

    // Send response
    ubus_send_reply(ctx, req, b.head);
    blob_buf_free(&b);
    return 0;
}

// UBUS method to read UCI config
static int get_config(struct ubus_context *ctx, struct ubus_object *obj,
                      struct ubus_request_data *req, const char *method,
                      struct blob_attr *msg) {
    struct uci_context *uci_ctx = uci_alloc_context();
    struct uci_package *pkg = NULL;
    struct blob_buf b = {0};
    blob_buf_init(&b, 0);

    // Load UCI network config
    uci_load(uci_ctx, "network", &pkg);
    if (pkg) {
        struct uci_section *s = uci_lookup_section(uci_ctx, pkg, "lan");
        if (s) {
            const char *ipaddr = uci_lookup_option_string(uci_ctx, s, "ipaddr");
            if (ipaddr)
                blobmsg_add_string(&b, "lan_ip", ipaddr);
        }
        uci_unload(uci_ctx, pkg);
    }

    uci_free_context(uci_ctx);
    ubus_send_reply(ctx, req, b.head);
    blob_buf_free(&b);
    return 0;
}

// UBUS method table
static const struct ubus_method methods[] = {
    UBUS_METHOD_NOARG("status", system_status),
    UBUS_METHOD_NOARG("config", get_config),
};

// UBUS object type
static struct ubus_object_type obj_type =
    UBUS_OBJECT_TYPE("system_info", methods);

// UBUS object
static struct ubus_object obj = {
    .name = "system",
    .type = &obj_type,
    .methods = methods,
    .n_methods = ARRAY_SIZE(methods),
};

int main(void) {
    struct ubus_context *ctx;

    // Initialize uloop
    uloop_init();

    // Connect to ubusd
    ctx = ubus_connect(NULL);
    if (!ctx) {
        fprintf(stderr, "Failed to connect to ubusd\n");
        return -1;
    }

    // Add ubus to uloop
    ubus_add_uloop(ctx);

    // Register UBUS object
    if (ubus_add_object(ctx, &obj)) {
        fprintf(stderr, "Failed to add object\n");
        ubus_free(ctx);
        return -1;
    }

    // Run event loop
    uloop_run();

    // Cleanup
    ubus_free(ctx);
    uloop_done();
    return 0;
}

Code Walkthrough

  1. Includes and Setup:

    • Includes libubus.h for UBUS communication, libubox/uloop.h for event loop, libubox/blobmsg_json.h for JSON-like message formatting, and uci.h for configuration18 configuration access.
    • The program uses uloop for event handling and blob_buf for message construction.
  2. UBUS Methods:

    • system_status: Responds with system information (hostname and uptime) using blob_buf to format the response.
    • get_config: Reads the LAN IP address from the UCI network configuration file and returns it in the response.
    • Both methods use ubus_send_reply to send data back to the caller.
  3. UBUS Object Definition:

    • Defines a method table (methods) with two methods: status and config, both without arguments.
    • Creates an object type (obj_type) and object (obj) named “system” to register with ubusd.
  4. Main Function:

    • Initializes uloop for event handling.
    • Connects to ubusd using ubus_connect(NULL) (default UNIX socket).
    • Integrates UBUS with uloop via ubus_add_uloop.
    • Registers the system object with ubus_add_object.
    • Runs the event loop with uloop_run to handle incoming UBUS calls.
    • Cleans up with ubus_free and uloop_done.
  5. UCI Integration:

    • In get_config, a UCI context is created (uci_alloc_context), the network config is loaded, and the lan section’s ipaddr is retrieved and added to the response.

How to Test

  • Compile with gcc -o system_info system_info.c -lubus -lubox -luci.
  • Run on an OpenWRT system: ./system_info.
  • Use the ubus CLI to test:
    ubus call system status
    ubus call system config
    Expected output:
    // For status
    {
        "hostname": "openwrt-router",
        "uptime": 123456789
    }
    // For config
    {
        "lan_ip": "192.168.1.1"
    }

Notes

  • The program registers a UBUS object named system with two methods: status (returns hostname and uptime) and config (returns LAN IP from UCI).
  • It uses libubus for communication with ubusd and libuci for configuration access.
  • The uloop event loop ensures the program handles incoming UBUS calls asynchronously.
  • Ensure libubus, libubox, and libuci are installed on the OpenWrt system.

This is a basic example demonstrating UBUS object registration and UCI integration. For production, error handling and support for method arguments are to be added.


Service Startup, Watchdog, and Daemonization

Startup:

  • procd manages service startup via /etc/init.d/ scripts.
  • Scripts define start, stop, and restart functions.

Watchdog:

  • procd monitors services and restarts them if they crash.
  • Hardware watchdog supported on compatible SoCs.

Daemonization:

  • Services run as daemons, managed by procd.