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  }