// Package agent implements `tomswall agent`: it pulls a device's compiled config // from tomswallapi, differentially applies it, and reports the applied generation. // It never fails closed — if the control plane is unreachable it keeps the last // known-good config running. package agent // RenderedConfig is the per-device document served by tomswallapi at // GET /api/v1/devices/{name}/config. It mirrors the control plane's compiler // output: interface-agnostic, address-matched rules plus named sets. type RenderedConfig struct { Generation int64 `yaml:"generation" json:"generation"` Device string `yaml:"device" json:"device"` Class string `yaml:"class" json:"class"` Enforcing bool `yaml:"enforcing" json:"enforcing"` Settings RenderedSettings `yaml:"settings" json:"settings"` Resolver []string `yaml:"resolver,omitempty" json:"resolver,omitempty"` Bindings map[string][]string `yaml:"bindings,omitempty" json:"bindings,omitempty"` // zone -> interfaces Sets []RenderedSet `yaml:"sets,omitempty" json:"sets,omitempty"` Rules []RenderedRule `yaml:"rules,omitempty" json:"rules,omitempty"` Policies []RenderedPolicy `yaml:"policies,omitempty" json:"policies,omitempty"` SNAT []RenderedSNAT `yaml:"snat,omitempty" json:"snat,omitempty"` Netmap []RenderedNetmap `yaml:"netmap,omitempty" json:"netmap,omitempty"` NAT []RenderedNAT `yaml:"nat,omitempty" json:"nat,omitempty"` Hosts []RenderedHost `yaml:"hosts,omitempty" json:"hosts,omitempty"` Providers []RenderedProvider `yaml:"providers,omitempty" json:"providers,omitempty"` Routes []RenderedRoute `yaml:"routes,omitempty" json:"routes,omitempty"` RoutingRules []RenderedRoutingRule `yaml:"routing_rules,omitempty" json:"routing_rules,omitempty"` Tunnels []RenderedTunnel `yaml:"tunnels,omitempty" json:"tunnels,omitempty"` StoppedRules []RenderedStoppedRule `yaml:"stopped_rules,omitempty" json:"stopped_rules,omitempty"` ProxyARP []RenderedProxy `yaml:"proxy_arp,omitempty" json:"proxy_arp,omitempty"` ProxyNDP []RenderedProxy `yaml:"proxy_ndp,omitempty" json:"proxy_ndp,omitempty"` ArpRules []RenderedArpRule `yaml:"arp_rules,omitempty" json:"arp_rules,omitempty"` Maclist []RenderedMaclist `yaml:"maclist,omitempty" json:"maclist,omitempty"` Mangle []RenderedMangle `yaml:"mangle,omitempty" json:"mangle,omitempty"` Accounting []RenderedAccounting `yaml:"accounting,omitempty" json:"accounting,omitempty"` TCDevices []RenderedTCDevice `yaml:"tc_devices,omitempty" json:"tc_devices,omitempty"` TCClasses []RenderedTCClass `yaml:"tc_classes,omitempty" json:"tc_classes,omitempty"` TCFilters []RenderedTCFilter `yaml:"tc_filters,omitempty" json:"tc_filters,omitempty"` TCInterfaces []RenderedTCInterface `yaml:"tc_interfaces,omitempty" json:"tc_interfaces,omitempty"` TCPriorities []RenderedTCPriority `yaml:"tc_priorities,omitempty" json:"tc_priorities,omitempty"` Blrules []RenderedBlrule `yaml:"blrules,omitempty" json:"blrules,omitempty"` Conntrack []RenderedConntrack `yaml:"conntrack,omitempty" json:"conntrack,omitempty"` Secmarks []RenderedSecmark `yaml:"secmarks,omitempty" json:"secmarks,omitempty"` Vars map[string]string `yaml:"vars,omitempty" json:"vars,omitempty"` } type RenderedBlrule struct { Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` Action string `yaml:"action" json:"action"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Log string `yaml:"log,omitempty" json:"log,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedConntrack struct { Action string `yaml:"action" json:"action"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Chain string `yaml:"chain,omitempty" json:"chain,omitempty"` Helper string `yaml:"helper,omitempty" json:"helper,omitempty"` User string `yaml:"user,omitempty" json:"user,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedSecmark struct { Secmark string `yaml:"secmark" json:"secmark"` Chain string `yaml:"chain" json:"chain"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedMangle struct { Action string `yaml:"action" json:"action"` Chain string `yaml:"chain,omitempty" json:"chain,omitempty"` MarkValue string `yaml:"mark_value,omitempty" json:"mark_value,omitempty"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` User string `yaml:"user,omitempty" json:"user,omitempty"` Mark string `yaml:"mark,omitempty" json:"mark,omitempty"` Length string `yaml:"length,omitempty" json:"length,omitempty"` TOS string `yaml:"tos,omitempty" json:"tos,omitempty"` Helper string `yaml:"helper,omitempty" json:"helper,omitempty"` Probability *float64 `yaml:"probability,omitempty" json:"probability,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedAccounting struct { Action string `yaml:"action" json:"action"` Section string `yaml:"section,omitempty" json:"section,omitempty"` Chain string `yaml:"chain,omitempty" json:"chain,omitempty"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Mark string `yaml:"mark,omitempty" json:"mark,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTCDevice struct { Interface string `yaml:"interface" json:"interface"` InBandwidth string `yaml:"in_bandwidth,omitempty" json:"in_bandwidth,omitempty"` OutBandwidth string `yaml:"out_bandwidth,omitempty" json:"out_bandwidth,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTCClass struct { Interface string `yaml:"interface" json:"interface"` Mark int `yaml:"mark,omitempty" json:"mark,omitempty"` Rate string `yaml:"rate,omitempty" json:"rate,omitempty"` Ceil string `yaml:"ceil,omitempty" json:"ceil,omitempty"` Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTCFilter struct { Class string `yaml:"class" json:"class"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` TOS string `yaml:"tos,omitempty" json:"tos,omitempty"` Length int `yaml:"length,omitempty" json:"length,omitempty"` Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTCInterface struct { Interface string `yaml:"interface" json:"interface"` Type string `yaml:"type,omitempty" json:"type,omitempty"` InBandwidth string `yaml:"in_bandwidth,omitempty" json:"in_bandwidth,omitempty"` OutBandwidth string `yaml:"out_bandwidth,omitempty" json:"out_bandwidth,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTCPriority struct { Band int `yaml:"band" json:"band"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Address string `yaml:"address,omitempty" json:"address,omitempty"` Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` Helper string `yaml:"helper,omitempty" json:"helper,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedTunnel struct { Type string `yaml:"type" json:"type"` Zone string `yaml:"zone" json:"zone"` Gateways []string `yaml:"gateways,omitempty" json:"gateways,omitempty"` GatewayZones []string `yaml:"gateway_zones,omitempty" json:"gateway_zones,omitempty"` Port int `yaml:"port,omitempty" json:"port,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedStoppedRule struct { Action string `yaml:"action" json:"action"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` DPort []string `yaml:"dport,omitempty" json:"dport,omitempty"` SPort []string `yaml:"sport,omitempty" json:"sport,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedProxy struct { Address string `yaml:"address" json:"address"` Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` External string `yaml:"external" json:"external"` HaveRoute bool `yaml:"haveroute,omitempty" json:"haveroute,omitempty"` Persistent bool `yaml:"persistent,omitempty" json:"persistent,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedArpRule struct { Action string `yaml:"action" json:"action"` ActionAddress string `yaml:"action_address,omitempty" json:"action_address,omitempty"` ActionMAC string `yaml:"action_mac,omitempty" json:"action_mac,omitempty"` Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Opcode int `yaml:"opcode,omitempty" json:"opcode,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedMaclist struct { Action string `yaml:"action" json:"action"` Interface string `yaml:"interface" json:"interface"` MAC string `yaml:"mac,omitempty" json:"mac,omitempty"` Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"` Log string `yaml:"log,omitempty" json:"log,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedHost struct { Zone string `yaml:"zone" json:"zone"` Interface string `yaml:"interface" json:"interface"` Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"` Exclusions []string `yaml:"exclusions,omitempty" json:"exclusions,omitempty"` Dynamic bool `yaml:"dynamic,omitempty" json:"dynamic,omitempty"` } type RenderedProvider struct { Name string `yaml:"name" json:"name"` Number int `yaml:"number" json:"number"` Mark int `yaml:"mark,omitempty" json:"mark,omitempty"` Duplicate string `yaml:"duplicate,omitempty" json:"duplicate,omitempty"` Interface string `yaml:"interface" json:"interface"` Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"` Copy []string `yaml:"copy,omitempty" json:"copy,omitempty"` } type RenderedRoute struct { Provider string `yaml:"provider,omitempty" json:"provider,omitempty"` Dest string `yaml:"dest" json:"dest"` Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"` Oif string `yaml:"oif,omitempty" json:"oif,omitempty"` Persistent bool `yaml:"persistent,omitempty" json:"persistent,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedRoutingRule struct { Source string `yaml:"source,omitempty" json:"source,omitempty"` Dest string `yaml:"dest,omitempty" json:"dest,omitempty"` Provider string `yaml:"provider" json:"provider"` Priority int `yaml:"priority,omitempty" json:"priority,omitempty"` Persistent bool `yaml:"persistent,omitempty" json:"persistent,omitempty"` Mark string `yaml:"mark,omitempty" json:"mark,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } // RenderedSNAT is a resolved SNAT/masquerade rule (egress carries interface names). type RenderedSNAT struct { Action string `yaml:"action" json:"action"` Source []string `yaml:"source,omitempty" json:"source,omitempty"` Egress []string `yaml:"egress" json:"egress"` Address string `yaml:"address,omitempty" json:"address,omitempty"` Probability *float64 `yaml:"probability,omitempty" json:"probability,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } // RenderedNetmap is a resolved network-to-network mapping on one interface. type RenderedNetmap struct { Type string `yaml:"type" json:"type"` FromNet string `yaml:"from_net" json:"from_net"` ToNet string `yaml:"to_net" json:"to_net"` Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } // RenderedNAT is a resolved one-to-one static NAT on one interface. type RenderedNAT struct { External string `yaml:"external" json:"external"` Internal string `yaml:"internal" json:"internal"` Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedSettings struct { AddressFamily string `yaml:"address_family" json:"address_family"` LogLevel string `yaml:"log_level" json:"log_level"` IPForwarding bool `yaml:"ip_forwarding" json:"ip_forwarding"` TableName string `yaml:"table_name" json:"table_name"` } // RenderedSet is an address group's nftables set. Members carries the concrete // elements the control plane knows (static CIDRs, expanded ASN prefixes); FQDNs // are resolved on-device; ASNs are informational (already expanded into Members). type RenderedSet struct { Name string `yaml:"name" json:"name"` Kind string `yaml:"kind" json:"kind"` // static | dns | asn Members []string `yaml:"members,omitempty" json:"members,omitempty"` FQDNs []string `yaml:"fqdns,omitempty" json:"fqdns,omitempty"` ASNs []string `yaml:"asns,omitempty" json:"asns,omitempty"` Refresh string `yaml:"refresh,omitempty" json:"refresh,omitempty"` } // RenderedMatch is one OR'd element of a rule direction: a zone's subnets AND, // optionally, a named set to intersect with. type RenderedMatch struct { Zone string `yaml:"zone" json:"zone"` Subnets []string `yaml:"subnets,omitempty" json:"subnets,omitempty"` Set string `yaml:"set,omitempty" json:"set,omitempty"` } type RenderedRule struct { Action string `yaml:"action" json:"action"` Source []RenderedMatch `yaml:"source" json:"source"` Dest []RenderedMatch `yaml:"dest" json:"dest"` Proto string `yaml:"proto,omitempty" json:"proto,omitempty"` Ports []string `yaml:"ports,omitempty" json:"ports,omitempty"` Log string `yaml:"log,omitempty" json:"log,omitempty"` Comment string `yaml:"comment,omitempty" json:"comment,omitempty"` } type RenderedPolicy struct { Priority int `yaml:"priority" json:"priority"` Source string `yaml:"source" json:"source"` Dest string `yaml:"dest" json:"dest"` Action string `yaml:"action" json:"action"` Log string `yaml:"log,omitempty" json:"log,omitempty"` } // setMembers returns the concrete address elements for a set: static/asn use // Members; dns is resolved separately and merged in before translation. func (s RenderedSet) staticMembers() []string { return s.Members }