From daeafa406dc667c056e269a9ef46f9074700670c Mon Sep 17 00:00:00 2001 From: Vladimir Stoilov Date: Fri, 23 Jun 2023 10:06:31 +0300 Subject: [PATCH] Add ebpf bandwidth monitoring --- .../interception/ebpf/bandwidth/bpf_bpfeb.go | 147 +++++++++++++ .../interception/ebpf/bandwidth/bpf_bpfeb.o | Bin 0 -> 41832 bytes .../interception/ebpf/bandwidth/bpf_bpfel.go | 147 +++++++++++++ .../interception/ebpf/bandwidth/bpf_bpfel.o | Bin 0 -> 41832 bytes .../interception/ebpf/bandwidth/interface.go | 179 +++++++++++++++ firewall/interception/ebpf/bpf_bpfeb.o | Bin 43024 -> 0 bytes firewall/interception/ebpf/bpf_bpfel.o | Bin 43024 -> 0 bytes .../{ => connection_listener}/bpf_bpfeb.go | 6 +- .../ebpf/connection_listener/bpf_bpfeb.o | Bin 0 -> 43080 bytes .../{ => connection_listener}/bpf_bpfel.go | 6 +- .../ebpf/{ => connection_listener}/packet.go | 6 - .../ebpf/{ => connection_listener}/worker.go | 26 +-- .../interception/ebpf/programs/bandwidth.c | 204 ++++++++++++++++++ .../{program => programs}/bpf/bpf_core_read.h | 0 .../bpf/bpf_helper_defs.h | 0 .../{program => programs}/bpf/bpf_helpers.h | 0 .../{program => programs}/bpf/bpf_tracing.h | 0 .../ebpf/{program => programs}/monitor.c | 8 +- .../ebpf/{program => programs}/update.sh | 0 .../ebpf/{program => programs}/vmlinux-x86.h | 0 firewall/interception/interception_linux.go | 13 +- 21 files changed, 707 insertions(+), 35 deletions(-) create mode 100644 firewall/interception/ebpf/bandwidth/bpf_bpfeb.go create mode 100644 firewall/interception/ebpf/bandwidth/bpf_bpfeb.o create mode 100644 firewall/interception/ebpf/bandwidth/bpf_bpfel.go create mode 100644 firewall/interception/ebpf/bandwidth/bpf_bpfel.o create mode 100644 firewall/interception/ebpf/bandwidth/interface.go delete mode 100644 firewall/interception/ebpf/bpf_bpfeb.o delete mode 100644 firewall/interception/ebpf/bpf_bpfel.o rename firewall/interception/ebpf/{ => connection_listener}/bpf_bpfeb.go (95%) create mode 100644 firewall/interception/ebpf/connection_listener/bpf_bpfeb.o rename firewall/interception/ebpf/{ => connection_listener}/bpf_bpfel.go (95%) rename firewall/interception/ebpf/{ => connection_listener}/packet.go (84%) rename firewall/interception/ebpf/{ => connection_listener}/worker.go (84%) create mode 100644 firewall/interception/ebpf/programs/bandwidth.c rename firewall/interception/ebpf/{program => programs}/bpf/bpf_core_read.h (100%) rename firewall/interception/ebpf/{program => programs}/bpf/bpf_helper_defs.h (100%) rename firewall/interception/ebpf/{program => programs}/bpf/bpf_helpers.h (100%) rename firewall/interception/ebpf/{program => programs}/bpf/bpf_tracing.h (100%) rename firewall/interception/ebpf/{program => programs}/monitor.c (94%) rename firewall/interception/ebpf/{program => programs}/update.sh (100%) rename firewall/interception/ebpf/{program => programs}/vmlinux-x86.h (100%) diff --git a/firewall/interception/ebpf/bandwidth/bpf_bpfeb.go b/firewall/interception/ebpf/bandwidth/bpf_bpfeb.go new file mode 100644 index 00000000..6c5d088c --- /dev/null +++ b/firewall/interception/ebpf/bandwidth/bpf_bpfeb.go @@ -0,0 +1,147 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 +// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +type bpfSkInfo struct { + Rx uint64 + Tx uint64 +} + +type bpfSkKey struct { + SrcIp [4]uint32 + DstIp [4]uint32 + SrcPort uint16 + DstPort uint16 + Protocol uint8 + Ipv6 uint8 + _ [2]byte +} + +// loadBpf returns the embedded CollectionSpec for bpf. +func loadBpf() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_BpfBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load bpf: %w", err) + } + + return spec, err +} + +// loadBpfObjects loads bpf and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *bpfObjects +// *bpfPrograms +// *bpfMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadBpf() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// bpfSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfSpecs struct { + bpfProgramSpecs + bpfMapSpecs +} + +// bpfSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfProgramSpecs struct { + SocketOperations *ebpf.ProgramSpec `ebpf:"socket_operations"` + UdpRecvmsg *ebpf.ProgramSpec `ebpf:"udp_recvmsg"` + UdpSendmsg *ebpf.ProgramSpec `ebpf:"udp_sendmsg"` + Udpv6Recvmsg *ebpf.ProgramSpec `ebpf:"udpv6_recvmsg"` + Udpv6Sendmsg *ebpf.ProgramSpec `ebpf:"udpv6_sendmsg"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + PmBandwidthMap *ebpf.MapSpec `ebpf:"pm_bandwidth_map"` +} + +// bpfObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfObjects struct { + bpfPrograms + bpfMaps +} + +func (o *bpfObjects) Close() error { + return _BpfClose( + &o.bpfPrograms, + &o.bpfMaps, + ) +} + +// bpfMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfMaps struct { + PmBandwidthMap *ebpf.Map `ebpf:"pm_bandwidth_map"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.PmBandwidthMap, + ) +} + +// bpfPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfPrograms struct { + SocketOperations *ebpf.Program `ebpf:"socket_operations"` + UdpRecvmsg *ebpf.Program `ebpf:"udp_recvmsg"` + UdpSendmsg *ebpf.Program `ebpf:"udp_sendmsg"` + Udpv6Recvmsg *ebpf.Program `ebpf:"udpv6_recvmsg"` + Udpv6Sendmsg *ebpf.Program `ebpf:"udpv6_sendmsg"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.SocketOperations, + p.UdpRecvmsg, + p.UdpSendmsg, + p.Udpv6Recvmsg, + p.Udpv6Sendmsg, + ) +} + +func _BpfClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed bpf_bpfeb.o +var _BpfBytes []byte diff --git a/firewall/interception/ebpf/bandwidth/bpf_bpfeb.o b/firewall/interception/ebpf/bandwidth/bpf_bpfeb.o new file mode 100644 index 0000000000000000000000000000000000000000..dea3c5f67ba7d8dd1c40046a0995b1410c19aed7 GIT binary patch literal 41832 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 BuKNH0 literal 0 HcmV?d00001 diff --git a/firewall/interception/ebpf/bandwidth/interface.go b/firewall/interception/ebpf/bandwidth/interface.go new file mode 100644 index 00000000..3930a45e --- /dev/null +++ b/firewall/interception/ebpf/bandwidth/interface.go @@ -0,0 +1,179 @@ +package ebpf + +import ( + "fmt" + "net" + "path/filepath" + "syscall" + "time" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/rlimit" + "github.com/safing/portbase/log" + "golang.org/x/sys/unix" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../programs/bandwidth.c + +var ebpfInterface = struct { + objs bpfObjects + sockOptionsLink link.Link + udpv4SMLink link.Link + udpv4RMLink link.Link + udpv6SMLink link.Link + udpv6RMLink link.Link +}{ + objs: bpfObjects{}, +} + +func SetupBandwidthInterface() error { + + // Allow the current process to lock memory for eBPF resources. + err := rlimit.RemoveMemlock() + if err != nil { + return fmt.Errorf("failed to remove memlock: %s", err) + } + + // Load pre-compiled programs and maps into the kernel. + err = loadBpfObjects(&ebpfInterface.objs, nil) + if err != nil { + return fmt.Errorf("feiled loading objects: %s", err) + } + + defer func() { + if err != nil { + // Defer the cleanup function to be called at the end of the enclosing function + // If there was an error during the execution, shutdown the BandwithInterface + ShutdownBandwithInterface() + } + }() + + // Find the cgroup path + path, err := findCgroupPath() + if err != nil { + return fmt.Errorf("faield to find cgroup paths: %s", err) + } + + // Attach socket options for monitoring connections + ebpfInterface.sockOptionsLink, err = link.AttachCgroup(link.CgroupOptions{ + Path: path, + Program: ebpfInterface.objs.bpfPrograms.SocketOperations, + Attach: ebpf.AttachCGroupSockOps, + }) + if err != nil { + return fmt.Errorf("Failed to open module sockops: %s", err) + } + + // Attach Udp Ipv4 recive message tracing + ebpfInterface.udpv4RMLink, err = link.AttachTracing(link.TracingOptions{ + Program: ebpfInterface.objs.UdpRecvmsg, + }) + if err != nil { + return fmt.Errorf("Failed to open trace Udp IPv4 recvmsg: %s", err) + } + + // Attach UDP IPv4 send message tracing + ebpfInterface.udpv4SMLink, err = link.AttachTracing(link.TracingOptions{ + Program: ebpfInterface.objs.UdpSendmsg, + }) + if err != nil { + return fmt.Errorf("Failed to open trace Udp IPv4 sendmsg: %s", err) + } + + // Attach UDP IPv6 receive message tracing + ebpfInterface.udpv6RMLink, err = link.AttachTracing(link.TracingOptions{ + Program: ebpfInterface.objs.Udpv6Recvmsg, + }) + if err != nil { + return fmt.Errorf("Failed to open trace Udp IPv6 recvmsg: %s", err) + } + + // Attach UDP IPv6 send message tracing + ebpfInterface.udpv6RMLink, err = link.AttachTracing(link.TracingOptions{ + Program: ebpfInterface.objs.Udpv6Sendmsg, + }) + if err != nil { + return fmt.Errorf("Failed to open trace Udp IPv6 sendmsg: %s", err) + } + + // Example code that will print the bandwidth table every 10 seconds + // go func() { + // ticker := time.NewTicker(10 * time.Second) + // defer ticker.Stop() + // for range ticker.C { + // printBandwidthData() + // } + // }() + + return nil +} + +// ShutdownBandwithInterface shuts down the bandwidth interface by closing the associated links and objects. +func ShutdownBandwithInterface() { + // Close the sockOptionsLink if it is not nil + if ebpfInterface.sockOptionsLink != nil { + ebpfInterface.sockOptionsLink.Close() + } + + // Close the udpv4SMLink if it is not nil + if ebpfInterface.udpv4SMLink != nil { + ebpfInterface.udpv4SMLink.Close() + } + + // Close the udpv4RMLink if it is not nil + if ebpfInterface.udpv4RMLink != nil { + ebpfInterface.udpv4RMLink.Close() + } + + // Close the udpv6SMLink if it is not nil + if ebpfInterface.udpv6SMLink != nil { + ebpfInterface.udpv6SMLink.Close() + } + + // Close the udpv6RMLink if it is not nil + if ebpfInterface.udpv6RMLink != nil { + ebpfInterface.udpv6RMLink.Close() + } + + // Close the ebpfInterface objects + ebpfInterface.objs.Close() +} + +func findCgroupPath() (string, error) { + cgroupPath := "/sys/fs/cgroup" + + var st syscall.Statfs_t + err := syscall.Statfs(cgroupPath, &st) + if err != nil { + return "", err + } + isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC + if !isCgroupV2Enabled { + cgroupPath = filepath.Join(cgroupPath, "unified") + } + return cgroupPath, nil +} + +func printBandwidthData() { + iter := ebpfInterface.objs.bpfMaps.PmBandwidthMap.Iterate() + var skKey bpfSkKey + var skInfo bpfSkInfo + for iter.Next(&skKey, &skInfo) { + log.Debugf("Connection: %d %s:%d %s:%d %d %d", skKey.Protocol, + arrayToIP(skKey.SrcIp, skKey.Ipv6).String(), skKey.SrcPort, + arrayToIP(skKey.DstIp, skKey.Ipv6).String(), skKey.DstPort, + skInfo.Rx, skInfo.Tx, + ) + } +} + +// arrayToIP converts IP number array to net.IP +func arrayToIP(ipNum [4]uint32, ipv6 uint8) net.IP { + if ipv6 == 0 { + return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4) + } else { + return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 16) + } +} diff --git a/firewall/interception/ebpf/bpf_bpfeb.o b/firewall/interception/ebpf/bpf_bpfeb.o deleted file mode 100644 index 93a2bb8f51fd011e2cf92eef5ca20774e48c8c4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43024 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 OX(>(mo}AWy!T$sHqU>z| diff --git a/firewall/interception/ebpf/bpf_bpfel.o b/firewall/interception/ebpf/bpf_bpfel.o deleted file mode 100644 index f5396ab0ee2c9729a24d334d225cb2ef93de1414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43024 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 literal 0 HcmV?d00001 diff --git a/firewall/interception/ebpf/bpf_bpfel.go b/firewall/interception/ebpf/connection_listener/bpf_bpfel.go similarity index 95% rename from firewall/interception/ebpf/bpf_bpfel.go rename to firewall/interception/ebpf/connection_listener/bpf_bpfel.go index a72d9e29..8a0ab7be 100644 --- a/firewall/interception/ebpf/bpf_bpfel.go +++ b/firewall/interception/ebpf/connection_listener/bpf_bpfel.go @@ -75,7 +75,7 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - Events *ebpf.MapSpec `ebpf:"events"` + PmConnectionEvents *ebpf.MapSpec `ebpf:"pm_connection_events"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -97,12 +97,12 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - Events *ebpf.Map `ebpf:"events"` + PmConnectionEvents *ebpf.Map `ebpf:"pm_connection_events"` } func (m *bpfMaps) Close() error { return _BpfClose( - m.Events, + m.PmConnectionEvents, ) } diff --git a/firewall/interception/ebpf/packet.go b/firewall/interception/ebpf/connection_listener/packet.go similarity index 84% rename from firewall/interception/ebpf/packet.go rename to firewall/interception/ebpf/connection_listener/packet.go index 19895646..fffd5558 100644 --- a/firewall/interception/ebpf/packet.go +++ b/firewall/interception/ebpf/connection_listener/packet.go @@ -13,12 +13,6 @@ type infoPacket struct { pmpacket.Base } -// InfoOnly returns whether the packet is informational only and does not -// represent an actual packet. -func (pkt *infoPacket) InfoOnly() bool { - return true -} - // LoadPacketData does nothing on Linux, as data is always fully parsed. func (pkt *infoPacket) LoadPacketData() error { return fmt.Errorf("can't load data in info only packet") diff --git a/firewall/interception/ebpf/worker.go b/firewall/interception/ebpf/connection_listener/worker.go similarity index 84% rename from firewall/interception/ebpf/worker.go rename to firewall/interception/ebpf/connection_listener/worker.go index 2fdf50c2..ba3d9022 100644 --- a/firewall/interception/ebpf/worker.go +++ b/firewall/interception/ebpf/connection_listener/worker.go @@ -5,21 +5,18 @@ import ( "encoding/binary" "errors" "net" - "time" "unsafe" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/rlimit" - "github.com/safing/portbase/log" "github.com/safing/portmaster/network/packet" ) -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf program/monitor.c +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf ../programs/monitor.c var stopper chan struct{} -// StartEBPFWorker starts the ebpf worker. func StartEBPFWorker(ch chan packet.Packet) { stopper = make(chan struct{}) go func() { @@ -35,7 +32,7 @@ func StartEBPFWorker(ch chan packet.Packet) { log.Errorf("ebpf: failed to load ebpf object: %s", err) return } - defer objs.Close() //nolint:errcheck + defer objs.Close() // Create a link to the tcp_connect program. linkTCPConnect, err := link.AttachTracing(link.TracingOptions{ @@ -45,7 +42,7 @@ func StartEBPFWorker(ch chan packet.Packet) { log.Errorf("ebpf: failed to attach to tcp_v4_connect: %s ", err) return } - defer linkTCPConnect.Close() //nolint:errcheck + defer linkTCPConnect.Close() // Create a link to the udp_v4_connect program. linkUDPV4, err := link.AttachTracing(link.TracingOptions{ @@ -55,7 +52,7 @@ func StartEBPFWorker(ch chan packet.Packet) { log.Errorf("ebpf: failed to attach to udp_v4_connect: %s ", err) return } - defer linkUDPV4.Close() //nolint:errcheck + defer linkUDPV4.Close() // Create a link to the udp_v6_connect program. linkUDPV6, err := link.AttachTracing(link.TracingOptions{ @@ -65,14 +62,14 @@ func StartEBPFWorker(ch chan packet.Packet) { log.Errorf("ebpf: failed to attach to udp_v6_connect: %s ", err) return } - defer linkUDPV6.Close() //nolint:errcheck + defer linkUDPV6.Close() - rd, err := ringbuf.NewReader(objs.bpfMaps.Events) + rd, err := ringbuf.NewReader(objs.bpfMaps.PmConnectionEvents) if err != nil { log.Errorf("ebpf: failed to open ring buffer: %s", err) return } - defer rd.Close() //nolint:errcheck + defer rd.Close() go func() { <-stopper @@ -111,7 +108,6 @@ func StartEBPFWorker(ch chan packet.Packet) { Src: arrayToIP(event.Saddr, packet.IPVersion(event.IpVersion)), Dst: arrayToIP(event.Daddr, packet.IPVersion(event.IpVersion)), PID: int(event.Pid), - SeenAt: time.Now(), } 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()) @@ -127,7 +123,6 @@ func StartEBPFWorker(ch chan packet.Packet) { }() } -// StopEBPFWorker stops the ebpf worker. func StopEBPFWorker() { close(stopper) } @@ -153,12 +148,11 @@ func isEventValid(event bpfEvent) bool { return true } -// arrayToIP converts IP number array to net.IP. +// arrayToIP converts IP number array to net.IP func arrayToIP(ipNum [4]uint32, ipVersion packet.IPVersion) net.IP { if ipVersion == packet.IPv4 { - // FIXME: maybe convertIPv4 from windowskext package return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4) + } else { + return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 16) } - // FIXME: maybe use convertIPv6 from windowskext package - return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 16) } diff --git a/firewall/interception/ebpf/programs/bandwidth.c b/firewall/interception/ebpf/programs/bandwidth.c new file mode 100644 index 00000000..8971de48 --- /dev/null +++ b/firewall/interception/ebpf/programs/bandwidth.c @@ -0,0 +1,204 @@ +#include "vmlinux-x86.h" +#include "bpf/bpf_helpers.h" +#include "bpf/bpf_tracing.h" +#include "bpf/bpf_core_read.h" + +#define AF_INET 2 +#define AF_INET6 10 + +#define PROTOCOL_TCP 6 +#define PROTOCOL_UDP 17 + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct sk_key { + u32 src_ip[4]; + u32 dst_ip[4]; + u16 src_port; + u16 dst_port; + u8 protocol; + u8 ipv6; +}; + +struct sk_info { + u64 rx; + u64 tx; +}; + +// Max number of connections that will be kept. Increse the number if it's not enough. +#define SOCKOPS_MAP_SIZE 5000 +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, SOCKOPS_MAP_SIZE); + __type(key, struct sk_key); + __type(value, struct sk_info); +} pm_bandwidth_map SEC(".maps"); + +SEC("sockops") +int socket_operations(struct bpf_sock_ops *skops) { + switch (skops->op) { + case BPF_SOCK_OPS_TCP_CONNECT_CB: // Outgoing connections + // Set flag so any modification on the socket, will trigger this function. + bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); + return 0; + case BPF_SOCK_OPS_TCP_LISTEN_CB: // Listening ports + bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); + // No rx tx data for this socket object. + return 0; + case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: // Incoming connections + // Set flag so any modification on the socket, will trigger this function. + bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS); + return 0; + default: + break; + } + + struct bpf_sock *sk = skops->sk; + if (sk == NULL) { + return 0; + } + + struct sk_key key = {0}; + key.protocol = PROTOCOL_TCP; + if(sk->family == AF_INET) { + // Generate key for IPv4 + key.src_ip[0] = sk->src_ip4; + key.src_port = sk->src_port; + key.dst_ip[0] = sk->dst_ip4; + key.dst_port = __builtin_bswap16(sk->dst_port); + key.ipv6 = 0; + + struct sk_info newInfo = {0}; + newInfo.rx = skops->bytes_received; + newInfo.tx = skops->bytes_acked; + + bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); + } else if(sk->family == AF_INET6){ + // Generate key for IPv6 + for(int i = 0; i < 4; i++) { + key.src_ip[i] = sk->src_ip6[i]; + } + key.src_port = sk->src_port; + for(int i = 0; i < 4; i++) { + key.dst_ip[i] = sk->dst_ip6[i]; + } + key.dst_port = __builtin_bswap16(sk->dst_port); + key.ipv6 = 1; + } + + return 0; +} + +// udp_sendmsg hookes to the equvelent kernel function and saves the bandwoth data +SEC("fentry/udp_sendmsg") +int BPF_PROG(udp_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) { + struct sock_common *skc = &sk->__sk_common; + + // Create a key for the map and set all the nececery information. + struct sk_key key = {0}; + key.protocol = PROTOCOL_UDP; + key.src_ip[0] = skc->skc_rcv_saddr; + key.dst_ip[0] = skc->skc_daddr; + key.src_port = skc->skc_num; + key.dst_port = __builtin_bswap16(skc->skc_dport); + key.ipv6 = 0; + + // Update the map with the new information + struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); + if (info != NULL) { + __sync_fetch_and_add(&info->tx, len); + } else { + struct sk_info newInfo = {0}; + + newInfo.tx = len; + bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); + } + + return 0; +}; + +// udp_recvmsg hookes to the equvelent kernel function and saves the bandwoth data +SEC("fentry/udp_recvmsg") +int BPF_PROG(udp_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) { + struct sock_common *skc = &sk->__sk_common; + + // Create a key for the map and set all the nececery information. + struct sk_key key = {0}; + key.protocol = PROTOCOL_UDP; + key.src_ip[0] = skc->skc_rcv_saddr; + key.dst_ip[0] = skc->skc_daddr; + key.src_port = skc->skc_num; + key.dst_port = __builtin_bswap16(skc->skc_dport); + key.ipv6 = 0; + + // Update the map with the new information + struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); + if (info != NULL) { + __sync_fetch_and_add(&info->rx, len); + } else { + struct sk_info newInfo = {0}; + + newInfo.rx = len; + bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); + } + + return 0; +}; + +// udpv6_sendmsg hookes to the equvelent kernel function and saves the bandwoth data +SEC("fentry/udpv6_sendmsg") +int BPF_PROG(udpv6_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) { + struct sock_common *skc = &sk->__sk_common; + + // Create a key for the map and set all the nececery information. + struct sk_key key = {0}; + key.protocol = PROTOCOL_UDP; + for (int i = 0; i < 4; i++) { + key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; + key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; + } + key.src_port = skc->skc_num; + key.dst_port = __builtin_bswap16(skc->skc_dport); + key.ipv6 = 1; + + // Update the map with the new information + struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); + if (info != NULL) { + __sync_fetch_and_add(&info->tx, len); + } else { + struct sk_info newInfo = {0}; + newInfo.tx = len; + bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); + } + + return 0; +} + +// udpv6_recvmsg hookes to the equvelent kernel function and saves the bandwoth data +SEC("fentry/udpv6_recvmsg") +int BPF_PROG(udpv6_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) { + struct sock_common *skc = &sk->__sk_common; + + // Create a key for the map and set all the nececery information. + struct sk_key key = {0}; + key.protocol = PROTOCOL_UDP; + for (int i = 0; i < 4; i++) { + key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; + key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i]; + } + key.src_port = skc->skc_num; + key.dst_port = __builtin_bswap16(skc->skc_dport); + key.ipv6 = 1; + + // Update the map with the new information + struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key); + if (info != NULL) { + __sync_fetch_and_add(&info->rx, len); + } else { + struct sk_info newInfo = {0}; + newInfo.rx = len; + bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY); + } + + return 0; +} \ No newline at end of file diff --git a/firewall/interception/ebpf/program/bpf/bpf_core_read.h b/firewall/interception/ebpf/programs/bpf/bpf_core_read.h similarity index 100% rename from firewall/interception/ebpf/program/bpf/bpf_core_read.h rename to firewall/interception/ebpf/programs/bpf/bpf_core_read.h diff --git a/firewall/interception/ebpf/program/bpf/bpf_helper_defs.h b/firewall/interception/ebpf/programs/bpf/bpf_helper_defs.h similarity index 100% rename from firewall/interception/ebpf/program/bpf/bpf_helper_defs.h rename to firewall/interception/ebpf/programs/bpf/bpf_helper_defs.h diff --git a/firewall/interception/ebpf/program/bpf/bpf_helpers.h b/firewall/interception/ebpf/programs/bpf/bpf_helpers.h similarity index 100% rename from firewall/interception/ebpf/program/bpf/bpf_helpers.h rename to firewall/interception/ebpf/programs/bpf/bpf_helpers.h diff --git a/firewall/interception/ebpf/program/bpf/bpf_tracing.h b/firewall/interception/ebpf/programs/bpf/bpf_tracing.h similarity index 100% rename from firewall/interception/ebpf/program/bpf/bpf_tracing.h rename to firewall/interception/ebpf/programs/bpf/bpf_tracing.h diff --git a/firewall/interception/ebpf/program/monitor.c b/firewall/interception/ebpf/programs/monitor.c similarity index 94% rename from firewall/interception/ebpf/program/monitor.c rename to firewall/interception/ebpf/programs/monitor.c index 9ba6a309..e82c6756 100644 --- a/firewall/interception/ebpf/program/monitor.c +++ b/firewall/interception/ebpf/programs/monitor.c @@ -20,7 +20,7 @@ char __license[] SEC("license") = "GPL"; struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 24); -} events SEC(".maps"); +} pm_connection_events SEC(".maps"); // Event struct that will be sent to Go on each new connection. (The name should be the same as the go generate command) struct Event { @@ -41,7 +41,7 @@ SEC("fentry/tcp_connect") int BPF_PROG(tcp_connect, struct sock *sk) { // Alloc space for the event struct Event *tcp_info; - tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0); + tcp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!tcp_info) { return 0; } @@ -97,7 +97,7 @@ int BPF_PROG(udp_v4_connect, struct sock *sk) { // Allocate space for the event. struct Event *udp_info; - udp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0); + udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!udp_info) { return 0; } @@ -146,7 +146,7 @@ int BPF_PROG(udp_v6_connect, struct sock *sk) { // Allocate space for the event. struct Event *udp_info; - udp_info = bpf_ringbuf_reserve(&events, sizeof(struct Event), 0); + udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0); if (!udp_info) { return 0; } diff --git a/firewall/interception/ebpf/program/update.sh b/firewall/interception/ebpf/programs/update.sh similarity index 100% rename from firewall/interception/ebpf/program/update.sh rename to firewall/interception/ebpf/programs/update.sh diff --git a/firewall/interception/ebpf/program/vmlinux-x86.h b/firewall/interception/ebpf/programs/vmlinux-x86.h similarity index 100% rename from firewall/interception/ebpf/program/vmlinux-x86.h rename to firewall/interception/ebpf/programs/vmlinux-x86.h diff --git a/firewall/interception/interception_linux.go b/firewall/interception/interception_linux.go index 01aa3398..b657c080 100644 --- a/firewall/interception/interception_linux.go +++ b/firewall/interception/interception_linux.go @@ -1,7 +1,8 @@ package interception import ( - "github.com/safing/portmaster/firewall/interception/ebpf" + // bandwidth "github.com/safing/portmaster/firewall/interception/ebpf/bandwidth" + conn_listener "github.com/safing/portmaster/firewall/interception/ebpf/connection_listener" "github.com/safing/portmaster/firewall/interception/nfq" "github.com/safing/portmaster/network" "github.com/safing/portmaster/network/packet" @@ -9,13 +10,19 @@ import ( // start starts the interception. func start(ch chan packet.Packet) error { - ebpf.StartEBPFWorker(ch) + // Start ebpf new connection listener + conn_listener.StartEBPFWorker(ch) + // Start ebpf bandwidth listener + // bandwidth.SetupBandwidthInterface() return StartNfqueueInterception(ch) } // stop starts the interception. func stop() error { - ebpf.StopEBPFWorker() + // Stop ebpf connection listener + conn_listener.StopEBPFWorker() + // Stop ebpf bandwidth listener + // bandwidth.ShutdownBandwithInterface() return StopNfqueueInterception() }