github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/bpf_to_bpf/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "embed" 6 "fmt" 7 "os" 8 "os/signal" 9 "runtime" 10 "syscall" 11 "time" 12 "unsafe" 13 14 "github.com/dylandreimerink/gobpfld" 15 "github.com/dylandreimerink/gobpfld/bpfsys" 16 "github.com/dylandreimerink/gobpfld/bpftypes" 17 "golang.org/x/sys/unix" 18 ) 19 20 //go:embed src/xdp 21 var f embed.FS 22 23 func main() { 24 elfFileBytes, err := f.ReadFile("src/xdp") 25 if err != nil { 26 fmt.Fprintf(os.Stderr, "error opening ELF file: %s\n", err.Error()) 27 os.Exit(1) 28 } 29 30 elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{}) 31 if err != nil { 32 fmt.Fprintf(os.Stderr, "error while reading ELF file: %s\n", err.Error()) 33 os.Exit(1) 34 } 35 36 program := elf.Programs["firewall_prog"].(*gobpfld.ProgramXDP) 37 38 log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 39 VerifierLogLevel: bpftypes.BPFLogLevelVerbose, 40 }) 41 42 fmt.Printf("BPF Verifier log:\n%s\n", log) 43 44 if err != nil { 45 fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error()) 46 os.Exit(1) 47 } 48 49 sigChan := make(chan os.Signal, 1) 50 signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT) 51 52 err = program.Attach(gobpfld.ProgXDPAttachOpts{ 53 InterfaceName: "lo", 54 Replace: true, 55 }) 56 57 if err != nil { 58 fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error()) 59 os.Exit(1) 60 } 61 62 // We need to know how many CPU's this machine has to size our value buffers correctly 63 // runtime.NumCPU returns the usable number of CPU's for this process. 64 // This number can be different from the number available to the kernel if this process 65 // has custom CPU affinity / scheduling. To avoid this the /proc/cpuinfo "file" should be 66 // parsed which seems the most reliable method for CPU count detection. 67 // But this is not (yet) included in gobpfld. 68 numCPUs := runtime.NumCPU() 69 70 ipStats := program.Maps["ip_proto_stats"].(*gobpfld.HashMap) 71 tcpStats := program.Maps["tcp_stats"].(*gobpfld.HashMap) 72 udpStats := program.Maps["udp_stats"].(*gobpfld.HashMap) 73 74 type trafficStats struct { 75 packets uint64 76 bytes uint64 77 } 78 79 ticker := time.Tick(1 * time.Second) 80 for { 81 select { 82 case <-ticker: 83 fmt.Println("-------------------------") 84 fmt.Println("IP Proto stats:") 85 86 var protoNum uint8 87 stats := make([]trafficStats, numCPUs) 88 89 attr := &bpfsys.BPFAttrMapElem{ 90 MapFD: ipStats.GetFD(), 91 Value_NextKey: uintptr(unsafe.Pointer(&protoNum)), 92 } 93 94 for { 95 err := bpfsys.MapGetNextKey(attr) 96 if err != nil { 97 syserr, ok := err.(*bpfsys.BPFSyscallError) 98 if !ok || syserr.Errno != syscall.ENOENT { 99 fmt.Fprintf(os.Stderr, "error while getting next key from ip_proto_stats: %s\n", err.Error()) 100 close(sigChan) 101 } 102 103 // 104 break 105 } 106 107 attr.Key = attr.Value_NextKey 108 109 err = ipStats.Get(&protoNum, &stats) 110 if err != nil { 111 fmt.Fprintf(os.Stderr, "error while getting value from ip_proto_stats: %s\n", err.Error()) 112 close(sigChan) 113 break 114 } 115 116 var sum trafficStats 117 for _, row := range stats { 118 sum.bytes += row.bytes 119 sum.packets += row.packets 120 } 121 122 fmt.Printf(" %d: pkts: %d, bytes: %d\n", protoNum, sum.packets, sum.bytes) 123 } 124 125 fmt.Println("TCP Proto stats:") 126 127 var tcpPort uint16 128 attr = &bpfsys.BPFAttrMapElem{ 129 MapFD: tcpStats.GetFD(), 130 Value_NextKey: uintptr(unsafe.Pointer(&tcpPort)), 131 } 132 133 for { 134 err := bpfsys.MapGetNextKey(attr) 135 if err != nil { 136 syserr, ok := err.(*bpfsys.BPFSyscallError) 137 if !ok || syserr.Errno != syscall.ENOENT { 138 fmt.Fprintf(os.Stderr, "error while getting next key from tcp_stats: %s\n", err.Error()) 139 close(sigChan) 140 } 141 142 // 143 break 144 } 145 146 attr.Key = attr.Value_NextKey 147 148 err = tcpStats.Get(&tcpPort, &stats) 149 if err != nil { 150 fmt.Fprintf(os.Stderr, "error while getting value from tcp_stats: %s\n", err.Error()) 151 close(sigChan) 152 break 153 } 154 155 var sum trafficStats 156 for _, row := range stats { 157 sum.bytes += row.bytes 158 sum.packets += row.packets 159 } 160 161 fmt.Printf(" %d: pkts: %d, bytes: %d\n", tcpPort, sum.packets, sum.bytes) 162 } 163 164 fmt.Println("UDP Proto stats:") 165 166 var udpPort uint16 167 attr = &bpfsys.BPFAttrMapElem{ 168 MapFD: udpStats.GetFD(), 169 Value_NextKey: uintptr(unsafe.Pointer(&udpPort)), 170 } 171 172 for { 173 err := bpfsys.MapGetNextKey(attr) 174 if err != nil { 175 syserr, ok := err.(*bpfsys.BPFSyscallError) 176 if !ok || syserr.Errno != syscall.ENOENT { 177 fmt.Fprintf(os.Stderr, "error while getting next key from udp_stats: %s\n", err.Error()) 178 close(sigChan) 179 } 180 181 // 182 break 183 } 184 185 attr.Key = attr.Value_NextKey 186 187 err = udpStats.Get(&udpPort, &stats) 188 if err != nil { 189 fmt.Fprintf(os.Stderr, "error while getting value from udp_stats: %s\n", err.Error()) 190 close(sigChan) 191 break 192 } 193 194 var sum trafficStats 195 for _, row := range stats { 196 sum.bytes += row.bytes 197 sum.packets += row.packets 198 } 199 200 fmt.Printf(" %d: pkts: %d, bytes: %d\n", udpPort, sum.packets, sum.bytes) 201 } 202 203 case <-sigChan: 204 fmt.Println("Detaching XPD program and stopping") 205 206 err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{ 207 All: true, 208 }) 209 if err != nil { 210 fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error()) 211 os.Exit(1) 212 } 213 214 os.Exit(0) 215 } 216 } 217 }