feat: add DSCP rule for Tproxy UDP packets

This commit is contained in:
oracl 2024-01-18 19:42:26 +08:00
parent 460cc240b0
commit 1c3132c222
8 changed files with 96 additions and 7 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

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

View file

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