Documentation
¶
Overview ¶
Package routing provides bytecode based routing rules for Gonnect middleware.
The package can build gonnect.RouterCfg, tun.SplitRouter, sniffer.Sniffer control callbacks, gonnect.Remapper rules, and gonnect/dns.RouteFunc values from immutable bytecode tables or from a small text rule format.
Index ¶
- Constants
- func NewBytecodeDNSRouteFunc(rules DNSBytecodeRules) (gdns.RouteFunc, error)
- func NewBytecodeRemapRules(rules RemapperBytecodeRules) ([]gonnect.RemapRule, error)
- func NewBytecodeSniffer(config sniffer.SnifferConfig, rules SnifferBytecodeRules) (*sniffer.Sniffer, error)
- func NewRemapRules(program string) ([]gonnect.RemapRule, error)
- type BytecodeRules
- type DNSBytecodeRules
- type IPv4Subnet
- type NamedSniffClassifier
- type NamedSniffClassifierConstructor
- type RemapAction
- type RemapperBytecodeRule
- type RemapperBytecodeRules
- type RouterCfg
- type SlotReporter
- type SniffClassifierConstructor
- type SniffClassifierOption
- type SnifferBytecodeRules
- type SnifferCallMutation
- type SnifferControls
- type SnifferRouteAction
- type SplitBytecodeRules
- type SplitRouter
Constants ¶
const ( // OP_DROP pops a boolean value and routes to slot 0 when it is true. OP_DROP byte = iota // OP_SLOT pops a boolean value and routes to the following uint8 slot when it is true. OP_SLOT // OP_TRUE pushes true onto the boolean stack. OP_TRUE // OP_FALSE pushes false onto the boolean stack. OP_FALSE // OP_NOT inverts the boolean value on top of the stack. OP_NOT // OP_AND replaces the two top stack values with their logical conjunction. OP_AND // OP_OR replaces the two top stack values with their logical disjunction. OP_OR // OP_NET4 pushes whether the operation is for IPv4 or has an IPv4 address. OP_NET4 // OP_NET6 pushes whether the operation is for IPv6 or has an IPv6 address. OP_NET6 // OP_UDP pushes whether the operation uses a UDP network. OP_UDP // OP_TCP pushes whether the operation uses a TCP network. OP_TCP // OP_FQDN pushes whether the remote address is a hostname rather than an IP. OP_FQDN // OP_LFQDN pushes whether the local address is a hostname rather than an IP. OP_LFQDN // OP_ADDR_S pushes whether the remote address host equals a string table value. OP_ADDR_S // OP_LADDR_S pushes whether the local address host equals a string table value. OP_LADDR_S // OP_ADDR_RE pushes whether the remote address host matches a regexp table value. OP_ADDR_RE // OP_LADDR_RE pushes whether the local address host matches a regexp table value. OP_LADDR_RE // OP_ADDR4 pushes whether the remote address equals an IPv4 table value. OP_ADDR4 // OP_LADDR4 pushes whether the local address equals an IPv4 table value. OP_LADDR4 // OP_ADDR6 pushes whether the remote address equals an IPv6 table value. OP_ADDR6 // OP_LADDR6 pushes whether the local address equals an IPv6 table value. OP_LADDR6 // OP_SNET4 pushes whether the remote IPv4 address is in an IPv4 subnet table value. OP_SNET4 // OP_LSNET4 pushes whether the local IPv4 address is in an IPv4 subnet table value. OP_LSNET4 // OP_SNET6 pushes whether the remote IPv6 address is in an IPv6 subnet table value. OP_SNET6 // OP_LSNET6 pushes whether the local IPv6 address is in an IPv6 subnet table value. OP_LSNET6 // OP_PORT pushes whether the remote address port equals the following uint16 port. OP_PORT // OP_LPORT pushes whether the local address port equals the following uint16 port. OP_LPORT // OP_RULE pushes whether the packet flow matches a sysnet rule. OP_RULE // OP_DIAL pushes whether the RouterCfg method is DialTCP, DialUDP, or RouteUDP. OP_DIAL // OP_LISTEN pushes whether the RouterCfg method is ListenTCP. OP_LISTEN // OP_LOOKUP pushes whether the RouterCfg method is Lookup. OP_LOOKUP // OP_INTERCEPT pops a boolean value and asks sniffer.Sniffer to sniff the // TCP stream when it is true. It is valid only in Sniffer control bytecode. OP_INTERCEPT // OP_SNIFF pushes whether the sniff result matched the named classifier // referenced by the following uint16 classifier table index. OP_SNIFF // OP_SNIFF_NONE pushes whether sniffing found no matching classifier. OP_SNIFF_NONE // OP_ROUTE pops a boolean value, mutates a sniffer call, and routes to // a route-action slot when it is true. OP_ROUTE // OP_BACKEND pops a boolean value and routes a DNS request to the following // backend-name table index when it is true. OP_BACKEND // OP_QTYPE pushes whether the first DNS question type equals the following // uint16 value. OP_QTYPE // OP_QCLASS pushes whether the first DNS question class equals the following // uint16 value. OP_QCLASS // OP_OPCODE pushes whether the DNS message opcode equals the following // uint16 value. OP_OPCODE )
Variables ¶
This section is empty.
Functions ¶
func NewBytecodeDNSRouteFunc ¶ added in v0.40.0
func NewBytecodeDNSRouteFunc(rules DNSBytecodeRules) (gdns.RouteFunc, error)
NewBytecodeDNSRouteFunc validates rules and returns a dns.RouteFunc for gonnect/dns.Router.
func NewBytecodeRemapRules ¶ added in v0.40.0
func NewBytecodeRemapRules( rules RemapperBytecodeRules, ) ([]gonnect.RemapRule, error)
NewBytecodeRemapRules validates rules and returns gonnect.RemapRule values that can be passed to gonnect.NewRemapper.
func NewBytecodeSniffer ¶ added in v0.39.0
func NewBytecodeSniffer( config sniffer.SnifferConfig, rules SnifferBytecodeRules, ) (*sniffer.Sniffer, error)
NewBytecodeSniffer validates rules and returns callbacks that can be used by sniffer.NewSniffer. Existing Control, SniffControl, and Classifiers fields in config are replaced with the bytecode-backed values.
Types ¶
type BytecodeRules ¶
type BytecodeRules struct {
Strings []string
Regexps []*regexp.Regexp
IPv4Addrs []uint32
IPv4Subnets []IPv4Subnet
IPv6Addrs []netip.Addr
IPv6Subnets []netip.Prefix
DNSCacheStorage gdns.CacheStorage
DialTCP []byte
ListenTCP []byte
DialUDP []byte
RouteUDP []byte
Lookup []byte
}
BytecodeRules contains the immutable tables and bytecode programs used to build a gonnect.RouterCfg.
Each program is encoded as one-byte opcodes followed by the opcode parameter when it has one. OP_SLOT uses one uint8 parameter. String, regexp, address, subnet, and port operations use one little-endian uint16 parameter. A program routes to slot 0 when it finishes without a matching OP_DROP or OP_SLOT. OP_DIAL, OP_LISTEN, and OP_LOOKUP push the current RouterCfg method class: DialTCP, DialUDP, and RouteUDP are dial operations; ListenTCP is a listen operation; Lookup is a lookup operation.
Every bytecode slice is validated by NewBytecodeRouterCfg. The constructor copies all slices, so later changes to BytecodeRules do not affect routing. When DNSCacheStorage is set, cached reverse DNS names are matched exactly as stored; names produced by github.com/asciimoth/gonnect/dns.Cache are usually absolute names with a trailing dot, such as "example.test.".
func NewBytecodeRules ¶
func NewBytecodeRules( dialTCP, listenTCP, dialUDP, routeUDP, lookup string, ) (BytecodeRules, error)
NewBytecodeRules parses the simple routing rules language into BytecodeRules.
func NewBytecodeRulesProgram ¶ added in v0.39.0
func NewBytecodeRulesProgram(program string) (BytecodeRules, error)
NewBytecodeRulesProgram parses one routing rules program and derives the five RouterCfg method programs from it.
The input is split into independent segments. A segment ends at a DROP or SLOT operation, matched case-insensitively, or at the end of the string. Each derived method program receives only the segments that can still affect that method:
- segments without DIAL, LISTEN, or LOOKUP are copied to every method;
- segments with method operations are omitted only when their terminal DROP or SLOT condition is provably false for that method.
The proof is intentionally conservative. Method operations are evaluated as constants for the target method, TRUE/FALSE/NOT/AND/OR are evaluated exactly, and every runtime-dependent predicate is treated as unknown. Unknown, invalid, or unterminated segments are kept and then validated normally by NewBytecodeRouterCfg.
type DNSBytecodeRules ¶ added in v0.40.0
type DNSBytecodeRules struct {
// Backends is the backend-name table used by OP_BACKEND.
Backends []string
Strings []string
Regexps []*regexp.Regexp
IPv4Addrs []uint32
IPv4Subnets []IPv4Subnet
IPv6Addrs []netip.Addr
IPv6Subnets []netip.Prefix
Route []byte
}
DNSBytecodeRules contains immutable tables and one bytecode program used to build a gonnect/dns.RouteFunc.
The route program evaluates the first DNS question in the message. Remote address predicates such as OP_FQDN, OP_ADDR_S, and OP_ADDR_RE test the first question name. DNS-specific predicates test request metadata:
- OP_QTYPE tests the first question type.
- OP_QCLASS tests the first question class.
- OP_OPCODE tests the message opcode.
OP_BACKEND is the DNS route action. It pops a condition and returns the backend name at its table index when the condition is true. OP_DROP also pops a condition and returns an empty backend name when the condition is true. Empty backend names, unmatched rules, and names without attached backends make gonnect/dns.Router reject the request with dns.ErrNoUpstream.
NewBytecodeDNSRouteFunc validates and copies all slices, so later changes to DNSBytecodeRules do not affect routing.
func NewDNSBytecodeRules ¶ added in v0.40.0
func NewDNSBytecodeRules(route string) (DNSBytecodeRules, error)
NewDNSBytecodeRules parses the simple routing rules language into DNSBytecodeRules.
DNS rules use BACKEND as their terminal route operation:
BACKEND secure
The backend name is returned by the generated dns.RouteFunc and should match a backend attached to gonnect/dns.Router.
type IPv4Subnet ¶
IPv4Subnet is an IPv4 CIDR subnet used by bytecode routing rules.
Addr is the canonical big-endian 32-bit IPv4 address. Bits is the CIDR prefix length and must be in the range 0..32.
type NamedSniffClassifier ¶ added in v0.39.0
NamedSniffClassifier binds a rule-language name to a Sniffer classifier factory.
Name must be non-empty and must not contain white space. Factory can be any implementation of sniffer.Factory. Rules can refer to the name with SNIFF <name>.
type NamedSniffClassifierConstructor ¶ added in v0.39.0
type NamedSniffClassifierConstructor struct {
Name string
Constructor SniffClassifierConstructor
}
NamedSniffClassifierConstructor binds a constructor name to an inline SNIFF classifier constructor.
Name is case-sensitive and must be non-empty. It must not contain white space or ":". Constructor must not be nil.
func DefaultSniffClassifierConstructors ¶ added in v0.39.0
func DefaultSniffClassifierConstructors() []NamedSniffClassifierConstructor
DefaultSniffClassifierConstructors returns the built-in inline SNIFF classifier constructors.
The current collection contains HTTP and TLS. The returned slice is a copy and can be changed by the caller before it is passed to NewSnifferBytecodeRulesWithConstructors.
type RemapAction ¶ added in v0.40.0
type RemapAction struct {
Endpoint gonnect.RemapEndpoint
Field gonnect.RemapField
Addr string
Port string
}
RemapAction describes the fixed rewrite done by one REMAP rule.
Endpoint selects source or destination. Field selects which part of that endpoint changes. Addr and Port have the same meaning as gonnect.RemapRule.
type RemapperBytecodeRule ¶ added in v0.40.0
type RemapperBytecodeRule struct {
Predicate []byte
Action RemapAction
}
RemapperBytecodeRule is one bytecode predicate plus one remap action.
Predicate is stack bytecode that must leave exactly one boolean value. An empty predicate matches all calls that expose Action.Endpoint.
type RemapperBytecodeRules ¶ added in v0.40.0
type RemapperBytecodeRules struct {
Strings []string
Regexps []*regexp.Regexp
IPv4Addrs []uint32
IPv4Subnets []IPv4Subnet
IPv6Addrs []netip.Addr
IPv6Subnets []netip.Prefix
// DNSCacheStorage enables cached reverse-DNS matches for ADDR_S,
// LADDR_S, ADDR_RE, and LADDR_RE when addresses are IP literals. The
// filter never performs live DNS lookups.
DNSCacheStorage gdns.CacheStorage
Rules []RemapperBytecodeRule
}
RemapperBytecodeRules contains immutable tables and bytecode used to build gonnect.RemapRule values.
REMAP rules evaluate in order. Each matching rule applies, and later rules see the network and addresses left by earlier rules. This matches gonnect.Remapper.
func NewRemapperBytecodeRules ¶ added in v0.40.0
func NewRemapperBytecodeRules(program string) (RemapperBytecodeRules, error)
NewRemapperBytecodeRules parses one rule text into RemapperBytecodeRules.
Each segment contains normal bytecode predicates and ends with REMAP:
REMAP <endpoint> <field> <value>
Endpoint is SRC or DST. Field is ADDR_PORT, ADDR, or PORT. ADDR_PORT value must be a host:port endpoint, for example "127.0.0.1:8080" or "[::1]:443". ADDR and PORT values are used as written and are not resolved.
type RouterCfg ¶ added in v0.39.0
type RouterCfg interface {
gonnect.RouterCfg
SlotReporter
}
func NewBytecodeRouterCfg ¶
func NewBytecodeRouterCfg(rules BytecodeRules) (RouterCfg, error)
NewBytecodeRouterCfg validates rules and returns a gonnect.RouterCfg that evaluates stack-based bytecode for each Router operation.
type SlotReporter ¶ added in v0.39.0
type SlotReporter interface {
MentionedSlots() []int
}
SlotReporter is implemented by bytecode-backed routers that can report the non-drop slots mentioned by their rules.
type SniffClassifierConstructor ¶ added in v0.39.0
type SniffClassifierConstructor func( options []SniffClassifierOption, ) (canonical string, factory sniffer.Factory, err error)
SniffClassifierConstructor builds one Sniffer classifier factory from inline SNIFF options.
The returned canonical string must identify the effective options. It is used only for de-duplication, so equivalent option sets should return the same canonical string. Return an empty canonical string when no option is set.
type SniffClassifierOption ¶ added in v0.39.0
SniffClassifierOption is one KEY:VALUE option from an inline SNIFF classifier specification.
Constructor implementations receive keys after parser normalization. Values are not decoded or changed by the parser.
type SnifferBytecodeRules ¶ added in v0.39.0
type SnifferBytecodeRules struct {
Classifiers []NamedSniffClassifier
Strings []string
Regexps []*regexp.Regexp
IPv4Addrs []uint32
IPv4Subnets []IPv4Subnet
IPv6Addrs []netip.Addr
IPv6Subnets []netip.Prefix
DNSCacheStorage gdns.CacheStorage
// RouteActions contains the fixed call changes used by OP_ROUTE.
RouteActions []SnifferRouteAction
Control []byte
SniffControl []byte
}
SnifferBytecodeRules contains the immutable tables and bytecode programs used to build gonnect/sniffer control callbacks.
Control runs before sniffing. It can route directly with OP_SLOT, OP_DROP, or OP_ROUTE, or request TCP interception with OP_INTERCEPT. SniffControl runs after Sniffer restores inspected bytes. It can route with OP_SLOT, OP_DROP, or OP_ROUTE, and it can test the matched classifier with OP_SNIFF or OP_SNIFF_NONE.
NewSnifferBytecodeRules derives Control and SniffControl from one rule text. Sniffer-only segments are copied only to the phase where they make sense; normal address, network, method, and slot segments are copied to both.
Classifiers contains named classifiers supplied by the caller and generated inline classifiers built from SNIFF specs such as "SNIFF HTTP URL:/blocked".
func NewSnifferBytecodeRules ¶ added in v0.39.0
func NewSnifferBytecodeRules( classifiers []NamedSniffClassifier, program string, ) (SnifferBytecodeRules, error)
NewSnifferBytecodeRules parses one rules program and derives the Sniffer control and sniff-control programs from it.
INTERCEPT segments are copied only to Control. SNIFF and SNIFF_NONE segments are copied only to SniffControl. Segments that use neither Sniffer-only operation are copied to both programs. A segment that uses SNIFF and ends in INTERCEPT is rejected because it has no meaningful execution phase.
SNIFF can refer to a named classifier from classifiers, or to an inline classifier specification built by DefaultSniffClassifierConstructors.
func NewSnifferBytecodeRulesWithConstructors ¶ added in v0.39.0
func NewSnifferBytecodeRulesWithConstructors( classifiers []NamedSniffClassifier, constructors []NamedSniffClassifierConstructor, program string, ) (SnifferBytecodeRules, error)
NewSnifferBytecodeRulesWithConstructors parses one rules program like NewSnifferBytecodeRules, but uses constructors as the inline SNIFF classifier collection.
A SNIFF argument that matches a named classifier still refers to that classifier. Other arguments can use this form:
SNIFF <constructor> [<option>:<value> ...]
Constructor names are case-sensitive. Option names are normalized by each constructor. Repeated equivalent inline specifications share one generated classifier entry. Generated classifiers are appended after the named classifiers, in first-use order.
type SnifferCallMutation ¶ added in v0.39.0
type SnifferCallMutation struct {
SetNetwork bool
Network string
SetSrc bool
Src string
SetDst bool
Dst string
SetSrcAddr bool
SrcAddr string
SetSrcPort bool
SrcPort string
SetDstAddr bool
DstAddr string
SetDstPort bool
DstPort string
SetHost bool
Host string
SetService bool
Service string
SetProto bool
Proto string
}
SnifferCallMutation contains fixed values that OP_ROUTE can write to a sniffer.Call.
A Set field controls whether the paired value is written. Full endpoint replacements, Src and Dst, are applied before endpoint-part replacements.
type SnifferControls ¶ added in v0.39.0
type SnifferControls interface {
SlotReporter
Control(call *sniffer.Call) sniffer.Action
SniffControl(call *sniffer.SniffedCall) sniffer.Action
Classifiers() []sniffer.Factory
}
SnifferControls is the bytecode-backed control pair for sniffer.Sniffer.
func NewBytecodeSnifferControls ¶ added in v0.39.0
func NewBytecodeSnifferControls( rules SnifferBytecodeRules, ) (SnifferControls, error)
NewBytecodeSnifferControls validates rules and returns a reusable Sniffer control pair. Sniff errors always reject, independent of SniffControl bytecode.
type SnifferRouteAction ¶ added in v0.39.0
type SnifferRouteAction struct {
Slot uint8
Mutation SnifferCallMutation
}
SnifferRouteAction is a route target with call field changes.
Slot is the output slot returned by OP_ROUTE. Mutation is applied to the active sniffer.Call before the action is returned.
type SplitBytecodeRules ¶
type SplitBytecodeRules struct {
System sysnet.System
Rules []sysnet.Rule
// RuleCacheTTL controls how long OP_RULE matcher results are cached by
// flow. A zero value uses the default TTL; a negative value disables the
// cross-packet rule cache.
RuleCacheTTL time.Duration
// RuleCacheMaxEntries bounds the cross-packet OP_RULE cache. A zero value
// uses the default size; a negative value disables the cache.
RuleCacheMaxEntries int
// RouteCacheTTL controls how long whole bytecode route results are cached
// by packet flow. When RouteCacheTTL and RouteCacheMaxEntries are both
// zero, the route cache inherits the OP_RULE cache settings; this means
// disabling the rule cache also disables the default route cache. A negative
// value disables the route cache.
RouteCacheTTL time.Duration
// RouteCacheMaxEntries bounds the whole bytecode route-result cache. A zero
// value uses the inherited or default size; a negative value disables the
// route cache.
RouteCacheMaxEntries int
Strings []string
Regexps []*regexp.Regexp
IPv4Addrs []uint32
IPv4Subnets []IPv4Subnet
IPv6Addrs []netip.Addr
IPv6Subnets []netip.Prefix
DNSCacheStorage gdns.CacheStorage
Route []byte
}
SplitBytecodeRules contains the immutable tables and bytecode program used to build a tun.SplitRouter.
The packet router supports the common bytecode opcodes plus OP_RULE. OP_RULE indexes Rules and is evaluated by a sysnet.Matcher built from System. The RouterCfg method opcodes OP_DIAL, OP_LISTEN, and OP_LOOKUP are not valid for packet routing. The constructor validates and copies all slices before returning the router. When DNSCacheStorage is set, cached reverse DNS names are matched exactly as stored; names produced by github.com/asciimoth/gonnect/dns.Cache are usually absolute names with a trailing dot, such as "example.test.".
func NewSplitBytecodeRules ¶
func NewSplitBytecodeRules( system sysnet.System, route string, ) (SplitBytecodeRules, error)
NewSplitBytecodeRules parses the simple routing rules language into SplitBytecodeRules. RULE takes a sysnet rule type followed by the rule text:
RULE app org.example.App
The first field after RULE becomes sysnet.Rule.Type. Everything after the separating space or tab becomes sysnet.Rule.Rule verbatim, so rule text may contain spaces and tabs.
type SplitRouter ¶ added in v0.39.0
type SplitRouter interface {
tun.SplitRouter
SlotReporter
Close() error
}
func NewBytecodeSplitRouter ¶
func NewBytecodeSplitRouter(rules SplitBytecodeRules) (SplitRouter, error)
NewBytecodeSplitRouter validates rules and returns a SplitRouter that evaluates stack-based bytecode against IP packets. The returned router owns matchers built from rules.System; call Close when the router is no longer used.