mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 11:42:43 +08:00
Compare commits
4 Commits
de5b1c5dde
...
30ede94ace
Author | SHA1 | Date | |
---|---|---|---|
|
30ede94ace | ||
|
d1a78581aa | ||
|
fa919737c1 | ||
|
c3b77652e6 |
|
@ -9,6 +9,8 @@ const (
|
|||
GEOSITE
|
||||
GEOIP
|
||||
SrcGEOIP
|
||||
SrcMAC
|
||||
DstMAC
|
||||
IPASN
|
||||
SrcIPASN
|
||||
IPCIDR
|
||||
|
@ -54,6 +56,10 @@ func (rt RuleType) String() string {
|
|||
return "GeoIP"
|
||||
case SrcGEOIP:
|
||||
return "SrcGeoIP"
|
||||
case SrcMAC:
|
||||
return "SrcMAC"
|
||||
case DstMAC:
|
||||
return "DstMAC"
|
||||
case IPASN:
|
||||
return "IPASN"
|
||||
case SrcIPASN:
|
||||
|
|
193
rules/common/mac.go
Normal file
193
rules/common/mac.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/metacubex/mihomo/common/cmd"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
var arpTable = make(map[string]string)
|
||||
|
||||
const reloadInterval = 5 * time.Minute
|
||||
|
||||
var startOnce sync.Once
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
||||
type MacAddr struct {
|
||||
*Base
|
||||
mac string
|
||||
adapter string
|
||||
isSourceIP bool
|
||||
}
|
||||
|
||||
func (d *MacAddr) RuleType() C.RuleType {
|
||||
if d.isSourceIP {
|
||||
return C.SrcMAC
|
||||
} else {
|
||||
return C.DstMAC
|
||||
}
|
||||
}
|
||||
|
||||
func getLoadArpTableFunc() func() (string, error) {
|
||||
const ipv6Error = "can't load ipv6 arp table, SRC-MAC/DST-MAC rule can't match src ipv6 address"
|
||||
|
||||
getIpv4Only := func() (string, error) {
|
||||
return cmd.ExecCmd("arp -a")
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
result, err := cmd.ExecCmd("ip --help")
|
||||
if err != nil {
|
||||
result += err.Error()
|
||||
}
|
||||
if strings.Contains(result, "neigh") && strings.Contains(result, "inet6") {
|
||||
return func() (string, error) {
|
||||
return cmd.ExecCmd("ip -s neigh show")
|
||||
}
|
||||
} else {
|
||||
log.Warnln(ipv6Error)
|
||||
const arpPath = "/proc/net/arp"
|
||||
if file, err := os.Open(arpPath); err == nil {
|
||||
defer file.Close()
|
||||
return func() (string, error) {
|
||||
data, err := os.ReadFile(arpPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
} else {
|
||||
return func() (string, error) {
|
||||
return cmd.ExecCmd("arp -a -n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "windows":
|
||||
getIpv6ArpWindows := func() (string, error) {
|
||||
return cmd.ExecCmd("netsh interface ipv6 show neighbors")
|
||||
}
|
||||
result, err := getIpv6ArpWindows()
|
||||
if err != nil || !strings.Contains(result, "----") {
|
||||
log.Warnln(ipv6Error)
|
||||
return getIpv4Only
|
||||
}
|
||||
return func() (string, error) {
|
||||
result, err := cmd.ExecCmd("netsh interface ipv4 show neighbors")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ipv6Result, err := getIpv6ArpWindows()
|
||||
if err == nil {
|
||||
result += ipv6Result
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
default:
|
||||
log.Warnln(ipv6Error)
|
||||
return getIpv4Only
|
||||
}
|
||||
}
|
||||
|
||||
func (d *MacAddr) Match(metadata *C.Metadata) (bool, string) {
|
||||
table := getArpTable()
|
||||
var ip string
|
||||
if d.isSourceIP {
|
||||
ip = metadata.SrcIP.String()
|
||||
} else {
|
||||
ip = metadata.DstIP.String()
|
||||
}
|
||||
mac, exists := table[ip]
|
||||
if exists {
|
||||
if mac == d.mac {
|
||||
return true, d.adapter
|
||||
}
|
||||
} else {
|
||||
log.Infoln("can't find the IP address in arp table: %s", ip)
|
||||
}
|
||||
return false, d.adapter
|
||||
}
|
||||
|
||||
func (d *MacAddr) Adapter() string {
|
||||
return d.adapter
|
||||
}
|
||||
|
||||
func (d *MacAddr) Payload() string {
|
||||
return d.mac
|
||||
}
|
||||
|
||||
var macRegex = regexp.MustCompile(`^([0-9a-f]{2}:){5}[0-9a-f]{2}$`)
|
||||
|
||||
func NewMAC(mac string, adapter string, isSrc bool) (*MacAddr, error) {
|
||||
macAddr := strings.ReplaceAll(strings.ToLower(mac), "-", ":")
|
||||
if !macRegex.MatchString(macAddr) {
|
||||
return nil, errors.New("mac address format error: " + mac)
|
||||
}
|
||||
return &MacAddr{
|
||||
Base: &Base{},
|
||||
mac: macAddr,
|
||||
adapter: adapter,
|
||||
isSourceIP: isSrc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var arpMapRegex = regexp.MustCompile(`((([0-9]{1,3}\.){3}[0-9]{1,3})|(\b[0-9a-fA-F:].*?:.*?))\s.*?\b(([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2})\b`)
|
||||
|
||||
func getArpTable() map[string]string {
|
||||
startOnce.Do(func() {
|
||||
loadArpTable := getLoadArpTableFunc()
|
||||
table, err := reloadArpTable(loadArpTable)
|
||||
if err == nil {
|
||||
arpTable = table
|
||||
} else {
|
||||
log.Errorln("init arp table failed: %s", err)
|
||||
}
|
||||
timer := time.NewTimer(reloadInterval)
|
||||
go func() {
|
||||
for {
|
||||
<-timer.C
|
||||
table, err := reloadArpTable(loadArpTable)
|
||||
if err == nil {
|
||||
arpTable = table
|
||||
} else {
|
||||
log.Errorln("reload arp table failed: %s", err)
|
||||
}
|
||||
timer.Reset(reloadInterval)
|
||||
}
|
||||
}()
|
||||
})
|
||||
return arpTable
|
||||
}
|
||||
|
||||
func reloadArpTable(loadArpFunc func() (string, error)) (map[string]string, error) {
|
||||
result, err := loadArpFunc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newArpTable := make(map[string]string)
|
||||
for _, line := range strings.Split(result, "\n") {
|
||||
matches := arpMapRegex.FindStringSubmatch(line)
|
||||
if matches == nil || len(matches) <= 0 {
|
||||
continue
|
||||
}
|
||||
ip := matches[1]
|
||||
mac := strings.ToLower(matches[5])
|
||||
if strings.Contains(mac, "-") {
|
||||
mac = strings.ReplaceAll(mac, "-", ":")
|
||||
}
|
||||
newArpTable[ip] = mac
|
||||
}
|
||||
return newArpTable, nil
|
||||
}
|
|
@ -26,6 +26,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||
parsed, parseErr = RC.NewGEOIP(payload, target, isSrc, noResolve)
|
||||
case "SRC-GEOIP":
|
||||
parsed, parseErr = RC.NewGEOIP(payload, target, true, true)
|
||||
case "SRC-MAC":
|
||||
parsed, parseErr = RC.NewMAC(payload, target, true)
|
||||
case "DST-MAC":
|
||||
parsed, parseErr = RC.NewMAC(payload, target, false)
|
||||
case "IP-ASN":
|
||||
isSrc, noResolve := RC.ParseParams(params)
|
||||
parsed, parseErr = RC.NewIPASN(payload, target, isSrc, noResolve)
|
||||
|
|
Loading…
Reference in New Issue
Block a user