Compare commits

...

5 Commits

Author SHA1 Message Date
mlkt
de5b1c5dde
Merge d1a78581aa into de19f927e8 2024-11-14 22:18:57 +08:00
Chenx Dust
de19f927e8 chore: restful api display smux and mptcp
Some checks failed
Trigger CMFA Update / trigger-CMFA-update (push) Has been cancelled
2024-11-14 10:08:02 +08:00
mlkt
d1a78581aa 支持DST-MAC 2024-08-17 05:30:35 +08:00
mlkt
fa919737c1 Merge remote-tracking branch 'remotes/origin/Alpha' into Alpha 2024-08-07 19:53:35 +08:00
mlkt
c3b77652e6 feat: add SRC-MAC rule 2024-07-25 04:35:44 +08:00
8 changed files with 235 additions and 0 deletions

View File

@ -165,6 +165,8 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
mapping["udp"] = p.SupportUDP()
mapping["xudp"] = p.SupportXUDP()
mapping["tfo"] = p.SupportTFO()
mapping["mptcp"] = p.SupportMPTCP()
mapping["smux"] = p.SupportSMUX()
return json.Marshal(mapping)
}

View File

@ -95,6 +95,16 @@ func (b *Base) SupportTFO() bool {
return b.tfo
}
// SupportMPTCP implements C.ProxyAdapter
func (b *Base) SupportMPTCP() bool {
return b.mpTcp
}
// SupportSMUX implements C.ProxyAdapter
func (b *Base) SupportSMUX() bool {
return false
}
// IsL3Protocol implements C.ProxyAdapter
func (b *Base) IsL3Protocol(metadata *C.Metadata) bool {
return false

View File

@ -97,6 +97,10 @@ func (s *SingMux) SupportUOT() bool {
return true
}
func (s *SingMux) SupportSMUX() bool {
return true
}
func closeSingMux(s *SingMux) {
_ = s.client.Close()
}

View File

@ -625,6 +625,20 @@ func (r *refProxyAdapter) SupportTFO() bool {
return false
}
func (r *refProxyAdapter) SupportMPTCP() bool {
if r.proxyAdapter != nil {
return r.proxyAdapter.SupportMPTCP()
}
return false
}
func (r *refProxyAdapter) SupportSMUX() bool {
if r.proxyAdapter != nil {
return r.proxyAdapter.SupportSMUX()
}
return false
}
func (r *refProxyAdapter) MarshalJSON() ([]byte, error) {
if r.proxyAdapter != nil {
return r.proxyAdapter.MarshalJSON()

View File

@ -106,6 +106,8 @@ type ProxyAdapter interface {
SupportUDP() bool
SupportXUDP() bool
SupportTFO() bool
SupportMPTCP() bool
SupportSMUX() bool
MarshalJSON() ([]byte, error)
// Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead.

View File

@ -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
View 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
}

View File

@ -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)