feat: add more trace info (pid, pname, mac)

This commit is contained in:
mzz2017
2023-02-21 16:10:44 +08:00
parent 474342c8f0
commit 384b4131a3
8 changed files with 143 additions and 110 deletions

View File

@ -15,8 +15,6 @@ const (
AppName = "dae"
BpfPinRoot = "/sys/fs/bpf"
AddrHdrSize = 24
TaskCommLen = 16
)

View File

@ -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[:])
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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