mirror of
https://github.com/apernet/OpenGFW.git
synced 2024-12-23 01:19:21 +08:00
Add CIDR support for expr (#62)
* feat: add cidr support for expr * docs: add example for cidr * minor code tweaks --------- Co-authored-by: Toby <tobyxdd@gmail.com>
This commit is contained in:
parent
94cfe7b2c1
commit
ebff4308e4
@ -130,6 +130,10 @@ workers:
|
|||||||
- name: block CN geoip
|
- name: block CN geoip
|
||||||
action: block
|
action: block
|
||||||
expr: geoip(string(ip.dst), "cn")
|
expr: geoip(string(ip.dst), "cn")
|
||||||
|
|
||||||
|
- name: block cidr
|
||||||
|
action: block
|
||||||
|
expr: cidr(string(ip.dst), "192.168.0.0/16")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### サポートされるアクション
|
#### サポートされるアクション
|
||||||
|
@ -136,6 +136,10 @@ to [Expr Language Definition](https://expr-lang.org/docs/language-definition).
|
|||||||
- name: block CN geoip
|
- name: block CN geoip
|
||||||
action: block
|
action: block
|
||||||
expr: geoip(string(ip.dst), "cn")
|
expr: geoip(string(ip.dst), "cn")
|
||||||
|
|
||||||
|
- name: block cidr
|
||||||
|
action: block
|
||||||
|
expr: cidr(string(ip.dst), "192.168.0.0/16")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Supported actions
|
#### Supported actions
|
||||||
|
@ -131,6 +131,10 @@ workers:
|
|||||||
- name: block CN geoip
|
- name: block CN geoip
|
||||||
action: block
|
action: block
|
||||||
expr: geoip(string(ip.dst), "cn")
|
expr: geoip(string(ip.dst), "cn")
|
||||||
|
|
||||||
|
- name: block cidr
|
||||||
|
action: block
|
||||||
|
expr: cidr(string(ip.dst), "192.168.0.0/16")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 支持的 action
|
#### 支持的 action
|
||||||
|
18
ruleset/builtins/cidr.go
Normal file
18
ruleset/builtins/cidr.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MatchCIDR(ip string, cidr *net.IPNet) bool {
|
||||||
|
ipAddr := net.ParseIP(ip)
|
||||||
|
if ipAddr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return cidr.Contains(ipAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileCIDR(cidr string) (*net.IPNet, error) {
|
||||||
|
_, ipNet, err := net.ParseCIDR(cidr)
|
||||||
|
return ipNet, err
|
||||||
|
}
|
@ -2,6 +2,7 @@ package ruleset
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/apernet/OpenGFW/analyzer"
|
"github.com/apernet/OpenGFW/analyzer"
|
||||||
"github.com/apernet/OpenGFW/modifier"
|
"github.com/apernet/OpenGFW/modifier"
|
||||||
|
"github.com/apernet/OpenGFW/ruleset/builtins"
|
||||||
"github.com/apernet/OpenGFW/ruleset/builtins/geo"
|
"github.com/apernet/OpenGFW/ruleset/builtins/geo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,17 +102,21 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
|||||||
return nil, fmt.Errorf("rule %q has invalid action %q", rule.Name, rule.Action)
|
return nil, fmt.Errorf("rule %q has invalid action %q", rule.Name, rule.Action)
|
||||||
}
|
}
|
||||||
visitor := &idVisitor{Identifiers: make(map[string]bool)}
|
visitor := &idVisitor{Identifiers: make(map[string]bool)}
|
||||||
|
patcher := &idPatcher{}
|
||||||
program, err := expr.Compile(rule.Expr,
|
program, err := expr.Compile(rule.Expr,
|
||||||
func(c *conf.Config) {
|
func(c *conf.Config) {
|
||||||
c.Strict = false
|
c.Strict = false
|
||||||
c.Expect = reflect.Bool
|
c.Expect = reflect.Bool
|
||||||
c.Visitors = append(c.Visitors, visitor)
|
c.Visitors = append(c.Visitors, visitor, patcher)
|
||||||
registerBuiltinFunctions(c.Functions, geoMatcher)
|
registerBuiltinFunctions(c.Functions, geoMatcher)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("rule %q has invalid expression: %w", rule.Name, err)
|
return nil, fmt.Errorf("rule %q has invalid expression: %w", rule.Name, err)
|
||||||
}
|
}
|
||||||
|
if patcher.Err != nil {
|
||||||
|
return nil, fmt.Errorf("rule %q failed to patch expression: %w", rule.Name, patcher.Err)
|
||||||
|
}
|
||||||
for name := range visitor.Identifiers {
|
for name := range visitor.Identifiers {
|
||||||
if isBuiltInAnalyzer(name) {
|
if isBuiltInAnalyzer(name) {
|
||||||
continue
|
continue
|
||||||
@ -126,6 +132,8 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
|||||||
if err := geoMatcher.LoadGeoSite(); err != nil {
|
if err := geoMatcher.LoadGeoSite(); err != nil {
|
||||||
return nil, fmt.Errorf("rule %q failed to load geosite: %w", rule.Name, err)
|
return nil, fmt.Errorf("rule %q failed to load geosite: %w", rule.Name, err)
|
||||||
}
|
}
|
||||||
|
case "cidr":
|
||||||
|
// No initialization needed for CIDR.
|
||||||
default:
|
default:
|
||||||
a, ok := fullAnMap[name]
|
a, ok := fullAnMap[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -179,6 +187,13 @@ func registerBuiltinFunctions(funcMap map[string]*ast.Function, geoMatcher *geo.
|
|||||||
},
|
},
|
||||||
Types: []reflect.Type{reflect.TypeOf(geoMatcher.MatchGeoSite)},
|
Types: []reflect.Type{reflect.TypeOf(geoMatcher.MatchGeoSite)},
|
||||||
}
|
}
|
||||||
|
funcMap["cidr"] = &ast.Function{
|
||||||
|
Name: "cidr",
|
||||||
|
Func: func(params ...any) (any, error) {
|
||||||
|
return builtins.MatchCIDR(params[0].(string), params[1].(*net.IPNet)), nil
|
||||||
|
},
|
||||||
|
Types: []reflect.Type{reflect.TypeOf((func(string, string) bool)(nil)), reflect.TypeOf(builtins.MatchCIDR)},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func streamInfoToExprEnv(info StreamInfo) map[string]interface{} {
|
func streamInfoToExprEnv(info StreamInfo) map[string]interface{} {
|
||||||
@ -247,6 +262,8 @@ func modifiersToMap(mods []modifier.Modifier) map[string]modifier.Modifier {
|
|||||||
return modMap
|
return modMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// idVisitor is a visitor that collects all identifiers in an expression.
|
||||||
|
// This is for determining which analyzers are used by the expression.
|
||||||
type idVisitor struct {
|
type idVisitor struct {
|
||||||
Identifiers map[string]bool
|
Identifiers map[string]bool
|
||||||
}
|
}
|
||||||
@ -256,3 +273,29 @@ func (v *idVisitor) Visit(node *ast.Node) {
|
|||||||
v.Identifiers[idNode.Value] = true
|
v.Identifiers[idNode.Value] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// idPatcher patches the AST during expr compilation, replacing certain values with
|
||||||
|
// their internal representations for better runtime performance.
|
||||||
|
type idPatcher struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *idPatcher) Visit(node *ast.Node) {
|
||||||
|
switch (*node).(type) {
|
||||||
|
case *ast.CallNode:
|
||||||
|
callNode := (*node).(*ast.CallNode)
|
||||||
|
switch callNode.Func.Name {
|
||||||
|
case "cidr":
|
||||||
|
cidrStringNode, ok := callNode.Arguments[1].(*ast.StringNode)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cidr, err := builtins.CompileCIDR(cidrStringNode.Value)
|
||||||
|
if err != nil {
|
||||||
|
p.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callNode.Arguments[1] = &ast.ConstantNode{Value: cidr}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user