Fix ebpf source port, add event validation and simpify tcp monitoring

This commit is contained in:
Vladimir Stoilov
2023-06-14 17:35:09 +03:00
parent f754555979
commit cecce3ffcb
6 changed files with 73 additions and 102 deletions

View File

@@ -21,7 +21,8 @@ type bpfEvent struct {
Pid uint32 Pid uint32
IpVersion uint8 IpVersion uint8
Protocol uint8 Protocol uint8
_ [2]byte Direction uint8
_ [1]byte
} }
// loadBpf returns the embedded CollectionSpec for bpf. // loadBpf returns the embedded CollectionSpec for bpf.
@@ -65,8 +66,7 @@ type bpfSpecs struct {
// //
// It can be passed ebpf.CollectionSpec.Assign. // It can be passed ebpf.CollectionSpec.Assign.
type bpfProgramSpecs struct { type bpfProgramSpecs struct {
TcpV4Connect *ebpf.ProgramSpec `ebpf:"tcp_v4_connect"` TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
TcpV6Connect *ebpf.ProgramSpec `ebpf:"tcp_v6_connect"`
UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"` UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"`
UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"` UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"`
} }
@@ -110,16 +110,14 @@ func (m *bpfMaps) Close() error {
// //
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
type bpfPrograms struct { type bpfPrograms struct {
TcpV4Connect *ebpf.Program `ebpf:"tcp_v4_connect"` TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
TcpV6Connect *ebpf.Program `ebpf:"tcp_v6_connect"`
UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"` UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"`
UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"` UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"`
} }
func (p *bpfPrograms) Close() error { func (p *bpfPrograms) Close() error {
return _BpfClose( return _BpfClose(
p.TcpV4Connect, p.TcpConnect,
p.TcpV6Connect,
p.UdpV4Connect, p.UdpV4Connect,
p.UdpV6Connect, p.UdpV6Connect,
) )

View File

@@ -21,7 +21,8 @@ type bpfEvent struct {
Pid uint32 Pid uint32
IpVersion uint8 IpVersion uint8
Protocol uint8 Protocol uint8
_ [2]byte Direction uint8
_ [1]byte
} }
// loadBpf returns the embedded CollectionSpec for bpf. // loadBpf returns the embedded CollectionSpec for bpf.
@@ -65,8 +66,7 @@ type bpfSpecs struct {
// //
// It can be passed ebpf.CollectionSpec.Assign. // It can be passed ebpf.CollectionSpec.Assign.
type bpfProgramSpecs struct { type bpfProgramSpecs struct {
TcpV4Connect *ebpf.ProgramSpec `ebpf:"tcp_v4_connect"` TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
TcpV6Connect *ebpf.ProgramSpec `ebpf:"tcp_v6_connect"`
UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"` UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"`
UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"` UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"`
} }
@@ -110,16 +110,14 @@ func (m *bpfMaps) Close() error {
// //
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
type bpfPrograms struct { type bpfPrograms struct {
TcpV4Connect *ebpf.Program `ebpf:"tcp_v4_connect"` TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
TcpV6Connect *ebpf.Program `ebpf:"tcp_v6_connect"`
UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"` UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"`
UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"` UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"`
} }
func (p *bpfPrograms) Close() error { func (p *bpfPrograms) Close() error {
return _BpfClose( return _BpfClose(
p.TcpV4Connect, p.TcpConnect,
p.TcpV6Connect,
p.UdpV4Connect, p.UdpV4Connect,
p.UdpV6Connect, p.UdpV6Connect,
) )

View File

@@ -11,6 +11,9 @@
#define UDP 17 #define UDP 17
#define UDPLite 136 #define UDPLite 136
#define OUTBOUND 0
#define INBOUND 1
char __license[] SEC("license") = "GPL"; char __license[] SEC("license") = "GPL";
// Ring buffer for all connection events // Ring buffer for all connection events
@@ -28,24 +31,14 @@ struct Event {
u32 pid; u32 pid;
u8 ipVersion; u8 ipVersion;
u8 protocol; u8 protocol;
u8 direction;
}; };
struct Event *unused __attribute__((unused)); struct Event *unused __attribute__((unused));
// Fexit of tcp_v4_connect will be executed when equivalent kernel function returns. // Fentry of tcp_connect will be executed when equivalent kernel function is called.
// In the kernel function all IPs and ports are set and then tcp_connect is called. tcp_v4_connect -> tcp_connect -> [this-function] // In the kernel all IP address and ports should be set before tcp_connect is called. [this-function] -> tcp_connect
SEC("fexit/tcp_v4_connect") SEC("fentry/tcp_connect")
int BPF_PROG(tcp_v4_connect, struct sock *sk) { int BPF_PROG(tcp_connect, struct sock *sk) {
// Ignore everything else then IPv4
if (sk->__sk_common.skc_family != AF_INET) {
return 0;
}
// Make sure it's tcp sock
struct tcp_sock *ts = bpf_skc_to_tcp_sock(sk);
if (!ts) {
return 0;
}
// Alloc space for the event // Alloc space for the event
struct Event *tcp_info; struct Event *tcp_info;
tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0); tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0);
@@ -56,67 +49,32 @@ int BPF_PROG(tcp_v4_connect, struct sock *sk) {
// Read PID // Read PID
tcp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid()); tcp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Set src and dist ports
tcp_info->sport = sk->__sk_common.skc_num;
tcp_info->dport = sk->__sk_common.skc_dport;
// Set src and dist IPs
tcp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr);
tcp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr);
// Set IP version
tcp_info->ipVersion = 4;
// Set protocol // Set protocol
tcp_info->protocol = TCP; tcp_info->protocol = TCP;
// Send event // Set direction
bpf_ringbuf_submit(tcp_info, 0); tcp_info->direction = OUTBOUND;
return 0;
};
// Fexit(function exit) of tcp_v6_connect will be executed when equivalent kernel function returns.
// In the kernel function all IPs and ports are set and then tcp_connect is called. tcp_v6_connect -> tcp_connect -> [this-function]
SEC("fexit/tcp_v6_connect")
int BPF_PROG(tcp_v6_connect, struct sock *sk) {
// Ignore everything else then IPv6
if (sk->__sk_common.skc_family != AF_INET6) {
return 0;
}
// Make sure its a tcp6 sock
struct tcp6_sock *ts = bpf_skc_to_tcp6_sock(sk);
if (!ts) {
return 0;
}
// Alloc space for the event
struct Event *tcp_info;
tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0);
if (!tcp_info) {
return 0;
}
// Read PID
tcp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Set src and dist ports // Set src and dist ports
tcp_info->sport = sk->__sk_common.skc_num; tcp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
tcp_info->dport = sk->__sk_common.skc_dport; tcp_info->dport = sk->__sk_common.skc_dport;
// Set src and dist IPs // Set src and dist IPs
for(int i = 0; i < 4; i++) { if (sk->__sk_common.skc_family == AF_INET) {
tcp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]); tcp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr);
tcp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr);
// Set IP version
tcp_info->ipVersion = 4;
} else if (sk->__sk_common.skc_family == AF_INET6) {
for(int i = 0; i < 4; i++) {
tcp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]);
}
for(int i = 0; i < 4; i++) {
tcp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]);
}
// Set IP version
tcp_info->ipVersion = 6;
} }
for(int i = 0; i < 4; i++) {
tcp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]);
}
// Set IP version
tcp_info->ipVersion = 6;
// Set protocol
tcp_info->protocol = TCP;
// Send event // Send event
bpf_ringbuf_submit(tcp_info, 0); bpf_ringbuf_submit(tcp_info, 0);
@@ -143,7 +101,7 @@ int BPF_PROG(udp_v4_connect, struct sock *sk) {
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid()); udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Set src and dist ports // Set src and dist ports
udp_info->sport = sk->__sk_common.skc_num; udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
udp_info->dport = sk->__sk_common.skc_dport; udp_info->dport = sk->__sk_common.skc_dport;
// Set src and dist IPs // Set src and dist IPs
@@ -187,7 +145,7 @@ int BPF_PROG(udp_v6_connect, struct sock *sk) {
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid()); udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Set src and dist ports // Set src and dist ports
udp_info->sport = sk->__sk_common.skc_num; udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
udp_info->dport = sk->__sk_common.skc_dport; udp_info->dport = sk->__sk_common.skc_dport;
// Set src and dist IPs // Set src and dist IPs

