mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-20 01:00:56 +00:00
feat: add DSCP
rule for Tproxy UDP packets
This commit is contained in:
parent
460cc240b0
commit
1c3132c222
8 changed files with 96 additions and 7 deletions
|
@ -63,3 +63,9 @@ func WithInAddr(addr net.Addr) Addition {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithDSCP(dscp uint8) Addition {
|
||||
return func(metadata *C.Metadata) {
|
||||
metadata.DSCP = dscp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ type Metadata struct {
|
|||
SpecialProxy string `json:"specialProxy"`
|
||||
SpecialRules string `json:"specialRules"`
|
||||
RemoteDst string `json:"remoteDestination"`
|
||||
DSCP uint8 `json:"dscp"`
|
||||
|
||||
RawSrcAddr net.Addr `json:"-"`
|
||||
RawDstAddr net.Addr `json:"-"`
|
||||
|
|
|
@ -14,6 +14,7 @@ const (
|
|||
SrcPort
|
||||
DstPort
|
||||
InPort
|
||||
DSCP
|
||||
InUser
|
||||
InName
|
||||
InType
|
||||
|
@ -73,6 +74,8 @@ func (rt RuleType) String() string {
|
|||
return "RuleSet"
|
||||
case Network:
|
||||
return "Network"
|
||||
case DSCP:
|
||||
return "DSCP"
|
||||
case Uid:
|
||||
return "Uid"
|
||||
case SubRules:
|
||||
|
|
|
@ -34,6 +34,14 @@ func setsockopt(rc syscall.RawConn, addr string) error {
|
|||
if err == nil && isIPv6 {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVTOS, 1)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, syscall.IPV6_RECVTCLASS, 1)
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
|
|
|
@ -74,7 +74,8 @@ func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPLi
|
|||
continue
|
||||
}
|
||||
|
||||
rAddr, err := getOrigDst(oob[:oobn])
|
||||
rAddr, dscp, err := getOrigDstAndDSCP(oob[:oobn])
|
||||
additions = append(additions, inbound.WithDSCP(dscp))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -96,17 +96,23 @@ func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {
|
|||
return syscall.AF_INET6
|
||||
}
|
||||
|
||||
func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
||||
func getOrigDstAndDSCP(oob []byte) (netip.AddrPort, uint8, error) {
|
||||
// oob contains socket control messages which we need to parse.
|
||||
scms, err := unix.ParseSocketControlMessage(oob)
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err)
|
||||
return netip.AddrPort{}, 0, fmt.Errorf("parse control message: %w", err)
|
||||
}
|
||||
|
||||
// retrieve the destination address from the SCM.
|
||||
sa, err := unix.ParseOrigDstAddr(&scms[0])
|
||||
sa, err := unix.ParseOrigDstAddr(&scms[1])
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
|
||||
return netip.AddrPort{}, 0, fmt.Errorf("retrieve destination: %w", err)
|
||||
}
|
||||
|
||||
// retrieve DSCP from the SCM
|
||||
dscp, err := parseDSCP(&scms[0])
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, 0, fmt.Errorf("retrieve DSCP: %w", err)
|
||||
}
|
||||
|
||||
// encode the destination address into a cmsg.
|
||||
|
@ -117,8 +123,23 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
|
|||
case *unix.SockaddrInet6:
|
||||
rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))
|
||||
default:
|
||||
return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v)
|
||||
return netip.AddrPort{}, 0, fmt.Errorf("unsupported address type: %T", v)
|
||||
}
|
||||
|
||||
return rAddr, nil
|
||||
return rAddr, dscp, nil
|
||||
}
|
||||
|
||||
func parseDSCP(m *unix.SocketControlMessage) (uint8, error) {
|
||||
switch {
|
||||
case m.Header.Level == unix.SOL_IP && m.Header.Type == unix.IP_TOS:
|
||||
dscp := uint8(m.Data[0] >> 2)
|
||||
return dscp, nil
|
||||
|
||||
case m.Header.Level == unix.SOL_IPV6 && m.Header.Type == unix.IPV6_TCLASS:
|
||||
dscp := uint8(m.Data[0] >> 2)
|
||||
return dscp, nil
|
||||
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
|
47
rules/common/dscp.go
Normal file
47
rules/common/dscp.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type DSCP struct {
|
||||
*Base
|
||||
dscp uint8
|
||||
payload string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (d *DSCP) RuleType() C.RuleType {
|
||||
return C.DSCP
|
||||
}
|
||||
|
||||
func (d *DSCP) Match(metadata *C.Metadata) (bool, string) {
|
||||
return metadata.DSCP == d.dscp, d.adapter
|
||||
}
|
||||
|
||||
func (d *DSCP) Adapter() string {
|
||||
return d.adapter
|
||||
}
|
||||
|
||||
func (d *DSCP) Payload() string {
|
||||
return d.payload
|
||||
}
|
||||
|
||||
func NewDSCP(dscp string, adapter string) (*DSCP, error) {
|
||||
dscpi, err := strconv.Atoi(dscp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse DSCP rule fail: %w", err)
|
||||
}
|
||||
if dscpi < 0 || dscpi > 63 {
|
||||
return nil, fmt.Errorf("DSCP couldn't be negative or exceed 63")
|
||||
}
|
||||
return &DSCP{
|
||||
Base: &Base{},
|
||||
payload: dscp,
|
||||
dscp: uint8(dscpi),
|
||||
adapter: adapter,
|
||||
}, nil
|
||||
}
|
|
@ -38,6 +38,8 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||
parsed, parseErr = RC.NewPort(payload, target, C.DstPort)
|
||||
case "IN-PORT":
|
||||
parsed, parseErr = RC.NewPort(payload, target, C.InPort)
|
||||
case "DSCP":
|
||||
parsed, parseErr = RC.NewDSCP(payload, target)
|
||||
case "PROCESS-NAME":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, true)
|
||||
case "PROCESS-PATH":
|
||||
|
|
Loading…
Add table
Reference in a new issue