github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/xdp_stats_assembly/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/signal"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/dylandreimerink/gobpfld"
    11  	"github.com/dylandreimerink/gobpfld/bpftypes"
    12  	"github.com/dylandreimerink/gobpfld/ebpf"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  // This example command executes the same program as xdp_stats which is basic03-map-counter from
    17  // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter
    18  // However, this example demonstrates manually recreating the program from eBPF assembly which
    19  // have been annotated as a bonus lesson. The assembly is then assembled into
    20  // eBPF instructions.
    21  // The same output can be generated by a program using gobpflb to generate dynamic eBPF programs
    22  
    23  func main() {
    24  	program := &gobpfld.ProgramXDP{
    25  		AbstractBPFProgram: gobpfld.AbstractBPFProgram{
    26  			Name:        gobpfld.MustNewObjName("xdp_stats1"),
    27  			ProgramType: bpftypes.BPF_PROG_TYPE_XDP,
    28  			License:     "GPL",
    29  			Maps: map[string]gobpfld.BPFMap{
    30  				"xdp_stats_map": &gobpfld.ArrayMap{
    31  					AbstractMap: gobpfld.AbstractMap{
    32  						Name: gobpfld.MustNewObjName("xdp_stats_map"),
    33  						Definition: gobpfld.BPFMapDef{
    34  							Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
    35  							KeySize:    4, // SizeOf(uint32)
    36  							ValueSize:  8, // SizeOf(uint64)
    37  							MaxEntries: 5,
    38  						},
    39  					},
    40  				},
    41  			},
    42  			MapFDLocations: map[string][]uint64{
    43  				"xdp_stats_map": {
    44  					// `r1 = 0 ll` is the 5th instruction in this program. So the first byte of the
    45  					// 5th instruction is the width of a instruction * 4 to skip the first 4 instructions
    46  					uint64(ebpf.BPFInstSize) * 4,
    47  				},
    48  			},
    49  		},
    50  	}
    51  
    52  	asm := `
    53  		# __u32 key = XDP_PASS;
    54   		r1 = 2
    55  		*(u32 *)(r10 - 4) = r1
    56  
    57  		# rec = bpf_map_lookup_elem(&xdp_stats_map, &key);
    58  		r2 = r10
    59  		r2 += -4
    60  		r1 = 0 ll
    61  		call 1
    62  		
    63  		# if (!rec) return XDP_ABORTED;
    64  		r1 = 0
    65  		if r0 == 0 goto return
    66  
    67  		# lock_xadd(&rec->rx_packets, 1);
    68  		r1 = 1
    69  		lock *(u64 *)(r0 + 0) += r1
    70  
    71  		# return XDP_PASS;
    72  		r1 = 2
    73  
    74  	return:
    75  		r0 = r1
    76  		exit		
    77  	`
    78  
    79  	inst, err := ebpf.AssemblyToInstructions("inline-asm", strings.NewReader(asm))
    80  	if err != nil {
    81  		panic(err)
    82  	}
    83  
    84  	program.Instructions = ebpf.MustEncode(inst)
    85  
    86  	// All maps loaded from elf files are BPFGenericMaps
    87  	statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap)
    88  
    89  	log, err := program.Load(gobpfld.ProgXDPLoadOpts{
    90  		VerifierLogLevel: bpftypes.BPFLogLevelBasic,
    91  	})
    92  
    93  	fmt.Printf("BPF Verifier log:\n%s\n", log)
    94  
    95  	if err != nil {
    96  		program.DecodeToReader(os.Stdout)
    97  		fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error())
    98  		os.Exit(1)
    99  	}
   100  
   101  	sigChan := make(chan os.Signal, 1)
   102  	signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT)
   103  
   104  	err = program.Attach(gobpfld.ProgXDPAttachOpts{
   105  		InterfaceName: "lo",
   106  		Replace:       true,
   107  	})
   108  
   109  	if err != nil {
   110  		fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error())
   111  		os.Exit(1)
   112  	}
   113  
   114  	ticker := time.Tick(1 * time.Second)
   115  	for {
   116  		select {
   117  		case <-ticker:
   118  			// The key is 2 since the program puts stats in the XDP_PASS key which has value 2
   119  			// Tho this is specific to the XDP program we are using as an example.
   120  			key := uint32(2)
   121  			var value int64
   122  
   123  			err = statsMap.Get(key, &value)
   124  			if err != nil {
   125  				fmt.Fprintf(os.Stderr, "error while getting stats from map: %s\n", err.Error())
   126  				os.Exit(1)
   127  			}
   128  
   129  			fmt.Printf("%d packets were processed\n", value)
   130  
   131  		case <-sigChan:
   132  			fmt.Println("Detaching XPD program and stopping")
   133  
   134  			err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{
   135  				All: true,
   136  			})
   137  			if err != nil {
   138  				fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error())
   139  				os.Exit(1)
   140  			}
   141  
   142  			os.Exit(0)
   143  		}
   144  	}
   145  }