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