mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-19 16:30:07 +08:00
1. add mac and schedule rule type. 2. fix a issue for subrule so that it could recursion correctly
This commit is contained in:
parent
93de49d20c
commit
f11558b12c
@ -35,6 +35,8 @@ const (
|
|||||||
AND
|
AND
|
||||||
OR
|
OR
|
||||||
NOT
|
NOT
|
||||||
|
SrcMAC
|
||||||
|
Schedule
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuleType int
|
type RuleType int
|
||||||
@ -107,6 +109,10 @@ func (rt RuleType) String() string {
|
|||||||
return "OR"
|
return "OR"
|
||||||
case NOT:
|
case NOT:
|
||||||
return "NOT"
|
return "NOT"
|
||||||
|
case SrcMAC:
|
||||||
|
return "SrcMAC"
|
||||||
|
case Schedule:
|
||||||
|
return "Schedule"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@ -96,6 +96,7 @@ require (
|
|||||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
|
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
|
||||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -163,6 +163,8 @@ github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs
|
|||||||
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
|
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
|||||||
127
rules/common/mac.go
Normal file
127
rules/common/mac.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
"github.com/metacubex/mihomo/log"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
|
)
|
||||||
|
|
||||||
|
var lc = cache.New(5*time.Minute, 10*time.Minute)
|
||||||
|
var arpCommand = "arp"
|
||||||
|
var arpVar = "-a"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
switch os := runtime.GOOS; os {
|
||||||
|
case "linux":
|
||||||
|
arpCommand = "cat"
|
||||||
|
arpVar = "/proc/net/arp"
|
||||||
|
case "windows":
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SrcMAC struct {
|
||||||
|
*Base
|
||||||
|
mac string
|
||||||
|
adapter string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SrcMAC) RuleType() C.RuleType {
|
||||||
|
return C.SrcMAC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SrcMAC) Match(metadata *C.Metadata) (bool, string) {
|
||||||
|
arpTable, err := getARPTable(false)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("can't initial arp table: %s", err)
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
mac, exists := arpTable[metadata.SrcIP.String()]
|
||||||
|
if exists {
|
||||||
|
if mac == d.mac {
|
||||||
|
return true, d.adapter
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arpTable, err := getARPTable(true)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("can't initial arp table: %s", err)
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
mac, exists := arpTable[metadata.SrcIP.String()]
|
||||||
|
if exists {
|
||||||
|
if mac == d.mac {
|
||||||
|
return true, d.adapter
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Errorln("can't find the IP address in arp table: %s", metadata.SrcIP.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, d.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SrcMAC) Adapter() string {
|
||||||
|
return d.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SrcMAC) Payload() string {
|
||||||
|
return d.mac
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMAC(mac string, adapter string) *SrcMAC {
|
||||||
|
punycode, _ := idna.ToASCII(strings.ToLower(mac))
|
||||||
|
return &SrcMAC{
|
||||||
|
Base: &Base{},
|
||||||
|
mac: punycode,
|
||||||
|
adapter: adapter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getARPTable(forceReload bool) (map[string]string, error) {
|
||||||
|
|
||||||
|
item, found := lc.Get("arpTable")
|
||||||
|
if found && !forceReload {
|
||||||
|
arpTable := item.(map[string]string)
|
||||||
|
//log.Infoln("get arpTable from cache")
|
||||||
|
return arpTable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行arp命令
|
||||||
|
cmd := exec.Command(arpCommand, arpVar)
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ipRegex := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3})`)
|
||||||
|
macRegex := regexp.MustCompile(`(?i)(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}`)
|
||||||
|
|
||||||
|
// 解析arp命令的输出
|
||||||
|
arpTable := make(map[string]string)
|
||||||
|
lines := strings.Split(out.String(), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
ip := ipRegex.FindString(line)
|
||||||
|
mac := macRegex.FindString(line)
|
||||||
|
|
||||||
|
if len(ip) > 0 && len(mac) > 0 {
|
||||||
|
punycode, _ := idna.ToASCII(strings.ToLower(mac))
|
||||||
|
arpTable[ip] = punycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lc.Set("arpTable", arpTable, cache.DefaultExpiration)
|
||||||
|
return arpTable, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//var _ C.Rule = (*Mac)(nil)
|
||||||
117
rules/common/schedule.go
Normal file
117
rules/common/schedule.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
"github.com/metacubex/mihomo/log"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
|
)
|
||||||
|
|
||||||
|
// make sure the system has install related zoneinfo for local time zone!
|
||||||
|
func init() {
|
||||||
|
log.Infoln("current system time is %s", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schedule struct {
|
||||||
|
*Base
|
||||||
|
weekDayArr [7]bool
|
||||||
|
startHour int
|
||||||
|
startMinute int
|
||||||
|
endHour int
|
||||||
|
endMinute int
|
||||||
|
schedule string
|
||||||
|
adapter string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Schedule) RuleType() C.RuleType {
|
||||||
|
return C.Schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Schedule) Match(metadata *C.Metadata) (bool, string) {
|
||||||
|
now := time.Now()
|
||||||
|
//log.Infoln("system time is %", now.Format("2006-01-02 15:04:05.000 Mon Jan"))
|
||||||
|
if d.weekDayArr[now.Weekday()] {
|
||||||
|
startTime := time.Date(now.Year(), now.Month(), now.Day(), d.startHour, d.startMinute, 0, 0, now.Location())
|
||||||
|
endTime := time.Date(now.Year(), now.Month(), now.Day(), d.endHour, d.endMinute, 59, 999999999, now.Location())
|
||||||
|
if now.After(startTime) && now.Before(endTime) {
|
||||||
|
//log.Infoln("src ip %s in the time %d:%d~%d:%d. adapter is %s.", metadata.SrcIP.String(), d.startHour, d.startMinute, d.endHour, d.endMinute, d.adapter)
|
||||||
|
return true, d.adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, d.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Schedule) Adapter() string {
|
||||||
|
return d.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Schedule) Payload() string {
|
||||||
|
return d.schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSchedule(schedule string, adapter string) (*Schedule, error) {
|
||||||
|
punycode, _ := idna.ToASCII(strings.ToUpper(schedule))
|
||||||
|
weekDayArr := [7]bool{false, false, false, false, false, false, false}
|
||||||
|
if len(punycode) != 19 {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the rule format is not correct!", punycode)
|
||||||
|
}
|
||||||
|
if punycode[0] == 'S' {
|
||||||
|
weekDayArr[0] = true
|
||||||
|
}
|
||||||
|
if punycode[1] == 'M' {
|
||||||
|
weekDayArr[1] = true
|
||||||
|
}
|
||||||
|
if punycode[2] == 'T' {
|
||||||
|
weekDayArr[2] = true
|
||||||
|
}
|
||||||
|
if punycode[3] == 'W' {
|
||||||
|
weekDayArr[3] = true
|
||||||
|
}
|
||||||
|
if punycode[4] == 'T' {
|
||||||
|
weekDayArr[4] = true
|
||||||
|
}
|
||||||
|
if punycode[5] == 'F' {
|
||||||
|
weekDayArr[5] = true
|
||||||
|
}
|
||||||
|
if punycode[6] == 'S' {
|
||||||
|
weekDayArr[6] = true
|
||||||
|
}
|
||||||
|
startHour, err := strconv.Atoi(punycode[8:10])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the time format is not correct!", punycode)
|
||||||
|
}
|
||||||
|
startMinute, err := strconv.Atoi(punycode[11:13])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the time format is not correct!", punycode)
|
||||||
|
}
|
||||||
|
endHour, err := strconv.Atoi(punycode[14:16])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the time format is not correct!", punycode)
|
||||||
|
}
|
||||||
|
endMinute, err := strconv.Atoi(punycode[17:19])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the time format is not correct!", punycode)
|
||||||
|
}
|
||||||
|
if startHour > endHour {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the end time should not be earlier than start time s!", punycode)
|
||||||
|
}
|
||||||
|
if startHour == endHour && startMinute > endMinute {
|
||||||
|
return nil, fmt.Errorf("could you initial Schedule rule %, the end time should not be earlier than start time s!", punycode)
|
||||||
|
}
|
||||||
|
return &Schedule{
|
||||||
|
Base: &Base{},
|
||||||
|
weekDayArr: weekDayArr,
|
||||||
|
startHour: startHour,
|
||||||
|
startMinute: startMinute,
|
||||||
|
endHour: endHour,
|
||||||
|
endMinute: endMinute,
|
||||||
|
schedule: punycode,
|
||||||
|
adapter: adapter,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//var _ C.Rule = (*Schedule)(nil)
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
list "github.com/bahlo/generic-list-go"
|
||||||
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/rules/common"
|
"github.com/metacubex/mihomo/rules/common"
|
||||||
|
|
||||||
|
|||||||
@ -81,6 +81,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||||||
parsed, parseErr = logic.NewOR(payload, target, ParseRule)
|
parsed, parseErr = logic.NewOR(payload, target, ParseRule)
|
||||||
case "NOT":
|
case "NOT":
|
||||||
parsed, parseErr = logic.NewNOT(payload, target, ParseRule)
|
parsed, parseErr = logic.NewNOT(payload, target, ParseRule)
|
||||||
|
case "SRC-MAC":
|
||||||
|
parsed = RC.NewMAC(payload, target)
|
||||||
|
case "SCHEDULE":
|
||||||
|
parsed, parseErr = RC.NewSchedule(payload, target)
|
||||||
case "RULE-SET":
|
case "RULE-SET":
|
||||||
isSrc, noResolve := RC.ParseParams(params)
|
isSrc, noResolve := RC.ParseParams(params)
|
||||||
parsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve)
|
parsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user