mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-28 22:23:24 +00:00
- Add detailed package documentation to engine.go explaining the rule matching system, supported rule types (Network, MAC, Domain), and priority ordering - Include usage example demonstrating typical API usage patterns - Remove unused Type() method from RuleMatcher interface and implementations - Maintain backward compatibility while improving code documentation The documentation explains the policy-based DNS routing system and how different rule types interact with configurable priority ordering.
149 lines
4.4 KiB
Go
149 lines
4.4 KiB
Go
// Package rulematcher provides a flexible rule matching engine for DNS request routing.
|
|
//
|
|
// The rulematcher package implements a policy-based DNS routing system that allows
|
|
// configuring different types of rules to determine which upstream DNS servers should
|
|
// handle specific requests. It supports three types of rules:
|
|
//
|
|
// - Network rules: Match requests based on source IP address ranges
|
|
// - MAC rules: Match requests based on source MAC addresses
|
|
// - Domain rules: Match requests based on requested domain names
|
|
//
|
|
// The matching engine uses a configurable priority order to determine which rules
|
|
// take precedence when multiple rules match. By default, the priority order is:
|
|
// Network -> MAC -> Domain, with Domain rules having the highest priority and
|
|
// overriding all other matches.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// config := &MatchingConfig{
|
|
// Order: []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain},
|
|
// }
|
|
// engine := NewMatchingEngine(config)
|
|
//
|
|
// request := &MatchRequest{
|
|
// SourceIP: net.ParseIP("192.168.1.100"),
|
|
// SourceMac: "aa:bb:cc:dd:ee:ff",
|
|
// Domain: "example.com",
|
|
// Policy: policyConfig,
|
|
// Config: appConfig,
|
|
// }
|
|
//
|
|
// result := engine.FindUpstreams(ctx, request)
|
|
// if result.Matched {
|
|
// // Use result.Upstreams to route the request
|
|
// }
|
|
//
|
|
// The package maintains backward compatibility with existing behavior while
|
|
// providing a clean, extensible interface for adding new rule types.
|
|
package rulematcher
|
|
|
|
import (
|
|
"context"
|
|
)
|
|
|
|
// MatchingEngine orchestrates rule matching based on configurable order
|
|
type MatchingEngine struct {
|
|
config *MatchingConfig
|
|
matchers map[RuleType]RuleMatcher
|
|
}
|
|
|
|
// NewMatchingEngine creates a new matching engine with the given configuration
|
|
func NewMatchingEngine(config *MatchingConfig) *MatchingEngine {
|
|
if config == nil {
|
|
config = DefaultMatchingConfig()
|
|
}
|
|
|
|
engine := &MatchingEngine{
|
|
config: config,
|
|
matchers: map[RuleType]RuleMatcher{
|
|
RuleTypeNetwork: &NetworkRuleMatcher{},
|
|
RuleTypeMac: &MacRuleMatcher{},
|
|
RuleTypeDomain: &DomainRuleMatcher{},
|
|
},
|
|
}
|
|
|
|
return engine
|
|
}
|
|
|
|
// FindUpstreams determines which upstreams should handle a request based on policy rules
|
|
// It implements the original behavior where MAC and domain rules can override network rules
|
|
func (e *MatchingEngine) FindUpstreams(ctx context.Context, req *MatchRequest) *MatchingResult {
|
|
result := &MatchingResult{
|
|
Upstreams: []string{},
|
|
MatchedPolicy: "no policy",
|
|
MatchedNetwork: "no network",
|
|
MatchedRule: "no rule",
|
|
Matched: false,
|
|
SrcAddr: req.SourceIP.String(),
|
|
MatchedRuleType: "",
|
|
MatchingOrder: e.config.Order,
|
|
}
|
|
|
|
if req.Policy == nil {
|
|
return result
|
|
}
|
|
|
|
result.MatchedPolicy = req.Policy.Name
|
|
|
|
var networkMatch *MatchResult
|
|
var macMatch *MatchResult
|
|
var domainMatch *MatchResult
|
|
|
|
// Check all rule types and store matches
|
|
for _, ruleType := range e.config.Order {
|
|
matcher, exists := e.matchers[ruleType]
|
|
if !exists {
|
|
continue
|
|
}
|
|
|
|
matchResult := matcher.Match(ctx, req)
|
|
if matchResult.Matched {
|
|
switch matchResult.RuleType {
|
|
case RuleTypeNetwork:
|
|
networkMatch = matchResult
|
|
case RuleTypeMac:
|
|
macMatch = matchResult
|
|
case RuleTypeDomain:
|
|
domainMatch = matchResult
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine the final match based on original logic:
|
|
// Domain rules override everything, MAC rules override network rules
|
|
if domainMatch != nil {
|
|
result.Upstreams = domainMatch.Targets
|
|
result.Matched = true
|
|
result.MatchedRuleType = string(domainMatch.RuleType)
|
|
result.MatchedRule = domainMatch.MatchedRule
|
|
// Special case: domain rules override network rules
|
|
if networkMatch != nil {
|
|
result.MatchedNetwork = networkMatch.MatchedRule + " (unenforced)"
|
|
}
|
|
} else if macMatch != nil {
|
|
result.Upstreams = macMatch.Targets
|
|
result.Matched = true
|
|
result.MatchedRuleType = string(macMatch.RuleType)
|
|
result.MatchedNetwork = macMatch.MatchedRule
|
|
} else if networkMatch != nil {
|
|
result.Upstreams = networkMatch.Targets
|
|
result.Matched = true
|
|
result.MatchedRuleType = string(networkMatch.RuleType)
|
|
result.MatchedNetwork = networkMatch.MatchedRule
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// MatchingResult represents the result of the matching engine
|
|
type MatchingResult struct {
|
|
Upstreams []string
|
|
MatchedPolicy string
|
|
MatchedNetwork string
|
|
MatchedRule string
|
|
Matched bool
|
|
SrcAddr string
|
|
MatchedRuleType string
|
|
MatchingOrder []RuleType
|
|
}
|