# hostmngr

`hostmngr` is a daemon that detects and monitors host devices in a network.

A *host device* is any device with at least one network interface capable of
being configured with an IPv4 or IPv6 address via DHCP or static assignment.

Examples: desktop/laptop, router/AP, IP camera, smart TV, and similar
network-connected devices.


## Table of Contents

- [Build](#build)
- [Configuration](#configuration)
- [Usage](#usage)
- [UBUS Interface](#ubus-interface)
- [Dependencies](#dependencies)
- [Code Structure](#code-structure)


## Build

Run `make` from within the `src` directory to build the `hostmngr` binary.
See the [Dependencies](#dependencies) section for prerequisites.

```sh
cd src
make
```

To install the binary to `/usr/sbin/hostmngr`:

```sh
make install
```

### Build Options

The following flags can be passed to `make` to enable optional features:

| Flag | Description |
|------|-------------|
| `CONFIG_HOSTMNGR_HOSTS_HISTORY=y` | Enable host history tracking and persistence. |
| `HOSTMNGR_PLATFORM_HAS_WIFI=y` | Enable WiFi support via libwifi APIs (links `-lwifi-7`). |


## Configuration

The default configuration file is `/etc/config/hosts`, in
[UCI](https://openwrt.org/docs/guide-user/base-system/uci) format.
A different path can be specified at runtime with the `-c` flag.

**Sample `/etc/config/hosts`:**

```
config global 'global'
	option enabled '1'
	option history '1'
	option history_reboot_persistent '1'
	option history_file '/etc/hosts_history.json'
	option history_timeout '604800'

config interface
	list ifname 'br-lan'
```

### Configuration Reference

**`global` section**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | boolean | `1` | When set to `0`, disables host detection. The daemon keeps running and can be re-enabled by setting this back to `1`. |
| `history` | boolean | `1` | When set to `0`, disables host history. |
| `history_reboot_persistent` | boolean | `1` | When set to `0`, history entries are not persisted across reboots. The history file is stored in `/tmp` instead. |
| `history_file` | string | `/etc/hosts_history.json` | Path to the JSON file used for storing history records. |
| `history_timeout` | integer | `604800` | Duration in seconds that a non-live history record is retained before being aged out. Default is 7 days. |
| `history_max_entries` | integer | — | Maximum number of entries retained in the history table. |
| `history_max_size` | integer | — | Maximum size in bytes of the history file. |

**`interface` section**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `ifname` | string (list) | `br-lan` | Interface name(s) to monitor. Typically the LAN-side bridge interface. Multiple `list ifname` entries are supported. |


## Usage

```
hostmngr [OPTIONS]

Options:
  -h, --help                 Show this help message and exit
  -v, --version              Print version information and exit
  -D                         Run as a background daemon
  -s <socket>                UBUS socket path
  -c, --config <file>        Path to configuration file (default: /etc/config/hosts)
 --ifname <interface>        Monitor the specified interface
 --exclude-ifname <if>       Exclude the specified interface from monitoring
  -d                         Increase debug verbosity (repeat for more output, e.g. -ddd)
  -p <pidfile>               PID file path (default: /var/run/hostmngr.pid)
  -o <file>                  Write log output to the specified file
  -f                         Treat the log output file as a FIFO
  -l                         Enable syslog logging
  -O <object>                UBUS object name (default: hosts)
```

**Example — run in the foreground with debug output:**

```sh
hostmngr -ddd -c /etc/config/hosts
```

**Example — run as a daemon:**

```sh
hostmngr -D -p /var/run/hostmngr.pid
```


## UBUS Interface

`hostmngr` registers a UBUS object (default name: `hosts`) that exposes the
following methods:

```sh
root@device:~# ubus -v list hosts
'hosts' @1000ffff
	"show":{}
	"history":{}
	"debug":{}
```

### `ubus call hosts show`

Returns all currently detected host devices.

```sh
root@device:~# ubus call hosts show
{
	"hosts": [
		{
			"macaddr": "00:02:02:aa:bb:cc",
			"hostname": "acx-laptop",
			"active": true,
			"reconnect_count": 1,
			"local": true,
			"active_last_change": "2023-05-30T21:31:57Z",
			"ipaddr": "192.168.1.160",
			"address_source": "DHCP",
			"lease_time_remaining": 3369,
			"interface_type": "Ethernet",
			"is1905": false,
			"ndm_state": "reachable",
			"num_tcp": 0,
			"num_udp": 10,
			"active_connections": 10,
			"device": "eth3",
			"network": "lan",
			"ipv4addr": [
				{ "ip": "192.168.1.160" }
			],
			"ipv6addr": [],
			"stats": {
				"tx_packets": 70,
				"tx_bytes": 7323,
				"rx_packets": 61,
				"rx_bytes": 5860
			},
			"total_stats": {
				"tx_packets": 1420,
				"tx_bytes": 198341,
				"rx_packets": 1301,
				"rx_bytes": 174022
			}
		}
	]
}
```

**Field reference:**

| Field | Description |
|-------|-------------|
| `macaddr` | MAC address of the host device. |
| `hostname` | Hostname, resolved from DHCP leases or static configuration. |
| `active` | `true` if the device is currently reachable on the network. |
| `reconnect_count` | Number of times this device has reconnected since it was first seen. |
| `local` | `true` if the device is directly connected to a monitored interface; `false` if seen via a remote 1905 node. |
| `active_last_change` | ISO 8601 timestamp of the last active/inactive state change. |
| `ipaddr` | Primary IP address. |
| `address_source` | `"DHCP"` or `"Static"`. |
| `lease_time_remaining` | Remaining DHCP lease time in seconds. `0` for static addresses. |
| `interface_type` | One of: `"Ethernet"`, `"Wi-Fi"`, `"G.hn"`, `"HPNA"`, `"HomePlug"`, `"MoCA"`, `"UPA"`, `"Other"`. |
| `is1905` | `true` if the device is an IEEE 1905 node. |
| `ieee1905id` | AL-MAC address of the IEEE 1905 device (present when `is1905` is `true`). |
| `is_mld` | `true` if the device is a Multi-Link Device (MLD). Present only for MLD-capable Wi-Fi devices. |
| `mld_macaddr` | MAC address of the MLD peer (present when `is_mld` is `true`). |
| `link_macaddr` | Array of upstream link MAC addresses for 1905/MLD devices (present when `is1905` or `is_mld` is `true`). |
| `parent_device` | Parent interface name for 4-address mode Wi-Fi clients. Present only for Wi-Fi devices. |
| `ndm_state` | Neighbor discovery state: `"reachable"`, `"stale"`, `"probing"`, etc. |
| `num_tcp` | Number of active TCP connections. |
| `num_udp` | Number of active UDP connections. |
| `active_connections` | Total number of active connections (TCP + UDP). |
| `device` | Kernel interface name the device was last seen on (e.g., `eth3`). |
| `network` | Logical network name (e.g., `lan`). |
| `ipv4addr` | Array of IPv4 address objects (`{ "ip": "..." }`) associated with this device. |
| `ipv6addr` | Array of IPv6 address objects (`{ "ip": "..." }`) associated with this device. |
| `stats` | Traffic statistics for the current session only: `tx_packets`, `tx_bytes`, `rx_packets`, `rx_bytes`. |
| `total_stats` | Cumulative traffic statistics across all sessions (current session plus all past sessions recorded in history): `tx_packets`, `tx_bytes`, `rx_packets`, `rx_bytes`. |

### `ubus call hosts history`

Returns all host devices seen since history tracking began, including
devices that are no longer active.

```sh
root@device:~# ubus call hosts history
{
	"history": [
		{
			"idx": 4,
			"macaddr": "00:02:02:aa:bb:cc",
			"hostname": "acx-laptop",
			"first_seen": "2023-05-30T21:30:38",
			"last_seen": "2023-05-30T21:31:57",
			"alive": true,
			"iswifi": false,
			"stats": {
				"tx_packets": 0,
				"tx_bytes": 0,
				"rx_packets": 0,
				"rx_bytes": 0
			}
		}
	]
}
```

`alive` is `true` while the device is currently active on the network and
becomes `false` when the device is no longer detected. History records are
aged out after `history_timeout` seconds (default: 7 days).


## Dependencies

| Library | Repository | License |
|---------|-----------|---------|
| libeasy | https://dev.iopsys.eu/hal/libeasy.git | LGPL 2.1 |
| libwifi-7 | https://dev.iopsys.eu/hal/libwifi.git | LGPL 2.1 |
| libwifiutils | https://dev.iopsys.eu/hal/libwifi.git | LGPL 2.1 |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |
| libubox | https://git.openwrt.org/project/libubox.git | ISC |
| libblobmsg_json | https://git.openwrt.org/project/libubox.git | ISC |
| libubus | https://git.openwrt.org/project/ubus.git | LGPL 2.1 |
| libjson-c | https://github.com/json-c/json-c | MIT |
| libnl-3 | https://github.com/thom311/libnl.git | LGPL 2.1 |
| libnl-genl-3 | https://github.com/thom311/libnl.git | LGPL 2.1 |
| libnl-route-3 | https://github.com/thom311/libnl.git | LGPL 2.1 |
| libnetfilter_conntrack | https://git.netfilter.org/libnetfilter_conntrack | GPL 2.0+ |
| libnfnetlink | https://git.netfilter.org/libnfnetlink | GPL 2.0+ |
| libmnl | https://git.netfilter.org/libmnl | LGPL 2.1 |

> `libwifi-7` and `libwifiutils` are required only when building with
> `HOSTMNGR_PLATFORM_HAS_WIFI=y`.


## Code Structure

All source files are located in the `src/` directory.

| File(s) | Description |
|---------|-------------|
| `main.c` | Entry point. Parses command-line arguments and starts the daemon. |
| `hostmngr.c` / `hostmngr.h` | Core daemon logic: interface management, host discovery (ARP, DHCP, WiFi, netlink), and event handling. |
| `neigh.c` / `neigh.h` | Neighbor (host) table management: hash-based storage, aging, IP tracking, and flow statistics per device. |
| `netlink.c` | Handles rtnetlink and generic netlink events from the kernel (neighbor cache changes, interface state, bridge FDB). |
| `stats.c` | Flow and IP table management; TCP/UDP connection tracking via libnetfilter_conntrack. |
| `history.c` | Loads and stores the JSON host history file; handles ageout and reboot persistence. |
| `ubus.c` | Implements the UBUS object and methods (`show`, `history`, `debug`). |
| `config.c` | Parses the UCI configuration file and applies defaults. |
| `arping.c` / `arping.h` | Sends unicast ARP requests to probe device reachability and detect static IP configurations. |
| `node.c` | Allocation and lookup of IEEE 1905 multi-interface device nodes. |
| `wifi_api.c` / `wifi_api.h` | WiFi interface and station enumeration via libwifi APIs (compiled in only when `HAS_WIFI=y`). |
| `debug.c` / `debug.h` | Logging subsystem: configurable verbosity levels, optional color output, syslog, and FIFO support. |
| `util.c` / `util.h` | Miscellaneous utilities: daemonization, interface helpers, media-type detection. |
| `timer.c` / `timer.h` / `timer_impl.h` | Abstract timer interface backed by uloop. |
| `lookup3_hash.c` / `lookup3_hash.h` | Bob Jenkins lookup3 hash, used for neighbor and flow table indexing. |
| `version.h` | Compile-time version string. |
| `useropts.h` | Build-time user option overrides. |
