Feature: support multiport condition for rule SRC-PORT and DST-PORT

This commit is contained in:
yaling888 2021-07-06 15:07:05 +08:00
parent e2c7b19000
commit 56dff65149
6 changed files with 118 additions and 28 deletions

View file

@ -31,7 +31,7 @@
## Getting Started
Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki).
## Advanced usage for this fork repository
## Advanced usage for this fork branch
### TUN configuration
Support macOS Linux and Windows.
@ -46,25 +46,33 @@ tun:
```
### Rules configuration
- Support rule `GEOSITE`
- Support rule `GEOIP` not match condition
- Support `multiport` condition for rule `SRC-PORT` and `DST-PORT`
- Support not match condition for rule `GEOIP`
- Support `network` condition for all rules
The `GEOSITE` and `GEOIP` databases via https://github.com/Loyalsoldier/v2ray-rules-dat
```yaml
rules:
# network condition for rules
- DOMAIN-SUFFIX,tabao.com,DIRECT,tcp
- DST-PORT,123,DIRECT,udp
- DOMAIN-SUFFIX,bilibili.com,DIRECT,tcp
- DOMAIN-SUFFIX,bilibili.com,REJECT,udp
# multiport condition for rule SRC-PORT and DST-PORT
- DST-PORT,123/136/137-139,DIRECT,udp
# rule GEOSITE
- GEOSITE,category-ads-all,REJECT
- GEOSITE,icloud@cn,DIRECT
- GEOSITE,apple@cn,DIRECT
- GEOSITE,microsoft@cn,DIRECT
- GEOSITE,facebook,PROXY
- GEOSITE,youtube,PROXY
- GEOSITE,geolocation-cn,DIRECT
- GEOSITE,gfw,PROXY
- GEOSITE,greatfire,PROXY
#- GEOSITE,geolocation-!cn,PROXY
- GEOIP,telegram,PROXY,no-resolve
- GEOIP,private,DIRECT,no-resolve
- GEOIP,cn,DIRECT
@ -76,7 +84,7 @@ rules:
### IPTABLES auto-configuration
Only work on Linux OS who support `iptables`, Clash will auto-configuration iptables for tproxy listener when `tproxy-port` value isn't zero.
When `TPROXY` is enabled, the `TUN` must be disabled.
If `TPROXY` is enabled, the `TUN` must be disabled.
```yaml
# Enable the TPROXY listener
tproxy-port: 9898
@ -84,7 +92,7 @@ tproxy-port: 9898
tun:
enable: false
```
Create user give name `clash`, run `$ sudo useradd -M clash` in command line.
Create user given name `clash`
Run Clash by user `clash` as a daemon.

View file

@ -6,6 +6,7 @@ import (
"net"
"net/url"
"os"
"runtime"
"strings"
"github.com/Dreamacro/clash/adapter"
@ -433,6 +434,8 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
rules = append(rules, parsed)
}
runtime.GC()
return rules, nil
}

View file

