feat: add DSCP rule for Tproxy UDP packets (#996)

* feat: add `DSCP` rule for Tproxy UDP packets

* fix: fix compatibility issue with non_linux platform

* chore: remove redundant lines for DSCP
This commit is contained in:
pretze 2024-01-20 10:19:42 +08:00 committed by GitHub
parent 90ea6ab278
commit 25d6ad220d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 102 additions and 1 deletions

View file

@ -63,3 +63,9 @@ func WithInAddr(addr net.Addr) Addition {
}
}
}
func WithDSCP(dscp uint8) Addition {
return func(metadata *C.Metadata) {
metadata.DSCP = dscp
}
}

View file

@ -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:"-"`

View file

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

View file

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

View file

@ -79,6 +79,9 @@ func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPLi
continue
}
dscp, _ := getDSCP(oob[:oobn])
additions = append(additions, inbound.WithDSCP(dscp))
if rAddr.Addr().Is4() {
// try to unmap 4in6 address
lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())

View file

@ -104,7 +104,7 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
}
// 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)
}
@ -122,3 +122,30 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
return rAddr, nil
}
func getDSCP (oob []byte) (uint8, error) {
scms, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return 0, fmt.Errorf("parse control message: %w", err)
}
dscp, err := parseDSCP(&scms[0])
if err != nil {
return 0, fmt.Errorf("retrieve DSCP: %w", err)
}
return 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
}
}

View file

@ -12,6 +12,10 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
}
func getDSCP(oob []byte) (uint8, error) {
return 0, errors.New("UDP redir not supported on current platform")
}
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
return nil, errors.New("UDP redir not supported on current platform")
}

47
rules/common/dscp.go Normal file
View 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
}

View file

@ -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":