From 45701f8559fcfcdd91621e26308130ceb74bc814 Mon Sep 17 00:00:00 2001 From: mzz2017 <2017@duck.com> Date: Tue, 7 Feb 2023 21:11:12 +0800 Subject: [PATCH] fix: patch old wan method --- common/consts/ebpf.go | 3 +- component/control/control_plane.go | 52 ++++++++++++++++--------- component/control/control_plane_core.go | 11 ++++-- component/control/kern/tproxy.c | 7 +++- component/control/objects.go | 2 +- component/control/tcp.go | 24 ++++++++++-- component/control/tproxy_utils.go | 12 +++--- component/control/udp.go | 7 +--- component/outbound/dialer/sockopt.go | 7 ++++ insert.sh | 12 +++--- 10 files changed, 90 insertions(+), 47 deletions(-) diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index 3b0a51d..4307305 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -112,5 +112,6 @@ var ( ) const ( - TproxyMark uint32 = 0x80000000 + TproxyMark uint32 = 0x80000000 + LoopbackIfIndex = 1 ) diff --git a/component/control/control_plane.go b/component/control/control_plane.go index 4ef3246..c71a1c2 100644 --- a/component/control/control_plane.go +++ b/component/control/control_plane.go @@ -7,7 +7,6 @@ package control import ( "context" - "encoding/hex" "errors" "fmt" "github.com/cilium/ebpf" @@ -38,6 +37,7 @@ import ( type ControlPlane struct { *ControlPlaneCore deferFuncs []func() error + listenIp string // TODO: add mutex? outbounds []*outbound.DialerGroup @@ -193,16 +193,15 @@ retryLoadBpf: _ = core.Close() } }() - // Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection. for _, ifname := range lanInterface { - if err = core.BindLan(ifname); err != nil { - return nil, fmt.Errorf("BindLan: %v: %w", ifname, err) + if err = core.bindLan(ifname); err != nil { + return nil, fmt.Errorf("bindLan: %v: %w", ifname, err) } } for _, ifname := range wanInterface { - if err = core.BindWan(ifname); err != nil { - return nil, fmt.Errorf("BindWan: %v: %w", ifname, err) + if err = core.bindWan(ifname); err != nil { + return nil, fmt.Errorf("bindWan: %v: %w", ifname, err) } } @@ -312,9 +311,14 @@ retryLoadBpf: } } + listenIp := "[::1]" + if len(wanInterface) > 0 { + listenIp = "0.0.0.0" + } return &ControlPlane{ ControlPlaneCore: core, deferFuncs: nil, + listenIp: listenIp, outbounds: outbounds, outboundName2Id: outboundName2Id, SimulatedLpmTries: builder.SimulatedLpmTries, @@ -333,12 +337,13 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { return dialer.TproxyControl(c) }, } - tcpListener, err := listenConfig.Listen(context.TODO(), "tcp", "[::1]:"+strconv.Itoa(int(port))) + listenAddr := net.JoinHostPort(c.listenIp, strconv.Itoa(int(port))) + tcpListener, err := listenConfig.Listen(context.TODO(), "tcp", listenAddr) if err != nil { return fmt.Errorf("listenTCP: %w", err) } defer tcpListener.Close() - packetConn, err := listenConfig.ListenPacket(context.TODO(), "udp", "[::1]:"+strconv.Itoa(int(port))) + packetConn, err := listenConfig.ListenPacket(context.TODO(), "udp", listenAddr) if err != nil { return fmt.Errorf("listenUDP: %w", err) } @@ -408,21 +413,30 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { break } dst := RetrieveOriginalDest(oob[:oobn]) - if !dst.IsValid() { - c.log.WithFields(logrus.Fields{ - "source": src.String(), - "oob": hex.EncodeToString(oob[:oobn]), - }).Warnf("Failed to retrieve original dest") - continue + var newBuf []byte + outboundIndex, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_UDP) + if err != nil { + // WAN. Old method. + addrHdr, dataOffset, err := ParseAddrHdr(buf[:n]) + if err != nil { + c.log.Warnf("No AddrPort presented") + continue + } + newBuf = pool.Get(n - dataOffset) + copy(newBuf, buf[dataOffset:n]) + outboundIndex = consts.OutboundIndex(addrHdr.Outbound) + src = netip.AddrPortFrom(dst.Addr(), src.Port()) + dst = addrHdr.Dest + } else { + newBuf = pool.Get(n) + copy(newBuf, buf[:n]) } - newBuf := pool.Get(n) - copy(newBuf, buf[:n]) - go func(data []byte, src, dst netip.AddrPort) { - if e := c.handlePkt(newBuf, src, dst); e != nil { + go func(data []byte, src, dst netip.AddrPort, outboundIndex consts.OutboundIndex) { + if e := c.handlePkt(newBuf, src, dst, outboundIndex); e != nil { c.log.Warnln("handlePkt:", e) } pool.Put(newBuf) - }(newBuf, src, dst) + }(newBuf, src, dst, outboundIndex) } }() <-ctx.Done() diff --git a/component/control/control_plane_core.go b/component/control/control_plane_core.go index a3e2586..25fa940 100644 --- a/component/control/control_plane_core.go +++ b/component/control/control_plane_core.go @@ -111,7 +111,7 @@ func getifParamsFromLink(link netlink.Link) (ifParams bpfIfParams, err error) { return ifParams, nil } -func (c *ControlPlaneCore) BindLan(ifname string) error { +func (c *ControlPlaneCore) bindLan(ifname string) error { c.log.Infof("Bind to LAN: %v", ifname) link, err := netlink.LinkByName(ifname) if err != nil { @@ -123,7 +123,7 @@ func (c *ControlPlaneCore) BindLan(ifname string) error { ip rule add fwmark 0x80000000/0x80000000 table 2023 ip route add local default dev lo table 2023 ip -6 rule add fwmark 0x80000000/0x80000000 table 2023 - ip -6 route add local ::/0 dev lo table 2023 + ip -6 route add local default dev lo table 2023 `).CombinedOutput(); err != nil { return fmt.Errorf("%w: %v", err, string(bytes.TrimSpace(output))) } @@ -132,7 +132,7 @@ func (c *ControlPlaneCore) BindLan(ifname string) error { ip rule del fwmark 0x80000000/0x80000000 table 2023 ip route del local default dev lo table 2023 ip -6 rule del fwmark 0x80000000/0x80000000 table 2023 - ip -6 route del local ::/0 dev lo table 2023 + ip -6 route del local default dev lo table 2023 `).Run() }) /// Insert an elem into IfindexParamsMap. @@ -208,12 +208,15 @@ func (c *ControlPlaneCore) BindLan(ifname string) error { return nil } -func (c *ControlPlaneCore) BindWan(ifname string) error { +func (c *ControlPlaneCore) bindWan(ifname string) error { c.log.Infof("Bind to WAN: %v", ifname) link, err := netlink.LinkByName(ifname) if err != nil { return err } + if link.Attrs().Index == consts.LoopbackIfIndex { + return fmt.Errorf("cannot bind to loopback interface") + } /// Insert an elem into IfindexParamsMap. ifParams, err := getifParamsFromLink(link) if err != nil { diff --git a/component/control/kern/tproxy.c b/component/control/kern/tproxy.c index 279f3c8..82b903a 100644 --- a/component/control/kern/tproxy.c +++ b/component/control/kern/tproxy.c @@ -18,6 +18,7 @@ // #define __DEBUG_ROUTING // #define __PRINT_ROUTING_RESULT +// #define __PRINT_SETUP_PROCESS_CONNNECTION // #define __REMOVE_BPF_PRINTK #ifdef __REMOVE_BPF_PRINTK @@ -130,6 +131,8 @@ struct { __type(key, struct tuples); __type(value, __u32); // outbound __uint(max_entries, MAX_DST_MAPPING_NUM); + /// NOTICE: It MUST be pinned. + __uint(pinning, LIBBPF_PIN_BY_NAME); } routing_tuples_map SEC(".maps"); // Params: @@ -1197,6 +1200,7 @@ int tproxy_lan_ingress(struct __sk_buff *skb) { ip -6 rule del fwmark 0x80000000/0x80000000 table 2023 ip -6 route del local ::/0 dev lo table 2023 */ + // Socket lookup and assign skb to existing socket connection. struct bpf_sock_tuple tuple = {0}; __u32 tuple_size; struct bpf_sock *sk; @@ -1204,7 +1208,6 @@ int tproxy_lan_ingress(struct __sk_buff *skb) { __u32 flag[6] = {0}; void *l4hdr; - // Socket lookup and assign skb to existing socket connection. if (ipversion == 4) { tuple.ipv4.daddr = tuples.dst.ip[3]; tuple.ipv4.saddr = tuples.src.ip[3]; @@ -1963,7 +1966,9 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) { return ret; } +#ifdef __PRINT_SETUP_PROCESS_CONNNECTION bpf_printk("setup_mapping: %llu -> %s (%d)", cookie, val.pname, val.pid); +#endif return 0; } diff --git a/component/control/objects.go b/component/control/objects.go index f222dbd..fabe0b1 100644 --- a/component/control/objects.go +++ b/component/control/objects.go @@ -41,7 +41,7 @@ func generate(output string) error { Ebpf: structField.Tag.Get("ebpf"), }) default: - return fmt.Errorf("unexpected prefix, should be TproxyWan or TproxyLan: %v", structField.Name) + return fmt.Errorf("unexpected program name which should begin with TproxyWan or TproxyLan, but get: %v", structField.Name) } } diff --git a/component/control/tcp.go b/component/control/tcp.go index 5dc9a88..bde7d2d 100644 --- a/component/control/tcp.go +++ b/component/control/tcp.go @@ -9,9 +9,12 @@ import ( "fmt" "github.com/mzz2017/softwind/pkg/zeroalloc/io" "github.com/sirupsen/logrus" + "github.com/v2rayA/dae/common" "github.com/v2rayA/dae/common/consts" + internal "github.com/v2rayA/dae/pkg/ebpf_internal" "golang.org/x/sys/unix" "net" + "net/netip" "strings" "time" ) @@ -20,12 +23,27 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) { defer lConn.Close() src := lConn.RemoteAddr().(*net.TCPAddr).AddrPort() dst := lConn.LocalAddr().(*net.TCPAddr).AddrPort() - outboundIndex, _, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_TCP) + outboundIndex, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_TCP) if err != nil { - return fmt.Errorf("RetrieveOutboundIndex: %w", err) + // WAN. Old method. + var value bpfIpPortOutbound + ip6 := src.Addr().As16() + if e := c.bpf.TcpDstMap.Lookup(bpfIpPort{ + Ip: common.Ipv6ByteSliceToUint32Array(ip6[:]), + Port: internal.Htons(src.Port()), + }, &value); e != nil { + return fmt.Errorf("failed to retrieve target info %v: %v, %v", src.String(), err, e) + } + outboundIndex = consts.OutboundIndex(value.Outbound) + + dstAddr, ok := netip.AddrFromSlice(common.Ipv6Uint32ArrayToByteSlice(value.Ip)) + if !ok { + return fmt.Errorf("failed to parse dest ip: %v", value.Ip) + } + dst = netip.AddrPortFrom(dstAddr, internal.Htons(value.Port)) } - switch consts.OutboundIndex(outboundIndex) { + switch outboundIndex { case consts.OutboundDirect: case consts.OutboundControlPlaneDirect: outboundIndex = consts.OutboundDirect diff --git a/component/control/tproxy_utils.go b/component/control/tproxy_utils.go index 2aafa41..eccd913 100644 --- a/component/control/tproxy_utils.go +++ b/component/control/tproxy_utils.go @@ -16,11 +16,11 @@ import ( "syscall" ) -func (c *ControlPlaneCore) RetrieveOutboundIndex(src, dst netip.AddrPort, l4proto uint8) (outboundIndex consts.OutboundIndex, tuples *bpfTuples, err error) { +func (c *ControlPlaneCore) RetrieveOutboundIndex(src, dst netip.AddrPort, l4proto uint8) (outboundIndex consts.OutboundIndex, err error) { srcIp6 := src.Addr().As16() dstIp6 := dst.Addr().As16() - tuples = &bpfTuples{ + tuples := &bpfTuples{ Src: bpfIpPort{ Ip: common.Ipv6ByteSliceToUint32Array(srcIp6[:]), Port: internal.Htons(src.Port()), @@ -34,12 +34,12 @@ func (c *ControlPlaneCore) RetrieveOutboundIndex(src, dst netip.AddrPort, l4prot var _outboundIndex uint32 if err := c.bpf.RoutingTuplesMap.Lookup(tuples, &_outboundIndex); err != nil { - return 0, nil, fmt.Errorf("reading map: key [%v, %v, %v]: %w", src.String(), l4proto, dst.String(), err) + return 0, fmt.Errorf("reading map: key [%v, %v, %v]: %w", src.String(), l4proto, dst.String(), err) } if _outboundIndex > uint32(consts.OutboundLogicalMax) { - return 0, nil, fmt.Errorf("bad outbound index") + return 0, fmt.Errorf("bad outbound index") } - return consts.OutboundIndex(_outboundIndex), tuples, nil + return consts.OutboundIndex(_outboundIndex), nil } func RetrieveOriginalDest(oob []byte) netip.AddrPort { @@ -55,7 +55,7 @@ func RetrieveOriginalDest(oob []byte) netip.AddrPort { } else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == unix.IPV6_RECVORIGDSTADDR { ip := msg.Data[8:24] port := binary.BigEndian.Uint16(msg.Data[2:4]) - return netip.AddrPortFrom(netip.AddrFrom4(*(*[4]byte)(ip)), port) + return netip.AddrPortFrom(netip.AddrFrom16(*(*[16]byte)(ip)), port) } } return netip.AddrPort{} diff --git a/component/control/udp.go b/component/control/udp.go index c9abff5..c8e89f3 100644 --- a/component/control/udp.go +++ b/component/control/udp.go @@ -14,7 +14,6 @@ import ( "github.com/v2rayA/dae/common/consts" "github.com/v2rayA/dae/component/outbound/dialer" "golang.org/x/net/dns/dnsmessage" - "golang.org/x/sys/unix" "net" "net/netip" "strings" @@ -123,11 +122,7 @@ func (c *ControlPlane) RelayToUDP(to netip.AddrPort, isDNS bool, dummyFrom *neti } } -func (c *ControlPlane) handlePkt(data []byte, src, dst netip.AddrPort) (err error) { - outboundIndex, _, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_UDP) - if err != nil { - return fmt.Errorf("RetrieveOutboundIndex: %w", err) - } +func (c *ControlPlane) handlePkt(data []byte, src, dst netip.AddrPort, outboundIndex consts.OutboundIndex) (err error) { switch outboundIndex { case consts.OutboundDirect: case consts.OutboundControlPlaneDirect: diff --git a/component/outbound/dialer/sockopt.go b/component/outbound/dialer/sockopt.go index 860b374..3f9d3b6 100644 --- a/component/outbound/dialer/sockopt.go +++ b/component/outbound/dialer/sockopt.go @@ -110,6 +110,13 @@ func bindAddr(fd uintptr, addrPort netip.AddrPort) error { a6 := &syscall.SockaddrInet6{ Port: int(addrPort.Port()), } + zone := addrPort.Addr().Zone() + if zone != "" { + //if link, e := netlink.LinkByName(zone); e == nil { + // a6.ZoneId = uint32(link.Attrs().Index) + //} + return fmt.Errorf("unsupported ipv6 zone") + } a6.Addr = addr.As16() sockAddr = a6 default: diff --git a/insert.sh b/insert.sh index b2d4fda..d02fd44 100755 --- a/insert.sh +++ b/insert.sh @@ -3,7 +3,7 @@ lan=docker0 wan=wlp5s0 sudo tc qdisc add dev $lan clsact > /dev/null 2>&1 -# sudo tc qdisc add dev $wan clsact > /dev/null 2>&1 +sudo tc qdisc add dev $wan clsact > /dev/null 2>&1 set -ex @@ -13,11 +13,11 @@ sudo rm -rf /sys/fs/bpf/tc/globals/* clang -O2 -g -Wall -Werror -c component/control/kern/tproxy.c -target bpf -D__TARGET_ARCH_x86 -o foo.o sudo tc filter del dev $lan ingress sudo tc filter del dev $lan egress -# sudo tc filter del dev $wan ingress -# sudo tc filter del dev $wan egress +sudo tc filter del dev $wan ingress +sudo tc filter del dev $wan egress + sudo tc filter add dev $lan ingress bpf direct-action obj foo.o sec tc/ingress -# sudo tc filter add dev $lan egress bpf direct-action obj foo.o sec tc/egress -# sudo tc filter add dev $wan ingress bpf direct-action obj foo.o sec tc/wan_ingress -# sudo tc filter add dev $wan egress bpf direct-action obj foo.o sec tc/wan_egress +sudo tc filter add dev $wan ingress bpf direct-action obj foo.o sec tc/wan_ingress +sudo tc filter add dev $wan egress bpf direct-action obj foo.o sec tc/wan_egress exit 0 \ No newline at end of file