doc/README.lua_host_api.md
host.* bindings)This document describes all C→Lua bindings exposed as host.* functions via
src/LuaEngineHost.cpp. It is intended both for human developers writing Lua
scripts and as a machine-readable reference for AI-assisted code generation (e.g.
Claude Code in this repository).
host.* workshost.* workshost.* functions are available only inside host check scripts — Lua scripts
invoked by the ntopng host-processing engine for each tracked host. They operate
on the current host implicitly stored in the Lua VM context
(NtopngLuaContext::host); no arguments are needed to identify the host.
Every function in _ntop_host_reg[] (bottom of src/LuaEngineHost.cpp) follows
this pattern:
static int ntop_host_get_xxx(lua_State* vm) {
NtopngLuaContext* c = getLuaVMContext(vm);
Host* h = c ? c->host : NULL;
if (h)
lua_push<type>(vm, h->get_xxx());
else
lua_pushnil(vm);
return ntop_lua_return_value(vm, __FUNCTION__, CONST_LUA_OK);
}
// Registered as:
{ "xxx", ntop_host_get_xxx }, // called as host.xxx()
Host check scripts are stored in:
scripts/lua/modules/host_checks/
Each script is a Lua module that exports a check() function called per-host by
the engine.
-- scripts/lua/modules/host_checks/my_host_check.lua
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
local host_consts = require("host_consts")
local my_check = {}
-- Called once per tracked host by the engine
function my_check.check(host_info)
-- Guard: skip non-local or non-unicast hosts
if not host.is_local() then return end
if not host.is_unicast() then return end
-- Skip on first run (no baseline yet)
if host.isFirstCheckRun() then return end
local bytes = host.bytes()
local score = host.score()
if bytes > 1e9 then
host.triggerAlert(100, "Host transferred > 1 GB: " .. tostring(bytes))
end
end
return my_check
host.* functions return nil if called outside a host check context (i.e.
when NtopngLuaContext::host is NULL).host.skipVisitedHost and
host.triggerAlert).host.* is completely separate from interface.* — there is no need to call
interface.select() inside host check scripts.flow.*, host check scripts run periodically (not per-packet), so
counters reflect accumulated totals since the host was first seen.| Lua call | Returns | Description |
|---|---|---|
host.ip() | string | The IP address (or IP/mask for network hosts) of the current host, e.g. "192.168.1.10" or "10.0.0.0/24". |
host.mac() | string | The MAC address string associated with the current host (e.g. "aa:bb:cc:dd:ee:ff"). Returns a zero MAC ("00:00:00:00:00:00") if unknown. |
host.name() | string | The visual display name for the current host — the resolved hostname if available, otherwise a custom label, otherwise the IP string. |
host.vlan_id() | integer | The VLAN ID associated with the current host. Returns 0 if the host is not on a tagged VLAN. |
These predicates classify the host's IP address type. All return false when
called outside a host check context.
| Lua call | Returns | Description |
|---|---|---|
host.is_local() | boolean | true if the host's IP falls within a locally configured network (i.e. it is an "inside" host). |
host.is_unicast() | boolean | true if the host's IP is a unicast address (not broadcast or multicast). Returns true when the host has no IP. |
host.is_multicast() | boolean | true if the host's IP is a multicast address (224.0.0.0/4 for IPv4, ff00::/8 for IPv6). |
host.is_broadcast() | boolean | true if the host's IP is a broadcast address (e.g. 255.255.255.255 or a directed broadcast). |
host.is_blacklisted() | boolean | true if the host's IP appears on any configured threat intelligence blacklist. |
host.is_rx_only() | boolean | true if the host has only ever been seen receiving traffic (no outbound packets observed). |
-- Most checks only make sense for local unicast hosts
if not host.is_local() then return end
if not host.is_unicast() then return end
if host.is_blacklisted() then return end -- already flagged elsewhere
All byte counters accumulate from the moment the host was first seen (or since the
last counter reset via interface.resetHostStats()).
| Lua call | Returns | Description |
|---|---|---|
host.bytes() | integer | Total bytes transferred in both directions (sent + received). |
host.bytes_sent() | integer | Total bytes sent (uploaded) by this host. |
host.bytes_rcvd() | integer | Total bytes received (downloaded) by this host. |
| Lua call | Returns | Description |
|---|---|---|
host.l7() | table | Returns a table of per-nDPI-protocol byte and flow statistics for this host. Each key is a protocol name; each value is a sub-table with bytes_sent, bytes_rcvd, flows fields. Returns nil if nDPI stats are not available for this host. |
local l7 = host.l7()
if l7 and l7["BitTorrent"] then
local bt = l7["BitTorrent"]
if (bt.bytes_sent + bt.bytes_rcvd) > 100e6 then
host.triggerAlert(60, "Heavy BitTorrent usage detected")
end
end
These counters track TCP/UDP connections that were one-sided (the host sent traffic but received no reply, or vice versa). They are useful for detecting port scans, SYN floods, and other anomalous contact patterns.
| Lua call | Returns | Description |
|---|---|---|
host.getNumContactedPeersAsClientTCPUDPNoTX() | integer | Number of distinct remote peers this host contacted as a TCP/UDP client but never received any reply from. High values may indicate a port/host scan. |
host.getNumContactsFromPeersAsServerTCPUDPNoTX() | integer | Number of distinct remote clients that attempted to connect to this host as a TCP/UDP server but were never answered. High values may indicate this host is a scan target. |
host.getNumContactedTCPUDPServerPortsNoTX() | integer | Number of distinct server ports this host contacted (as client) with no reply received. Useful for detecting horizontal port scans. |
host.getUnidirectionalTCPUDPFlowsStats() | table | Returns a table breaking down unidirectional (no-reply) TCP/UDP flow counts by client and server roles. |
host.resetHostContacts() | nil | Resets all peer-contact counters for this host (contacted peers, server ports, unidirectional flows). Used after processing to avoid double-counting across check intervals. |
local contacted_ports = host.getNumContactedTCPUDPServerPortsNoTX()
local contacted_peers = host.getNumContactedPeersAsClientTCPUDPNoTX()
if contacted_ports > 50 or contacted_peers > 100 then
host.triggerAlert(80, string.format(
"Possible scan: %d ports / %d peers with no reply",
contacted_ports, contacted_peers))
host.resetHostContacts() -- reset to avoid re-alerting next cycle
end
| Lua call | Returns | Description |
|---|---|---|
host.score() | integer | The current alert score for this host — the sum of all active alert severity scores. Higher values indicate more/worse active alerts. |
host.triggerAlert(value, msg) | nil | Triggers a custom alert on the current host. value is a numeric severity/score contribution; msg is a description string that appears in the alert details. No-op if called outside a host check context. |
| Score value | Suggested severity |
|---|---|
| 1–25 | Informational |
| 26–50 | Warning |
| 51–75 | Error |
| 76–100 | Critical |
| Lua call | Returns | Description |
|---|---|---|
host.isFirstCheckRun() | boolean | Returns true if this is the very first invocation of the host check script for this host. Use this to skip checks that require a baseline (e.g. delta comparisons). |
host.skipVisitedHost([skip[, skip_until]]) | nil | Controls whether the host check script re-evaluates this host. Call with skip=true to suppress future evaluations; optionally pass skip_until as a Unix timestamp after which evaluation resumes. Call with skip=false (or no arguments) to re-enable evaluation. |
skipVisitedHost usage pattern-- After triggering an alert, suppress re-alerting for 1 hour
if should_alert then
host.triggerAlert(70, "Anomaly detected")
local one_hour_from_now = os.time() + 3600
host.skipVisitedHost(true, one_hour_from_now)
end
All 23 host.* functions:
| Lua function | C implementation | Category |
|---|---|---|
host.bytes() | ntop_host_get_bytes_total | Traffic |
host.bytes_rcvd() | ntop_host_get_bytes_rcvd | Traffic |
host.bytes_sent() | ntop_host_get_bytes_sent | Traffic |
host.getNumContactedPeersAsClientTCPUDPNoTX() | ntop_get_num_contacted_peers_as_client_tcp_udp_notx | Peer contacts |
host.getNumContactedTCPUDPServerPortsNoTX() | ntop_get_num_contacted_tcp_udp_server_ports_notx | Peer contacts |
host.getNumContactsFromPeersAsServerTCPUDPNoTX() | ntop_get_num_contacts_from_peers_as_server_tcp_udp_notx | Peer contacts |
host.getUnidirectionalTCPUDPFlowsStats() | ntop_get_unidirectional_tcp_udp_flows_stats | Peer contacts |
host.ip() | ntop_host_get_ip | Identity |
host.isFirstCheckRun() | ntop_is_first_check_run | Script control |
host.is_blacklisted() | ntop_host_is_blacklisted | Address flags |
host.is_broadcast() | ntop_host_is_broadcast | Address flags |
host.is_local() | ntop_host_is_local | Address flags |
host.is_multicast() | ntop_host_is_multicast | Address flags |
host.is_rx_only() | ntop_host_is_rx_only | Address flags |
host.is_unicast() | ntop_host_is_unicast | Address flags |
host.l7() | ntop_host_get_l7_stats | Protocol stats |
host.mac() | ntop_host_get_mac | Identity |
host.name() | ntop_host_get_name | Identity |
host.resetHostContacts() | ntop_reset_host_contacts | Peer contacts |
host.score() | ntop_host_get_score | Score & alerts |
host.skipVisitedHost([skip[, skip_until]]) | ntop_skip_visited_host | Script control |
host.triggerAlert(value, msg) | ntop_trigger_host_alert | Score & alerts |
host.vlan_id() | ntop_host_get_vlan_id | Identity |