summaryrefslogtreecommitdiff
path: root/bpf
diff options
context:
space:
mode:
authorsinanmohd <sinan@sinanmohd.com>2024-07-06 15:24:51 +0530
committersinanmohd <sinan@sinanmohd.com>2024-07-06 15:26:51 +0530
commit459f114783b9e37f8c44d39802f01976b0129e7b (patch)
tree34f4a07406e07521b3bc589aefdbb2c4a419fa56 /bpf
parent46e8c7d6a678a3050a0bde7acef10278a72e8a6f (diff)
bpf -> usage
Diffstat (limited to 'bpf')
-rw-r--r--bpf/bpf_bpfel.go125
-rw-r--r--bpf/bpf_bpfel.obin6592 -> 0 bytes
-rw-r--r--bpf/bpf_usage.c102
-rw-r--r--bpf/gen.go3
-rw-r--r--bpf/main.go205
5 files changed, 0 insertions, 435 deletions
diff --git a/bpf/bpf_bpfel.go b/bpf/bpf_bpfel.go
deleted file mode 100644
index f4ffb76..0000000
--- a/bpf/bpf_bpfel.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Code generated by bpf2go; DO NOT EDIT.
-//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
-
-package bpf
-
-import (
- "bytes"
- _ "embed"
- "fmt"
- "io"
-
- "github.com/cilium/ebpf"
-)
-
-// 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 {
- EgressFunc *ebpf.ProgramSpec `ebpf:"egress__func"`
- IngressFunc *ebpf.ProgramSpec `ebpf:"ingress_func"`
-}
-
-// bpfMapSpecs contains maps before they are loaded into the kernel.
-//
-// It can be passed ebpf.CollectionSpec.Assign.
-type bpfMapSpecs struct {
- EgressIp4UsageMap *ebpf.MapSpec `ebpf:"egress_ip4_usage_map"`
- IngressIp4UsageMap *ebpf.MapSpec `ebpf:"ingress_ip4_usage_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 {
- EgressIp4UsageMap *ebpf.Map `ebpf:"egress_ip4_usage_map"`
- IngressIp4UsageMap *ebpf.Map `ebpf:"ingress_ip4_usage_map"`
-}
-
-func (m *bpfMaps) Close() error {
- return _BpfClose(
- m.EgressIp4UsageMap,
- m.IngressIp4UsageMap,
- )
-}
-
-// 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 {
- EgressFunc *ebpf.Program `ebpf:"egress__func"`
- IngressFunc *ebpf.Program `ebpf:"ingress_func"`
-}
-
-func (p *bpfPrograms) Close() error {
- return _BpfClose(
- p.EgressFunc,
- p.IngressFunc,
- )
-}
-
-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_bpfel.o
-var _BpfBytes []byte
diff --git a/bpf/bpf_bpfel.o b/bpf/bpf_bpfel.o
deleted file mode 100644
index d9a44b4..0000000
--- a/bpf/bpf_bpfel.o
+++ /dev/null
Binary files differ
diff --git a/bpf/bpf_usage.c b/bpf/bpf_usage.c
deleted file mode 100644
index 8b2343a..0000000
--- a/bpf/bpf_usage.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <linux/bpf.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-
-#include <bpf/bpf_endian.h>
-#include <bpf/bpf_helpers.h>
-
-#define MAX_MAP_ENTRIES 4096
-
-char __license[] SEC("license") = "GPL";
-
-struct {
- __uint(type, BPF_MAP_TYPE_LRU_HASH);
- __uint(max_entries, MAX_MAP_ENTRIES);
- __type(key, __u64); // source mac address
- __type(value, __u64); // no of bytes
-} ingress_ip4_usage_map SEC(".maps");
-
-struct {
- __uint(type, BPF_MAP_TYPE_LRU_HASH);
- __uint(max_entries, MAX_MAP_ENTRIES);
- __type(key, __u64); // destination mac address
- __type(value, __u64); // no of bytes
-} egress_ip4_usage_map SEC(".maps");
-
-typedef enum {
- UPDATE_USAGE_INGRESS,
- UPDATE_USAGE_EGRESS,
-} update_usage_t;
-
-static __always_inline __u64 nchar6_to_u64(unsigned char bytes[6])
-{
- union {
- char bytes[6];
- __u64 i;
- } ret;
-
- ret.i = 0;
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- ret.bytes[0] = bytes[5];
- ret.bytes[1] = bytes[4];
- ret.bytes[2] = bytes[3];
- ret.bytes[3] = bytes[2];
- ret.bytes[4] = bytes[1];
- ret.bytes[5] = bytes[0];
-#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- ret.bytes[0] = bytes[0];
- ret.bytes[1] = bytes[1];
- ret.bytes[2] = bytes[2];
- ret.bytes[3] = bytes[3];
- ret.bytes[4] = bytes[4];
- ret.bytes[5] = bytes[5];
-#endif
-
- return ret.i;
-}
-
-static __always_inline int update_usage(void *map, struct __sk_buff *skb,
- update_usage_t traffic)
-{
- __u64 mac, len, *usage;
-
- void *data_end = (void *)(long)skb->data_end;
- struct ethhdr *eth = (void *)(long)skb->data;
-
- if ((void *) (eth + 1) > data_end)
- return TCX_PASS;
-
- if (skb->protocol != bpf_htons(ETH_P_IP) &&
- skb->protocol != bpf_htons(ETH_P_IPV6)) {
- return TCX_PASS;
- }
-
- len = skb->len - sizeof(struct ethhdr);
- if (traffic == UPDATE_USAGE_INGRESS) {
- mac = nchar6_to_u64(eth->h_source);
- } else {
- mac = nchar6_to_u64(eth->h_dest);
- }
-
- usage = bpf_map_lookup_elem(map, &mac);
- if (!usage) {
- /* no entry in the map for this IP address yet. */
- bpf_map_update_elem(map, &mac, &len, BPF_ANY);
- } else {
- __sync_fetch_and_add(usage, len);
- }
-
- return TCX_PASS;
-}
-
-SEC("tc")
-int ingress_func(struct __sk_buff *skb)
-{
- return update_usage(&ingress_ip4_usage_map, skb, UPDATE_USAGE_INGRESS);
-}
-
-SEC("tc")
-int egress__func(struct __sk_buff *skb)
-{
- return update_usage(&egress_ip4_usage_map, skb, UPDATE_USAGE_EGRESS);
-}
diff --git a/bpf/gen.go b/bpf/gen.go
deleted file mode 100644
index ff585db..0000000
--- a/bpf/gen.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package bpf
-
-//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel bpf bpf_usage.c
diff --git a/bpf/main.go b/bpf/main.go
deleted file mode 100644
index d956a9b..0000000
--- a/bpf/main.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package bpf
-
-import (
- "context"
- "errors"
- "log"
- "net"
- "os"
- "os/signal"
- "syscall"
- "time"
-
- "github.com/cilium/ebpf"
- "github.com/cilium/ebpf/link"
- "github.com/jackc/pgx/v5/pgtype"
- "sinanmohd.com/redq/db"
-)
-
-type UsageStat struct {
- lastSeen time.Time
- lastDbPush time.Time
- bandwidthIngress uint64
- bandwidthEgress uint64
- ingress uint64
- egress uint64
-}
-type UsageMap map[uint64]UsageStat
-
-func Run(iface *net.Interface, queries *db.Queries, ctxDb context.Context) {
- usageMap := make(UsageMap)
-
- objs := bpfObjects{}
- if err := loadBpfObjects(&objs, nil); err != nil {
- log.Fatalf("loading objects: %s", err)
- }
- defer objs.Close()
-
- ingressLink, err := link.AttachTCX(link.TCXOptions{
- Interface: iface.Index,
- Program: objs.IngressFunc,
- Attach: ebpf.AttachTCXIngress,
- })
- if err != nil {
- log.Fatalf("could not attach TCx program: %s", err)
- }
- defer ingressLink.Close()
-
- egressLink, err := link.AttachTCX(link.TCXOptions{
- Interface: iface.Index,
- Program: objs.EgressFunc,
- Attach: ebpf.AttachTCXEgress,
- })
- if err != nil {
- log.Fatalf("could not attach TCx program: %s", err)
- }
- defer egressLink.Close()
-
- bpfTicker := time.NewTicker(time.Second)
- defer bpfTicker.Stop()
- dbTicker := time.NewTicker(time.Minute)
- defer dbTicker.Stop()
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, os.Interrupt, os.Kill, syscall.SIGTERM)
-
- for {
- select {
- case <-bpfTicker.C:
- err := usageMap.update(objs.IngressIp4UsageMap, objs.EgressIp4UsageMap)
- if err != nil {
- log.Printf("updating usageMap: %s", err)
- }
- case <-sigs:
- err := usageMap.updateDb(queries, ctxDb, false)
- if err != nil {
- log.Printf("updating Database: %s", err)
- }
- os.Exit(0)
- case <-dbTicker.C:
- err := usageMap.updateDb(queries, ctxDb, true)
- if err != nil {
- log.Printf("updating Database: %s", err)
- }
- }
- }
-}
-
-func (usageStat UsageStat) expired(timeStart *time.Time) bool {
- timeDiff := timeStart.Sub(usageStat.lastSeen)
- if timeDiff > time.Minute {
- return true
- }
-
- timeDiff = timeStart.Sub(usageStat.lastDbPush)
- if timeDiff > time.Hour {
- return true
- }
-
- return false
-}
-
-func (usageMap UsageMap) updateDb(queries *db.Queries, ctxDb context.Context, ifExpired bool) error {
- timeStart := time.Now()
-
- for key, value := range usageMap {
- if ifExpired && !value.expired(&timeStart) {
- continue
- }
-
- err := queries.EnterUsage(ctxDb, db.EnterUsageParams{
- Hardwareaddr: int64(key),
- Starttime: pgtype.Timestamp{
- Time: value.lastDbPush,
- Valid: true,
- },
- Stoptime: pgtype.Timestamp{
- Time: value.lastSeen,
- Valid: true,
- },
- Egress: int64(value.egress),
- Ingress: int64(value.ingress),
- })
- if err != nil {
- return err
- }
-
- delete(usageMap, key)
- }
-
- return nil
-}
-
-func (usageMap UsageMap) update(ingress *ebpf.Map, egress *ebpf.Map) error {
- timeStart := time.Now()
- batchKeys := make([]uint64, 4096)
- batchValues := make([]uint64, 4096)
- var key uint64
-
- cursor := ebpf.MapBatchCursor{}
- for {
- _, err := ingress.BatchLookupAndDelete(&cursor, batchKeys, batchValues, nil)
- for i := range batchKeys {
- /* TODO: maybe BatchLookupAndDelete is not the best idea with mostly empty map */
- if batchValues[i] == 0 {
- continue
- }
-
- key = batchKeys[i]
- usage, ok := usageMap[key]
- if ok {
- usage.bandwidthIngress = batchValues[i] - usage.ingress
- usage.ingress += batchValues[i]
- usage.lastSeen = timeStart
- usageMap[key] = usage
- } else {
- usageMap[key] = UsageStat{
- bandwidthIngress: batchValues[i],
- ingress: batchValues[i],
- lastDbPush: timeStart,
- lastSeen: timeStart,
- }
- }
- }
-
- if errors.Is(err, ebpf.ErrKeyNotExist) {
- break
- } else if err != nil {
- return err
- }
- }
-
- cursor = ebpf.MapBatchCursor{}
- for {
- _, err := egress.BatchLookupAndDelete(&cursor, batchKeys, batchValues, nil)
- for i := range batchKeys {
- /* TODO: maybe BatchLookupAndDelete is not the best idea with mostly empty map */
- if batchValues[i] == 0 {
- continue
- }
-
- key = batchKeys[i]
- usage, ok := usageMap[key]
- if ok {
- usage.bandwidthEgress = batchValues[i] - usage.egress
- usage.egress += batchValues[i]
- usage.lastSeen = timeStart
- usageMap[key] = usage
- } else {
- usageMap[key] = UsageStat{
- bandwidthEgress: batchValues[i],
- egress: batchValues[i],
- lastDbPush: timeStart,
- lastSeen: timeStart,
- }
- }
- }
-
- if errors.Is(err, ebpf.ErrKeyNotExist) {
- break
- } else if err != nil {
- return err
- }
- }
-
- return nil
-}