View File

@@ -32,23 +32,14 @@ func StartEBPFWorker(ch chan packet.Packet) {
} }
defer objs.Close() defer objs.Close()
// Create a link to the tcp_v4_connect program. // Create a link to the tcp_connect program.
linkTCPIPv4, err := link.AttachTracing(link.TracingOptions{ linkTCPConnect, err := link.AttachTracing(link.TracingOptions{
Program: objs.bpfPrograms.TcpV4Connect, Program: objs.bpfPrograms.TcpConnect,
}) })
if err != nil { if err != nil {
log.Errorf("ebpf: failed to attach to tcp_v4_connect: %s ", err) log.Errorf("ebpf: failed to attach to tcp_v4_connect: %s ", err)
} }
defer linkTCPIPv4.Close() defer linkTCPConnect.Close()
// Create a link to the tcp_v6_connect program.
linkTCPIPv6, err := link.AttachTracing(link.TracingOptions{
Program: objs.bpfPrograms.TcpV6Connect,
})
if err != nil {
log.Errorf("ebpf: failed to attach to tcp_v6_connect: %s ", err)
}
defer linkTCPIPv6.Close()
// Create a link to the udp_v4_connect program. // Create a link to the udp_v4_connect program.
linkUDPV4, err := link.AttachTracing(link.TracingOptions{ linkUDPV4, err := link.AttachTracing(link.TracingOptions{
@@ -102,7 +93,7 @@ func StartEBPFWorker(ch chan packet.Packet) {
} }
info := packet.Info{ info := packet.Info{
Inbound: false, Inbound: event.Direction == 1,
InTunnel: false, InTunnel: false,
Version: packet.IPVersion(event.IpVersion), Version: packet.IPVersion(event.IpVersion),
Protocol: packet.IPProtocol(event.Protocol), Protocol: packet.IPProtocol(event.Protocol),
@@ -112,11 +103,16 @@ func StartEBPFWorker(ch chan packet.Packet) {
Dst: arrayToIP(event.Daddr, packet.IPVersion(event.IpVersion)), Dst: arrayToIP(event.Daddr, packet.IPVersion(event.IpVersion)),
PID: event.Pid, PID: event.Pid,
} }
log.Debugf("ebpf: PID: %d conn: %s:%d -> %s:%d %s %s", info.PID, info.LocalIP(), info.LocalPort(), info.RemoteIP(), info.RemotePort(), info.Version.String(), info.Protocol.String()) if isEventValid(event) {
log.Debugf("ebpf: PID: %d conn: %s:%d -> %s:%d %s %s", info.PID, info.LocalIP(), info.LocalPort(), info.RemoteIP(), info.RemotePort(), info.Version.String(), info.Protocol.String())
p := &infoPacket{}
p.SetPacketInfo(info)
ch <- p
} else {
log.Debugf("ebpf: invalid event PID: %d conn: %s:%d -> %s:%d %s %s", info.PID, info.LocalIP(), info.LocalPort(), info.RemoteIP(), info.RemotePort(), info.Version.String(), info.Protocol.String())
}
p := &infoPacket{}
p.SetPacketInfo(info)
ch <- p
} }
}() }()
} }
@@ -125,7 +121,28 @@ func StopEBPFWorker() {
close(stopper) close(stopper)
} }
// arrayToIP converts IPv4 number to net.IP func isEventValid(event bpfEvent) bool {
if event.Dport == 0 {
return false
}
if event.Sport == 0 {
return false
}
if event.IpVersion == 4 {
if event.Saddr[0] == 0 {
return false
}
if event.Daddr[0] == 0 {
return false
}
}
return true
}
// arrayToIP converts IP number array to net.IP
func arrayToIP(ipNum [4]uint32, ipVersion packet.IPVersion) net.IP { func arrayToIP(ipNum [4]uint32, ipVersion packet.IPVersion) net.IP {
if ipVersion == packet.IPv4 { if ipVersion == packet.IPv4 {
return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4) return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4)