Feature: add source ipcidr condition for all rules

This commit is contained in:
gVisor bot 2021-08-31 21:46:04 +08:00
parent 92396fc893
commit 66d9aab6cc
14 changed files with 155 additions and 93 deletions

View file

@ -52,5 +52,5 @@ type Rule interface {
Adapter() string Adapter() string
Payload() string Payload() string
ShouldResolveIP() bool ShouldResolveIP() bool
NetWork() NetWork RuleExtra() *RuleExtra
} }

25
constant/rule_extra.go Normal file
View file

@ -0,0 +1,25 @@
package constant
import "net"
type RuleExtra struct {
Network NetWork
SourceIPs []*net.IPNet
}
func (re *RuleExtra) NotMatchNetwork(network NetWork) bool {
return re.Network != ALLNet && re.Network != network
}
func (re *RuleExtra) NotMatchSourceIP(srcIP net.IP) bool {
if re.SourceIPs == nil {
return false
}
for _, ips := range re.SourceIPs {
if ips.Contains(srcIP) {
return false
}
}
return true
}

View file

@ -2,6 +2,8 @@ package rules
import ( import (
"errors" "errors"
"net"
"strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
@ -14,6 +16,7 @@ var (
func HasNoResolve(params []string) bool { func HasNoResolve(params []string) bool {
for _, p := range params { for _, p := range params {
p = strings.Trim(p, " ")
if p == noResolve { if p == noResolve {
return true return true
} }
@ -23,6 +26,7 @@ func HasNoResolve(params []string) bool {
func findNetwork(params []string) C.NetWork { func findNetwork(params []string) C.NetWork {
for _, p := range params { for _, p := range params {
p = strings.Trim(p, " ")
if p == "tcp" { if p == "tcp" {
return C.TCP return C.TCP
} else if p == "udp" { } else if p == "udp" {
@ -31,3 +35,23 @@ func findNetwork(params []string) C.NetWork {
} }
return C.ALLNet return C.ALLNet
} }
func findSourceIPs(params []string) []*net.IPNet {
var ips []*net.IPNet
for _, p := range params {
p = strings.Trim(p, " ")
if p == noResolve || len(p) < 7 {
continue
}
_, ipnet, err := net.ParseCIDR(p)
if err != nil {
continue
}
ips = append(ips, ipnet)
}
if len(ips) > 0 {
return ips
}
return nil
}

View file

@ -9,7 +9,7 @@ import (
type Domain struct { type Domain struct {
domain string domain string
adapter string adapter string
network C.NetWork ruleExtra *C.RuleExtra
} }
func (d *Domain) RuleType() C.RuleType { func (d *Domain) RuleType() C.RuleType {
@ -35,14 +35,14 @@ func (d *Domain) ShouldResolveIP() bool {
return false return false
} }
func (d *Domain) NetWork() C.NetWork { func (d *Domain) RuleExtra() *C.RuleExtra {
return d.network return d.ruleExtra
} }
func NewDomain(domain string, adapter string, network C.NetWork) *Domain { func NewDomain(domain string, adapter string, ruleExtra *C.RuleExtra) *Domain {
return &Domain{ return &Domain{
domain: strings.ToLower(domain), domain: strings.ToLower(domain),
adapter: adapter, adapter: adapter,
network: network, ruleExtra: ruleExtra,
} }
} }

View file

@ -9,7 +9,7 @@ import (
type DomainKeyword struct { type DomainKeyword struct {
keyword string keyword string
adapter string adapter string
network C.NetWork ruleExtra *C.RuleExtra
} }
func (dk *DomainKeyword) RuleType() C.RuleType { func (dk *DomainKeyword) RuleType() C.RuleType {
@ -36,14 +36,14 @@ func (dk *DomainKeyword) ShouldResolveIP() bool {
return false return false
} }
func (dk *DomainKeyword) NetWork() C.NetWork { func (dk *DomainKeyword) RuleExtra() *C.RuleExtra {
return dk.network return dk.ruleExtra
} }
func NewDomainKeyword(keyword string, adapter string, network C.NetWork) *DomainKeyword { func NewDomainKeyword(keyword string, adapter string, ruleExtra *C.RuleExtra) *DomainKeyword {
return &DomainKeyword{ return &DomainKeyword{
keyword: strings.ToLower(keyword), keyword: strings.ToLower(keyword),
adapter: adapter, adapter: adapter,
network: network, ruleExtra: ruleExtra,
} }
} }

View file

@ -9,7 +9,7 @@ import (
type DomainSuffix struct { type DomainSuffix struct {
suffix string suffix string
adapter string adapter string
network C.NetWork ruleExtra *C.RuleExtra
} }
func (ds *DomainSuffix) RuleType() C.RuleType { func (ds *DomainSuffix) RuleType() C.RuleType {
@ -36,14 +36,14 @@ func (ds *DomainSuffix) ShouldResolveIP() bool {
return false return false
} }
func (ds *DomainSuffix) NetWork() C.NetWork { func (ds *DomainSuffix) RuleExtra() *C.RuleExtra {
return ds.network return ds.ruleExtra
} }
func NewDomainSuffix(suffix string, adapter string, network C.NetWork) *DomainSuffix { func NewDomainSuffix(suffix string, adapter string, ruleExtra *C.RuleExtra) *DomainSuffix {
return &DomainSuffix{ return &DomainSuffix{
suffix: strings.ToLower(suffix), suffix: strings.ToLower(suffix),
adapter: adapter, adapter: adapter,
network: network, ruleExtra: ruleExtra,
} }
} }

View file

@ -28,8 +28,8 @@ func (f *Match) ShouldResolveIP() bool {
return false return false
} }
func (f *Match) NetWork() C.NetWork { func (f *Match) RuleExtra() *C.RuleExtra {
return C.ALLNet return nil
} }
func NewMatch(adapter string) *Match { func NewMatch(adapter string) *Match {

View file

@ -15,7 +15,7 @@ type GEOIP struct {
country string country string
adapter string adapter string
noResolveIP bool noResolveIP bool
network C.NetWork ruleExtra *C.RuleExtra
geoIPMatcher *router.GeoIPMatcher geoIPMatcher *router.GeoIPMatcher
} }
@ -43,11 +43,11 @@ func (g *GEOIP) ShouldResolveIP() bool {
return !g.noResolveIP return !g.noResolveIP
} }
func (g *GEOIP) NetWork() C.NetWork { func (g *GEOIP) RuleExtra() *C.RuleExtra {
return g.network return g.ruleExtra
} }
func NewGEOIP(country string, adapter string, noResolveIP bool, network C.NetWork) (*GEOIP, error) { func NewGEOIP(country string, adapter string, noResolveIP bool, ruleExtra *C.RuleExtra) (*GEOIP, error) {
geoLoaderName := "standard" geoLoaderName := "standard"
//geoLoaderName := "memconservative" //geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName) geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
@ -78,7 +78,7 @@ func NewGEOIP(country string, adapter string, noResolveIP bool, network C.NetWor
country: country, country: country,
adapter: adapter, adapter: adapter,
noResolveIP: noResolveIP, noResolveIP: noResolveIP,
network: network, ruleExtra: ruleExtra,
geoIPMatcher: geoIPMatcher, geoIPMatcher: geoIPMatcher,
} }

View file

@ -14,7 +14,7 @@ import (
type GEOSITE struct { type GEOSITE struct {
country string country string
adapter string adapter string
network C.NetWork ruleExtra *C.RuleExtra
matcher *router.DomainMatcher matcher *router.DomainMatcher
} }
@ -43,11 +43,11 @@ func (gs *GEOSITE) ShouldResolveIP() bool {
return false return false
} }
func (gs *GEOSITE) NetWork() C.NetWork { func (gs *GEOSITE) RuleExtra() *C.RuleExtra {
return gs.network return gs.ruleExtra
} }
func NewGEOSITE(country string, adapter string, network C.NetWork) (*GEOSITE, error) { func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) {
geoLoaderName := "standard" geoLoaderName := "standard"
//geoLoaderName := "memconservative" //geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName) geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
@ -74,7 +74,7 @@ func NewGEOSITE(country string, adapter string, network C.NetWork) (*GEOSITE, er
geoSite := &GEOSITE{ geoSite := &GEOSITE{
country: country, country: country,
adapter: adapter, adapter: adapter,
network: network, ruleExtra: ruleExtra,
matcher: matcher, matcher: matcher,
} }

View file

@ -23,7 +23,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
type IPCIDR struct { type IPCIDR struct {
ipnet *net.IPNet ipnet *net.IPNet
adapter string adapter string
network C.NetWork ruleExtra *C.RuleExtra
isSourceIP bool isSourceIP bool
noResolveIP bool noResolveIP bool
} }
@ -55,20 +55,20 @@ func (i *IPCIDR) ShouldResolveIP() bool {
return !i.noResolveIP return !i.noResolveIP
} }
func (i *IPCIDR) NetWork() C.NetWork { func (i *IPCIDR) RuleExtra() *C.RuleExtra {
return i.network return i.ruleExtra
} }
func NewIPCIDR(s string, adapter string, network C.NetWork, opts ...IPCIDROption) (*IPCIDR, error) { func NewIPCIDR(s string, adapter string, ruleExtra *C.RuleExtra, opts ...IPCIDROption) (*IPCIDR, error) {
_, ipnet, err := net.ParseCIDR(s) _, ipnet, err := net.ParseCIDR(s)
if err != nil { if err != nil {
return nil, errPayload return nil, errPayload
} }
ruleExtra.SourceIPs = nil
ipcidr := &IPCIDR{ ipcidr := &IPCIDR{
ipnet: ipnet, ipnet: ipnet,
adapter: adapter, adapter: adapter,
network: network, ruleExtra: ruleExtra,
} }
for _, o := range opts { for _, o := range opts {

View file

@ -10,32 +10,36 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
var ( var (
parseErr error parseErr error
parsed C.Rule parsed C.Rule
network = findNetwork(params)
) )
ruleExtra := &C.RuleExtra{
Network: findNetwork(params),
SourceIPs: findSourceIPs(params),
}
switch tp { switch tp {
case "DOMAIN": case "DOMAIN":
parsed = NewDomain(payload, target, network) parsed = NewDomain(payload, target, ruleExtra)
case "DOMAIN-SUFFIX": case "DOMAIN-SUFFIX":
parsed = NewDomainSuffix(payload, target, network) parsed = NewDomainSuffix(payload, target, ruleExtra)
case "DOMAIN-KEYWORD": case "DOMAIN-KEYWORD":
parsed = NewDomainKeyword(payload, target, network) parsed = NewDomainKeyword(payload, target, ruleExtra)
case "GEOSITE": case "GEOSITE":
parsed, parseErr = NewGEOSITE(payload, target, network) parsed, parseErr = NewGEOSITE(payload, target, ruleExtra)
case "GEOIP": case "GEOIP":
noResolve := HasNoResolve(params) noResolve := HasNoResolve(params)
parsed, parseErr = NewGEOIP(payload, target, noResolve, network) parsed, parseErr = NewGEOIP(payload, target, noResolve, ruleExtra)
case "IP-CIDR", "IP-CIDR6": case "IP-CIDR", "IP-CIDR6":
noResolve := HasNoResolve(params) noResolve := HasNoResolve(params)
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRNoResolve(noResolve)) parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRNoResolve(noResolve))
case "SRC-IP-CIDR": case "SRC-IP-CIDR":
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true)) parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
case "SRC-PORT": case "SRC-PORT":
parsed, parseErr = NewPort(payload, target, true, network) parsed, parseErr = NewPort(payload, target, true, ruleExtra)
case "DST-PORT": case "DST-PORT":
parsed, parseErr = NewPort(payload, target, false, network) parsed, parseErr = NewPort(payload, target, false, ruleExtra)
case "PROCESS-NAME": case "PROCESS-NAME":
parsed, parseErr = NewProcess(payload, target, network) parsed, parseErr = NewProcess(payload, target, ruleExtra)
case "MATCH": case "MATCH":
parsed = NewMatch(target) parsed = NewMatch(target)
default: default:

View file

@ -18,7 +18,7 @@ type Port struct {
port string port string
isSource bool isSource bool
portList []portReal portList []portReal
network C.NetWork ruleExtra *C.RuleExtra
} }
func (p *Port) RuleType() C.RuleType { func (p *Port) RuleType() C.RuleType {
@ -47,8 +47,8 @@ func (p *Port) ShouldResolveIP() bool {
return false return false
} }
func (p *Port) NetWork() C.NetWork { func (p *Port) RuleExtra() *C.RuleExtra {
return p.network return p.ruleExtra
} }
func (p *Port) matchPortReal(portRef string) bool { func (p *Port) matchPortReal(portRef string) bool {
@ -67,7 +67,7 @@ func (p *Port) matchPortReal(portRef string) bool {
return false return false
} }
func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Port, error) { func NewPort(port string, adapter string, isSource bool, ruleExtra *C.RuleExtra) (*Port, error) {
//the port format should be like this: "123/136/137-139" or "[123]/[136-139]" //the port format should be like this: "123/136/137-139" or "[123]/[136-139]"
ports := strings.Split(port, "/") ports := strings.Split(port, "/")
if len(ports) > 28 { if len(ports) > 28 {
@ -118,6 +118,6 @@ func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Po
port: port, port: port,
isSource: isSource, isSource: isSource,
portList: portList, portList: portList,
network: network, ruleExtra: ruleExtra,
}, nil }, nil
} }

View file

@ -16,7 +16,7 @@ var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
type Process struct { type Process struct {
adapter string adapter string
process string process string
network C.NetWork ruleExtra *C.RuleExtra
} }
func (ps *Process) RuleType() C.RuleType { func (ps *Process) RuleType() C.RuleType {
@ -71,14 +71,14 @@ func (ps *Process) ShouldResolveIP() bool {
return false return false
} }
func (ps *Process) NetWork() C.NetWork { func (ps *Process) RuleExtra() *C.RuleExtra {
return ps.network return ps.ruleExtra
} }
func NewProcess(process string, adapter string, network C.NetWork) (*Process, error) { func NewProcess(process string, adapter string, ruleExtra *C.RuleExtra) (*Process, error) {
return &Process{ return &Process{
adapter: adapter, adapter: adapter,
process: process, process: process,
network: network, ruleExtra: ruleExtra,
}, nil }, nil
} }

View file

@ -33,7 +33,7 @@ var (
// default timeout for UDP session // default timeout for UDP session
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
preProcessCacheFinder, _ = R.NewProcess("", "", C.ALLNet) preProcessCacheFinder, _ = R.NewProcess("", "", nil)
tunBroadcastAddr = net.IPv4(198, 18, 255, 255) tunBroadcastAddr = net.IPv4(198, 18, 255, 255)
) )
@ -235,7 +235,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
switch true { switch true {
case rule != nil: case rule != nil:
log.Infoln("[UDP] %s(%s) --> %s:%s match %s(%s) %s using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rule.NetWork().String(), rawPc.Chains().String()) log.Infoln("[UDP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rawPc.Chains().String())
case mode == Global: case mode == Global:
log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct: case mode == Direct:
@ -285,7 +285,7 @@ func handleTCPConn(ctx C.ConnContext) {
switch true { switch true {
case rule != nil: case rule != nil:
log.Infoln("[TCP] %s(%s) --> %s:%s match %s(%s) %s using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rule.NetWork().String(), remoteConn.Chains().String()) log.Infoln("[TCP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String())
case mode == Global: case mode == Global:
log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct: case mode == Direct:
@ -339,12 +339,21 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
continue continue
} }
if rule.NetWork() != C.ALLNet && rule.NetWork() != metadata.NetWork { extra := rule.RuleExtra()
if extra != nil {
if extra.NotMatchNetwork(metadata.NetWork) {
continue continue
} }
if extra.NotMatchSourceIP(metadata.SrcIP) {
continue
}
}
return adapter, rule, nil return adapter, rule, nil
} }
} }
return proxies["DIRECT"], nil, nil //return proxies["DIRECT"], nil, nil
return proxies["REJECT"], nil, nil
} }