package main import ( "log" "net" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" ) // Adjust these constants for your setup const ( bpfObjectFile = "udp_flood_blocker.bpf.o" interfaceName = "eth0" // e.g. "eth0", "ens5", etc. on Ubuntu packetsPerSecThreshold = 10000 // example threshold ) func main() { // 1) Remove the memlock limit so eBPF maps can be created if err := rlimit.RemoveMemlock(); err != nil { log.Fatalf("failed to remove memlock limit: %v", err) } // 2) Load the compiled eBPF object from file spec, err := ebpf.LoadCollectionSpec(bpfObjectFile) if err != nil { log.Fatalf("failed to load eBPF collection spec from %s: %v", bpfObjectFile, err) } // 3) Create a Collection from the spec (program + maps) coll, err := ebpf.NewCollection(spec) if err != nil { log.Fatalf("failed to create eBPF collection: %v", err) } defer coll.Close() // 4) Get the XDP program from the collection prog, ok := coll.Programs["detect_and_block_udp"] if !ok { log.Fatalf("program 'detect_and_block_udp' not found in %s", bpfObjectFile) } // 5) Attach the XDP program to the given network interface ifIndex := resolveInterfaceIndex(interfaceName) linkXDP, err := link.AttachXDP(link.XDPOptions{ Program: prog, Interface: ifIndex, Flags: link.XDPGenericMode, // or link.XDPDriverMode if supported }) if err != nil { log.Fatalf("failed to attach XDP to interface %s: %v", interfaceName, err) } defer linkXDP.Close() log.Printf("XDP program attached to interface %s (ifIndex=%d)", interfaceName, ifIndex) // 6) Retrieve eBPF maps from the collection udpCountMap, ok := coll.Maps["udp_count"] if !ok { log.Fatalf("map 'udp_count' not found in the collection") } blockStateMap, ok := coll.Maps["block_state"] if !ok { log.Fatalf("map 'block_state' not found in the collection") } // Initialize block_state to 0 (allow) zeroKey := uint32(0) zeroVal := uint32(0) if err := blockStateMap.Update(&zeroKey, &zeroVal, ebpf.UpdateAny); err != nil { log.Fatalf("failed to initialize block_state: %v", err) } // We'll track how many UDP packets we see each second var oldCount, newCount uint64 ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { // Read the current count from the eBPF map if err := udpCountMap.Lookup(&zeroKey, &newCount); err != nil { log.Printf("lookup failed: %v", err) continue } diff := newCount - oldCount oldCount = newCount if diff > packetsPerSecThreshold { // Exceeded threshold => block further UDP log.Printf("[ALERT] High UDP rate: %d pkts/sec => blocking...", diff) blockVal := uint32(1) if err := blockStateMap.Update(&zeroKey, &blockVal, ebpf.UpdateAny); err != nil { log.Printf("failed to set block_state: %v", err) } } else { // If you want automatic unblocking, uncomment: /* blockVal := uint32(0) blockStateMap.Update(&zeroKey, &blockVal, ebpf.UpdateAny) */ log.Printf("UDP packets last second: %d", diff) } } } // resolveInterfaceIndex fetches the index of an interface by name func resolveInterfaceIndex(ifaceName string) int { iface, err := net.InterfaceByName(ifaceName) if err != nil { log.Fatalf("cannot find interface %q: %v", ifaceName, err) } return iface.Index }