@ -10,7 +10,7 @@ import (
_ "github.com/Dreamacro/clash/rule/geodata/standard"
)
var geoIPMatcher *router.GeoIPMatcher
var multiGeoIPMatcher *router.MultiGeoIPMatcher
type fallbackIPFilter interface {
Match(net.IP) bool
@ -19,35 +19,49 @@ type fallbackIPFilter interface {
type geoipFilter struct{}
func (gf *geoipFilter) Match(ip net.IP) bool {
if geoIPMatcher == nil {
countryCode := "cn"
if multiGeoIPMatcher == nil {
countryCodeCN := "cn"
countryCodePrivate := "private"
geoLoader, err := geodata.GetGeoDataLoader("standard")
if err != nil {
log.Errorln("[GeoIPFilter] GetGeoDataLoader error: %s", err.Error())
return false
}
records, err := geoLoader.LoadGeoIP(countryCode)
recordsCN, err := geoLoader.LoadGeoIP(countryCodeCN)
if err != nil {
log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error())
return false
}
geoIP := &router.GeoIP{
CountryCode: countryCode,
Cidr: records,
ReverseMatch: false,
recordsPrivate, err := geoLoader.LoadGeoIP(countryCodePrivate)
if err != nil {
log.Errorln("[GeoIPFilter] LoadGeoIP error: %s", err.Error())
return false
}
geoIPMatcher, err = router.NewGeoIPMatcher(geoIP)
geoips := []*router.GeoIP{
{
CountryCode: countryCodeCN,
Cidr: recordsCN,
ReverseMatch: false,
},
{
CountryCode: countryCodePrivate,
Cidr: recordsPrivate,
ReverseMatch: false,
},
}
multiGeoIPMatcher, err = router.NewMultiGeoIPMatcher(geoips)
if err != nil {
log.Errorln("[GeoIPFilter] NewGeoIPMatcher error: %s", err.Error())
log.Errorln("[GeoIPFilter] NewMultiGeoIPMatcher error: %s", err.Error())
return false
}
}
return !geoIPMatcher.Match(ip)
return !multiGeoIPMatcher.ApplyIp(ip)
}
type ipnetFilter struct {
@ -61,6 +75,7 @@ func (inf *ipnetFilter) Match(ip net.IP) bool {
type fallbackDomainFilter interface {
Match(domain string) bool
}
type domainFilter struct {
tree *trie.DomainTrie
}

View file

@ -19,7 +19,7 @@ type TunDevice interface {
}
func SetLinuxAutoRoute() {
log.Infoln("Tun adapter auto setting MacOS route")
log.Infoln("Tun adapter auto setting global route")
addLinuxSystemRoute("1")
addLinuxSystemRoute("2/7")
addLinuxSystemRoute("4/6")
@ -32,7 +32,7 @@ func SetLinuxAutoRoute() {
}
func RemoveLinuxAutoRoute() {
log.Infoln("Tun adapter removing MacOS route")
log.Infoln("Tun adapter removing global route")
delLinuxSystemRoute("1")
delLinuxSystemRoute("2/7")
delLinuxSystemRoute("4/6")
@ -50,7 +50,7 @@ func addLinuxSystemRoute(net string) {
}
cmd := exec.Command("route", "add", "-net", net, "198.18.0.1")
if err := cmd.Run(); err != nil {
log.Errorln("[MacOS auto route] Failed to add system route: %s, cmd: %s", err.Error(), cmd.String())
log.Errorln("[auto route] Failed to add system route: %s, cmd: %s", err.Error(), cmd.String())
}
}
@ -61,6 +61,6 @@ func delLinuxSystemRoute(net string) {
cmd := exec.Command("route", "delete", "-net", net, "198.18.0.1")
_ = cmd.Run()
//if err := cmd.Run(); err != nil {
// log.Errorln("[MacOS auto route]Failed to delete system route: %s, cmd: %s", err.Error(), cmd.String())
// log.Errorln("[auto route]Failed to delete system route: %s, cmd: %s", err.Error(), cmd.String())
//}
}

View file

@ -1,15 +1,23 @@
package rules
import (
"fmt"
"strconv"
"strings"
C "github.com/Dreamacro/clash/constant"
)
type portReal struct {
portStart int
portEnd int
}
type Port struct {
adapter string
port string
isSource bool
portList []portReal
network C.NetWork
}
@ -22,9 +30,9 @@ func (p *Port) RuleType() C.RuleType {
func (p *Port) Match(metadata *C.Metadata) bool {
if p.isSource {
return metadata.SrcPort == p.port
return p.matchPortReal(metadata.SrcPort)
}
return metadata.DstPort == p.port
return p.matchPortReal(metadata.DstPort)
}
func (p *Port) Adapter() string {
@ -43,15 +51,72 @@ func (p *Port) NetWork() C.NetWork {
return p.network
}
func (p *Port) matchPortReal(portRef string) bool {
port, _ := strconv.Atoi(portRef)
var rs bool
for _, pr := range p.portList {
if pr.portEnd == -1 {
rs = port == pr.portStart
} else {
rs = port >= pr.portStart && port <= pr.portEnd
}
if rs {
return true
}
}
return false
}
func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Port, error) {
_, err := strconv.Atoi(port)
if err != nil {
ports := strings.Split(port, "/")
if len(ports) > 28 {
return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error())
}
var portList []portReal
for _, p := range ports {
if p == "" {
continue
}
subPort := strings.Split(strings.Trim(p, "[ ]"), "-")
subPortLen := len(subPort)
if subPortLen > 2 {
return nil, errPayload
}
portStart, err := strconv.Atoi(subPort[0])
if err != nil || portStart < 0 || portStart > 65535 {
return nil, errPayload
}
if subPortLen == 1 {
portList = append(portList, portReal{portStart, -1})
} else if subPortLen == 2 {
portEnd, err1 := strconv.Atoi(subPort[1])
if err1 != nil || portEnd < 0 || portEnd > 65535 {
return nil, errPayload
}
shouldReverse := portStart > portEnd
if shouldReverse {
portList = append(portList, portReal{portEnd, portStart})
} else {
portList = append(portList, portReal{portStart, portEnd})
}
}
}
if len(portList) == 0 {
return nil, errPayload
}
return &Port{
adapter: adapter,
port: port,
isSource: isSource,
portList: portList,
network: network,
}, nil
}

View file

@ -35,8 +35,7 @@ var (
preProcessCacheFinder, _ = R.NewProcess("", "", C.ALLNet)
fakeIpMask = net.IPv4Mask(0, 0, 0xff, 0xff)
fakeIpMaxIp = net.IPv4(0, 0, 255, 255)
tunBroadcastAddr = net.IPv4(198, 18, 255, 255)
)
func init() {
@ -144,7 +143,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
// redir-host should lookup the hosts
metadata.DstIP = node.Data.(net.IP)
}
} else if resolver.IsFakeIP(metadata.DstIP) && !fakeIpMaxIp.Equal(metadata.DstIP.Mask(fakeIpMask)) {
} else if resolver.IsFakeIP(metadata.DstIP) && !tunBroadcastAddr.Equal(metadata.DstIP) {
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
}
}