github.com/cilium/ebpf@v0.10.0/examples/tcprtt_sockops/main.go (about) 1 //go:build linux 2 // +build linux 3 4 // This program demonstrates attaching an eBPF program to 5 // a cgroupv2 path and using sockops to process TCP socket events. 6 // It prints the IPs/ports/RTT information every time TCP sockets 7 // update their internal RTT value. 8 // It supports only IPv4 for this example. 9 // 10 // Sample output: 11 // 12 // examples# go run -exec sudo ./tcprtt_sockops 13 // 2022/08/14 20:58:03 eBPF program loaded and attached on cgroup /sys/fs/cgroup/unified 14 // 2022/08/14 20:58:03 Src addr Port -> Dest addr Port RTT (ms) 15 // 2022/08/14 20:58:09 10.0.1.205 54844 -> 20.42.73.25 443 67 16 // 2022/08/14 20:58:09 10.0.1.205 54844 -> 20.42.73.25 443 67 17 // 2022/08/14 20:58:33 10.0.1.205 38620 -> 140.82.121.4 443 26 18 // 2022/08/14 20:58:33 10.0.1.205 38620 -> 140.82.121.4 443 26 19 // 2022/08/14 20:58:43 34.67.40.146 45380 -> 10.0.1.205 5201 106 20 // 2022/08/14 20:58:43 34.67.40.146 45380 -> 10.0.1.205 5201 106 21 22 package main 23 24 import ( 25 "bytes" 26 "encoding/binary" 27 "errors" 28 "log" 29 "net" 30 "os" 31 "os/signal" 32 "path/filepath" 33 "syscall" 34 35 "github.com/cilium/ebpf" 36 "github.com/cilium/ebpf/internal" 37 "github.com/cilium/ebpf/link" 38 "github.com/cilium/ebpf/ringbuf" 39 "github.com/cilium/ebpf/rlimit" 40 41 "golang.org/x/sys/unix" 42 ) 43 44 // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 45 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags "linux" -cc $BPF_CLANG -cflags $BPF_CFLAGS -type rtt_event bpf tcprtt_sockops.c -- -I../headers 46 47 func main() { 48 stopper := make(chan os.Signal, 1) 49 signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 50 51 // Allow the current process to lock memory for eBPF resources. 52 if err := rlimit.RemoveMemlock(); err != nil { 53 log.Fatal(err) 54 } 55 56 // Find the path to a cgroup enabled to version 2 57 cgroupPath, err := findCgroupPath() 58 if err != nil { 59 log.Fatal(err) 60 } 61 62 // Load pre-compiled programs and maps into the kernel. 63 objs := bpfObjects{} 64 if err := loadBpfObjects(&objs, nil); err != nil { 65 log.Fatalf("loading objects: %v", err) 66 } 67 defer objs.Close() 68 69 // Attach ebpf program to a cgroupv2 70 link, err := link.AttachCgroup(link.CgroupOptions{ 71 Path: cgroupPath, 72 Program: objs.bpfPrograms.BpfSockopsCb, 73 Attach: ebpf.AttachCGroupSockOps, 74 }) 75 if err != nil { 76 log.Fatal(err) 77 } 78 defer link.Close() 79 80 log.Printf("eBPF program loaded and attached on cgroup %s\n", cgroupPath) 81 82 rd, err := ringbuf.NewReader(objs.bpfMaps.RttEvents) 83 if err != nil { 84 log.Fatalf("opening ringbuf reader: %s", err) 85 } 86 defer rd.Close() 87 88 log.Printf("%-15s %-6s -> %-15s %-6s %-6s", 89 "Src addr", 90 "Port", 91 "Dest addr", 92 "Port", 93 "RTT (ms)", 94 ) 95 go readLoop(rd) 96 97 // Wait 98 <-stopper 99 } 100 101 func findCgroupPath() (string, error) { 102 cgroupPath := "/sys/fs/cgroup" 103 104 var st syscall.Statfs_t 105 err := syscall.Statfs(cgroupPath, &st) 106 if err != nil { 107 return "", err 108 } 109 isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC 110 if !isCgroupV2Enabled { 111 cgroupPath = filepath.Join(cgroupPath, "unified") 112 } 113 return cgroupPath, nil 114 } 115 116 func readLoop(rd *ringbuf.Reader) { 117 // bpfRttEvent is generated by bpf2go. 118 var event bpfRttEvent 119 for { 120 record, err := rd.Read() 121 if err != nil { 122 if errors.Is(err, ringbuf.ErrClosed) { 123 log.Println("received signal, exiting..") 124 return 125 } 126 log.Printf("reading from reader: %s", err) 127 continue 128 } 129 130 // Parse the ringbuf event entry into a bpfRttEvent structure. 131 if err := binary.Read(bytes.NewBuffer(record.RawSample), internal.NativeEndian, &event); err != nil { 132 log.Printf("parsing ringbuf event: %s", err) 133 continue 134 } 135 136 log.Printf("%-15s %-6d -> %-15s %-6d %-6d", 137 intToIP(event.Saddr), 138 event.Sport, 139 intToIP(event.Daddr), 140 event.Dport, 141 event.Srtt, 142 ) 143 } 144 } 145 146 // intToIP converts IPv4 number to net.IP 147 func intToIP(ipNum uint32) net.IP { 148 ip := make(net.IP, 4) 149 binary.BigEndian.PutUint32(ip, ipNum) 150 return ip 151 }