diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index 22db40f..c067680 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -15,8 +15,6 @@ const ( AppName = "dae" BpfPinRoot = "/sys/fs/bpf" - AddrHdrSize = 24 - TaskCommLen = 16 ) diff --git a/common/utils.go b/common/utils.go index 3dce960..bbb84a4 100644 --- a/common/utils.go +++ b/common/utils.go @@ -20,6 +20,7 @@ import ( "strconv" "strings" "time" + "unsafe" ) var ( @@ -393,3 +394,16 @@ func AddrToDnsType(addr netip.Addr) dnsmessage.Type { return dnsmessage.TypeAAAA } } + +// Htons converts the unsigned short integer hostshort from host byte order to network byte order. +func Htons(i uint16) uint16 { + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, i) + return *(*uint16)(unsafe.Pointer(&b[0])) +} + +// Ntohs converts the unsigned short integer hostshort from host byte order to network byte order. +func Ntohs(i uint16) uint16 { + bytes := *(*[2]byte)(unsafe.Pointer(&i)) + return binary.BigEndian.Uint16(bytes[:]) +} diff --git a/control/control_plane.go b/control/control_plane.go index 66f8427..f6aff95 100644 --- a/control/control_plane.go +++ b/control/control_plane.go @@ -347,7 +347,7 @@ func (c *ControlPlane) finishInitDnsUpstreamResolve(raw common.UrlOrEmpty, dnsUp Ip6: common.Ipv6ByteSliceToUint32Array(ip6[:]), HasIp4: dnsUpstream.Ip4.IsValid(), HasIp6: dnsUpstream.Ip6.IsValid(), - Port: internal.Htons(dnsUpstream.Port), + Port: common.Htons(dnsUpstream.Port), }, ebpf.UpdateAny); err != nil { return err } @@ -485,7 +485,7 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { return err } // Port. - if err := c.core.bpf.ParamMap.Update(consts.BigEndianTproxyPortKey, uint32(internal.Htons(port)), ebpf.UpdateAny); err != nil { + if err := c.core.bpf.ParamMap.Update(consts.BigEndianTproxyPortKey, uint32(common.Htons(port)), ebpf.UpdateAny); err != nil { return err } @@ -535,16 +535,16 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { // WAN. Old method. addrHdr, dataOffset, err := ParseAddrHdr(data) if err != nil { - c.log.Warnf("No AddrPort presented") + c.log.Warnf("No AddrPort presented: %v", err) return } copy(data, data[dataOffset:]) - routingResult = &bpfRoutingResult{ - Mark: addrHdr.Mark, - Outbound: addrHdr.Outbound, - } - src = netip.AddrPortFrom(addrHdr.Dest.Addr(), src.Port()) - realDst = addrHdr.Dest + routingResult = &addrHdr.RoutingResult + __ip := common.Ipv6Uint32ArrayToByteSlice(addrHdr.Ip) + _ip, _ := netip.AddrFromSlice(__ip) + // Comment it because them SHOULD equal. + //src = netip.AddrPortFrom(_ip, src.Port()) + realDst = netip.AddrPortFrom(_ip, addrHdr.Port) } else { realDst = pktDst } diff --git a/control/kern/tproxy.c b/control/kern/tproxy.c index d3394b6..bae3936 100644 --- a/control/kern/tproxy.c +++ b/control/kern/tproxy.c @@ -127,12 +127,18 @@ struct ip_port { __be16 port; }; -struct ip_port_outbound { +struct routing_result { + __u32 mark; + __u8 mac[6]; + __u8 outbound; + __u8 pname[TASK_COMM_LEN]; + __u32 pid; +}; + +struct dst_routing_result { __be32 ip[4]; __be16 port; - __u8 outbound; - __u8 unused; - __u32 mark; + struct routing_result routing_result; }; struct tuples { @@ -150,18 +156,13 @@ struct { // (source ip, source port, tcp) is // enough for identifier. And UDP client // side does not care it (full-cone). - __type(value, struct ip_port_outbound); // Original target. + __type(value, struct dst_routing_result); // Original target. __uint(max_entries, MAX_DST_MAPPING_NUM); /// NOTICE: It MUST be pinned. __uint(pinning, LIBBPF_PIN_BY_NAME); } tcp_dst_map SEC(".maps"); // This map is only for old method (redirect mode in WAN). -struct routing_result { - __u32 mark; - __u8 outbound; -}; - struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __type(key, struct tuples); @@ -1180,8 +1181,8 @@ routing(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4], } static bool __always_inline is_not_to_lan(void *_ori_src) { - struct ip_port_outbound *ori_src = _ori_src; - return ori_src->outbound == IS_WAN; + struct dst_routing_result *ori_src = _ori_src; + return ori_src->routing_result.outbound == IS_WAN; } // SNAT for UDP packet. @@ -1218,7 +1219,7 @@ int tproxy_lan_egress(struct __sk_buff *skb) { return TC_ACT_PIPE; } - struct ip_port_outbound ori_src; + struct dst_routing_result ori_src; if ((ret = decap_after_udp_hdr(skb, ipversion, ihl, ipversion == 4 ? iph.tot_len : 0, &ori_src, sizeof(ori_src), is_not_to_lan, true))) { @@ -1347,6 +1348,10 @@ new_connection: struct routing_result routing_result = {0}; routing_result.outbound = ret; routing_result.mark = ret >> 8; + __builtin_memcpy(routing_result.mac, ethh.h_source, + sizeof(routing_result.mac)); + // No pid pname info in LAN. + // Save routing result. if ((ret = bpf_map_update_elem(&routing_tuples_map, &tuples, &routing_result, BPF_ANY))) { @@ -1597,6 +1602,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) { key_src.port = tcph.source; __u8 outbound; __u32 mark; + struct pid_pname *pid_pname = NULL; if (unlikely(tcp_state_syn)) { // New TCP connection. // bpf_printk("[%X]New Connection", bpf_ntohl(tcph.seq)); @@ -1606,7 +1612,6 @@ int tproxy_wan_egress(struct __sk_buff *skb) { } else { flag[1] = IpVersionType_4; } - struct pid_pname *pid_pname; if (pid_is_control_plane(skb, &pid_pname)) { // From control plane. Direct. return TC_ACT_OK; @@ -1641,14 +1646,14 @@ int tproxy_wan_egress(struct __sk_buff *skb) { } else { // bpf_printk("[%X]Old Connection", bpf_ntohl(tcph.seq)); // The TCP connection exists. - struct ip_port_outbound *dst = + struct dst_routing_result *dst = bpf_map_lookup_elem(&tcp_dst_map, &key_src); if (!dst) { // Do not impact previous connections. return TC_ACT_OK; } - outbound = dst->outbound; - mark = dst->mark; + outbound = dst->routing_result.outbound; + mark = dst->routing_result.mark; } if ((outbound == OUTBOUND_DIRECT || outbound == OUTBOUND_MUST_DIRECT) && @@ -1675,15 +1680,22 @@ int tproxy_wan_egress(struct __sk_buff *skb) { } if (unlikely(tcp_state_syn)) { - struct ip_port_outbound value_dst; - __builtin_memset(&value_dst, 0, sizeof(value_dst)); - __builtin_memcpy(value_dst.ip, &tuples.dip, IPV6_BYTE_LENGTH); - value_dst.port = tcph.dest; - value_dst.outbound = outbound; - value_dst.mark = mark; + struct dst_routing_result routing_info; + __builtin_memset(&routing_info, 0, sizeof(routing_info)); + __builtin_memcpy(routing_info.ip, &tuples.dip, IPV6_BYTE_LENGTH); + routing_info.port = tcph.dest; + routing_info.routing_result.outbound = outbound; + routing_info.routing_result.mark = mark; + __builtin_memcpy(routing_info.routing_result.mac, ethh.h_source, + sizeof(ethh.h_source)); + if (pid_pname) { + __builtin_memcpy(routing_info.routing_result.pname, pid_pname->pname, + TASK_COMM_LEN); + routing_info.routing_result.pid = pid_pname->pid; + } // bpf_printk("UPDATE: %pI6:%u", key_src.ip.u6_addr32, // bpf_ntohs(key_src.port)); - bpf_map_update_elem(&tcp_dst_map, &key_src, &value_dst, BPF_ANY); + bpf_map_update_elem(&tcp_dst_map, &key_src, &routing_info, BPF_ANY); } // Write mac. @@ -1699,11 +1711,6 @@ int tproxy_wan_egress(struct __sk_buff *skb) { }; } else if (l4proto == IPPROTO_UDP) { - // Backup for further use. - struct ip_port_outbound new_hdr; - __builtin_memset(&new_hdr, 0, sizeof(new_hdr)); - __builtin_memcpy(new_hdr.ip, &tuples.dip, IPV6_BYTE_LENGTH); - new_hdr.port = udph.dest; // Routing. It decides if we redirect traffic to control plane. __u32 flag[6] = {L4ProtoType_UDP}; @@ -1732,8 +1739,20 @@ int tproxy_wan_egress(struct __sk_buff *skb) { bpf_printk("shot routing: %d", ret); return TC_ACT_SHOT; } - new_hdr.outbound = ret; - new_hdr.mark = ret >> 8; + // Construct new hdr to encap. + struct dst_routing_result new_hdr; + __builtin_memset(&new_hdr, 0, sizeof(new_hdr)); + __builtin_memcpy(new_hdr.ip, &tuples.dip, IPV6_BYTE_LENGTH); + new_hdr.port = udph.dest; + new_hdr.routing_result.outbound = ret; + new_hdr.routing_result.mark = ret >> 8; + __builtin_memcpy(new_hdr.routing_result.mac, ethh.h_source, + sizeof(ethh.h_source)); + if (pid_pname) { + __builtin_memcpy(new_hdr.routing_result.pname, pid_pname->pname, + TASK_COMM_LEN); + new_hdr.routing_result.pid = pid_pname->pid; + } #if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT) __u32 pid = pid_pname ? pid_pname->pid : 0; bpf_printk("udp(wan): from %pI6:%u [PID %u]", tuples.sip.u6_addr32, @@ -1742,13 +1761,13 @@ int tproxy_wan_egress(struct __sk_buff *skb) { tuples.dip.u6_addr32, bpf_ntohs(tuples.dport)); #endif - if ((new_hdr.outbound == OUTBOUND_DIRECT || - new_hdr.outbound == OUTBOUND_MUST_DIRECT) && - new_hdr.mark == 0 // If mark is not zero, we should re-route it, so we + if ((new_hdr.routing_result.outbound == OUTBOUND_DIRECT || + new_hdr.routing_result.outbound == OUTBOUND_MUST_DIRECT) && + new_hdr.routing_result.mark == 0 // If mark is not zero, we should re-route it, so we // send it to control plane in WAN. ) { return TC_ACT_OK; - } else if (unlikely(new_hdr.outbound == OUTBOUND_BLOCK)) { + } else if (unlikely(new_hdr.routing_result.outbound == OUTBOUND_BLOCK)) { return TC_ACT_SHOT; } @@ -1756,7 +1775,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) { // Check outbound connectivity in specific ipversion and l4proto. struct outbound_connectivity_query q = {0}; - q.outbound = new_hdr.outbound; + q.outbound = new_hdr.routing_result.outbound; q.ipversion = ipversion; q.l4proto = l4proto; __u32 *alive; @@ -1892,7 +1911,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) { // map element using income client ip (that is daddr). __builtin_memcpy(&key_dst.ip, &tuples.dip, IPV6_BYTE_LENGTH); key_dst.port = tcph.dest; - struct ip_port_outbound *original_dst = + struct dst_routing_result *original_dst = bpf_map_lookup_elem(&tcp_dst_map, &key_dst); if (!original_dst) { bpf_printk("[%X]Bad Connection: to: %pI6:%u", bpf_ntohl(tcph.seq), @@ -1917,7 +1936,7 @@ int tproxy_wan_ingress(struct __sk_buff *skb) { /// NOTICE: Actually, we do not need symmetrical headers in client and /// server. We use it for convinience. This behavior may change in the /// future. Outbound here is useless and redundant. - struct ip_port_outbound ori_src; + struct dst_routing_result ori_src; // Get source ip/port from our packet header. diff --git a/control/tcp.go b/control/tcp.go index 50ca6dc..730ff6f 100644 --- a/control/tcp.go +++ b/control/tcp.go @@ -14,7 +14,6 @@ import ( "github.com/v2rayA/dae/common/consts" "github.com/v2rayA/dae/component/outbound/dialer" "github.com/v2rayA/dae/component/sniffing" - internal "github.com/v2rayA/dae/pkg/ebpf_internal" "golang.org/x/sys/unix" "net" "net/netip" @@ -44,24 +43,21 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) { routingResult, err := c.core.RetrieveRoutingResult(src, dst, unix.IPPROTO_TCP) if err != nil { // WAN. Old method. - var value bpfIpPortOutbound + var value bpfDstRoutingResult ip6 := src.Addr().As16() if e := c.core.bpf.TcpDstMap.Lookup(bpfIpPort{ Ip: struct{ U6Addr8 [16]uint8 }{U6Addr8: ip6}, - Port: internal.Htons(src.Port()), + Port: common.Htons(src.Port()), }, &value); e != nil { return fmt.Errorf("failed to retrieve target info %v: %v, %v", src.String(), err, e) } - routingResult = &bpfRoutingResult{ - Mark: value.Mark, - Outbound: value.Outbound, - } + routingResult = &value.RoutingResult 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)) + dst = netip.AddrPortFrom(dstAddr, common.Htons(value.Port)) } var outboundIndex = consts.OutboundIndex(routingResult.Outbound) @@ -101,6 +97,9 @@ func (c *ControlPlane) handleConn(lConn net.Conn) (err error) { "policy": outbound.GetSelectionPolicy(), "dialer": d.Name(), "domain": domain, + "pid": routingResult.Pid, + "pname": ProcessName2String(routingResult.Pname[:]), + "mac": Mac2String(routingResult.Mac[:]), }).Infof("%v <-> %v", RefineSourceToShow(src, dst.Addr(), consts.LanWanFlag_NotApplicable), RefineAddrPortToShow(dst)) } diff --git a/control/udp.go b/control/udp.go index 8f77d01..f040b12 100644 --- a/control/udp.go +++ b/control/udp.go @@ -7,8 +7,10 @@ package control import ( "encoding/binary" + "encoding/gob" "errors" "fmt" + "github.com/mzz2017/softwind/pkg/zeroalloc/buffer" "github.com/mzz2017/softwind/pool" "github.com/sirupsen/logrus" "github.com/v2rayA/dae/common" @@ -22,6 +24,7 @@ import ( "strings" "syscall" "time" + "unsafe" ) const ( @@ -44,52 +47,32 @@ func ChooseNatTimeout(data []byte, sniffDns bool) (dmsg *dnsmessage.Message, tim return nil, DefaultNatTimeout } -type AddrHdr struct { - Dest netip.AddrPort - Outbound uint8 - Mark uint32 -} - -func ParseAddrHdr(data []byte) (hdr *AddrHdr, dataOffset int, err error) { - ipSize := 16 - dataOffset = consts.AddrHdrSize +func ParseAddrHdr(data []byte) (hdr *bpfDstRoutingResult, dataOffset int, err error) { + dataOffset = int(unsafe.Sizeof(bpfDstRoutingResult{})) if len(data) < dataOffset { return nil, 0, fmt.Errorf("data is too short to parse AddrHdr") } - destAddr, _ := netip.AddrFromSlice(data[:ipSize]) - port := binary.BigEndian.Uint16(data[ipSize:]) - outbound := data[ipSize+2] - mark := binary.BigEndian.Uint32(data[ipSize+4:]) - return &AddrHdr{ - Dest: netip.AddrPortFrom(destAddr, port), - Outbound: outbound, - Mark: mark, - }, dataOffset, nil -} - -func (hdr *AddrHdr) ToBytesFromPool() []byte { - ipSize := 16 - buf := pool.GetZero(consts.AddrHdrSize) // byte align to a multiple of 4 - ip := hdr.Dest.Addr().As16() - copy(buf, ip[:]) - binary.BigEndian.PutUint16(buf[ipSize:], hdr.Dest.Port()) - buf[ipSize+2] = hdr.Outbound - binary.BigEndian.PutUint32(buf[ipSize+4:], hdr.Mark) - return buf + _hdr := *(*bpfDstRoutingResult)(unsafe.Pointer(&data[0])) + _hdr.Port = common.Ntohs(_hdr.Port) + return &_hdr, dataOffset, nil } func sendPktWithHdrWithFlag(data []byte, mark uint32, from netip.AddrPort, lConn *net.UDPConn, to netip.AddrPort, lanWanFlag consts.LanWanFlag) error { - hdr := AddrHdr{ - Dest: from, - Mark: mark, - Outbound: uint8(lanWanFlag), // Pass some message to the kernel program. + hdr := bpfDstRoutingResult{ + Ip: common.Ipv6ByteSliceToUint32Array(from.Addr().AsSlice()), + Port: common.Htons(from.Port()), + RoutingResult: bpfRoutingResult{ + Outbound: uint8(lanWanFlag), // Pass some message to the kernel program. + }, } - bHdr := hdr.ToBytesFromPool() - defer pool.Put(bHdr) - buf := pool.Get(len(bHdr) + len(data)) + buf := pool.Get(int(unsafe.Sizeof(hdr)) + len(data)) defer pool.Put(buf) - copy(buf, bHdr) - copy(buf[len(bHdr):], data) + b := buffer.NewBufferFrom(buf) + defer b.Put() + if err := gob.NewEncoder(b).Encode(&hdr); err != nil { + return err + } + copy(buf[int(unsafe.Sizeof(hdr)):], data) //log.Println("from", from, "to", to) _, err := lConn.WriteToUDPAddrPort(buf, to) return err @@ -368,6 +351,9 @@ func (c *ControlPlane) handlePkt(lConn *net.UDPConn, data []byte, src, pktDst, r c.log.WithFields(logrus.Fields{ "to": destToSend.String(), "domain": domain, + "pid": routingResult.Pid, + "pname": ProcessName2String(routingResult.Pname[:]), + "mac": Mac2String(routingResult.Mac[:]), "from": realSrc.String(), "network": networkType.String(), "err": err.Error(), @@ -439,16 +425,25 @@ func (c *ControlPlane) handlePkt(lConn *net.UDPConn, data []byte, src, pktDst, r "dialer": realDialer.Name(), "qname": strings.ToLower(q.Name.String()), "qtype": q.Type, + "pid": routingResult.Pid, + "pname": ProcessName2String(routingResult.Pname[:]), + "mac": Mac2String(routingResult.Mac[:]), }).Infof("%v <-> %v", RefineSourceToShow(realSrc, realDst.Addr(), lanWanFlag), RefineAddrPortToShow(destToSend), ) } else if c.log.IsLevelEnabled(logrus.InfoLevel) { + if isDns && len(dnsMessage.Questions) > 0 { + domain = strings.ToLower(dnsMessage.Questions[0].Name.String()) + } c.log.WithFields(logrus.Fields{ "network": string(l4proto) + string(ipversion), "outbound": outbound.Name, "policy": outbound.GetSelectionPolicy(), "dialer": realDialer.Name(), "domain": domain, + "pid": routingResult.Pid, + "pname": ProcessName2String(routingResult.Pname[:]), + "mac": Mac2String(routingResult.Mac[:]), }).Infof("%v <-> %v", RefineSourceToShow(realSrc, realDst.Addr(), lanWanFlag), RefineAddrPortToShow(destToSend), ) diff --git a/control/utils.go b/control/utils.go index 1c6e643..5755d4e 100644 --- a/control/utils.go +++ b/control/utils.go @@ -8,10 +8,11 @@ package control import ( "bytes" "encoding/binary" + "encoding/hex" "fmt" "github.com/mzz2017/softwind/netproxy" + "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/netip" "os" @@ -24,14 +25,14 @@ func (c *ControlPlaneCore) RetrieveRoutingResult(src, dst netip.AddrPort, l4prot tuples := &bpfTuples{ Sip: struct{ U6Addr8 [16]uint8 }{U6Addr8: srcIp6}, - Sport: internal.Htons(src.Port()), + Sport: common.Htons(src.Port()), Dip: struct{ U6Addr8 [16]uint8 }{U6Addr8: dstIp6}, - Dport: internal.Htons(dst.Port()), + Dport: common.Htons(dst.Port()), L4proto: l4proto, } var routingResult bpfRoutingResult - if err := c.bpf.RoutingTuplesMap.Lookup(tuples, &routingResult); err != nil { + if err := c.bpf.RoutingTuplesMap.LookupAndDelete(tuples, &routingResult); err != nil { return nil, fmt.Errorf("reading map: key [%v, %v, %v]: %w", src.String(), l4proto, dst.String(), err) } return &routingResult, nil @@ -88,3 +89,18 @@ func GetNetwork(network string, mark uint32) string { }.Encode() } } + +func ProcessName2String(pname []uint8) string { + return string(bytes.TrimRight(pname[:], string([]byte{0}))) +} + +func Mac2String(mac []uint8) string { + ori := []byte(hex.EncodeToString(mac)) + // Insert ":". + b := make([]byte, len(ori)/2*3) + for i, j := 0, 0; i < len(ori); i, j = i+2, j+3 { + copy(b[j:j+2], ori[i:i+2]) + b[j+2] = ':' + } + return string(b) +} diff --git a/pkg/ebpf_internal/rawsock.go b/pkg/ebpf_internal/rawsock.go index 43cf43b..4df6753 100644 --- a/pkg/ebpf_internal/rawsock.go +++ b/pkg/ebpf_internal/rawsock.go @@ -3,29 +3,21 @@ package internal import ( - "encoding/binary" + "github.com/v2rayA/dae/common" "syscall" - "unsafe" ) func OpenRawSock(index int) (int, error) { - sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, int(Htons(syscall.ETH_P_ALL))) + sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, int(common.Htons(syscall.ETH_P_ALL))) if err != nil { return 0, err } sll := syscall.SockaddrLinklayer{ Ifindex: index, - Protocol: Htons(syscall.ETH_P_ALL), + Protocol: common.Htons(syscall.ETH_P_ALL), } if err := syscall.Bind(sock, &sll); err != nil { return 0, err } return sock, nil } - -// Htons converts the unsigned short integer hostshort from host byte order to network byte order. -func Htons(i uint16) uint16 { - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, i) - return *(*uint16)(unsafe.Pointer(&b[0])) -}