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

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/signal"
     7  	"time"
     8  
     9  	"github.com/dylandreimerink/gobpfld"
    10  	"github.com/dylandreimerink/gobpfld/bpftypes"
    11  	"github.com/dylandreimerink/gobpfld/ebpf"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  // This example command executes the same program as xdp_stats which is basic03-map-counter from
    16  // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter
    17  // However, this example demonstrates manually recreating the program from eBPF instructions which
    18  // have been annotated as a bonus lesson in eBPF instructions.
    19  // The same output can be generated by a program using gobpflb to generate dynamic eBPF programs
    20  
    21  func main() {
    22  	program := &gobpfld.ProgramXDP{
    23  		AbstractBPFProgram: gobpfld.AbstractBPFProgram{
    24  			Name:        gobpfld.MustNewObjName("xdp_stats1"),
    25  			ProgramType: bpftypes.BPF_PROG_TYPE_XDP,
    26  			License:     "GPL",
    27  			Maps: map[string]gobpfld.BPFMap{
    28  				"xdp_stats_map": &gobpfld.ArrayMap{
    29  					AbstractMap: gobpfld.AbstractMap{
    30  						Name: gobpfld.MustNewObjName("xdp_stats_map"),
    31  						Definition: gobpfld.BPFMapDef{
    32  							Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
    33  							KeySize:    4, // SizeOf(uint32)
    34  							ValueSize:  8, // SizeOf(uint64)
    35  							MaxEntries: 5,
    36  						},
    37  					},
    38  				},
    39  			},
    40  			MapFDLocations: map[string][]uint64{
    41  				"xdp_stats_map": {
    42  					// LoadConstant64bit is the 5th instruction in this program. So the first byte of the
    43  					// 5th instruction is the width of a instruction * 4 to skip the first 4 instructions
    44  					uint64(ebpf.BPFInstSize) * 4,
    45  				},
    46  			},
    47  			Instructions: ebpf.MustEncode([]ebpf.Instruction{
    48  				////
    49  				// __u32 key = XDP_PASS;
    50  				////
    51  				&ebpf.Mov64{ // r1 = 2
    52  					Dest:  ebpf.BPF_REG_1,
    53  					Value: ebpf.XDP_PASS,
    54  				},
    55  				&ebpf.StoreMemoryRegister{ // *(u32 *)(r10 - 4) = r1
    56  					Size:   ebpf.BPF_W,      // 32bits
    57  					Dest:   ebpf.BPF_REG_10, // R10 = Frame pointer(end of stack)
    58  					Offset: -4,              // 4x8 = 32
    59  					Src:    ebpf.BPF_REG_1,
    60  				},
    61  
    62  				////
    63  				// rec = bpf_map_lookup_elem(&xdp_stats_map, &key);
    64  				////
    65  				&ebpf.Mov64Register{ // r2 = r10
    66  					Dest: ebpf.BPF_REG_2,
    67  					Src:  ebpf.BPF_REG_10,
    68  				},
    69  				// make R2 a pointer to key, aka &key
    70  				&ebpf.Add64{ // r2 += -4
    71  					Dest:  ebpf.BPF_REG_2,
    72  					Value: -4,
    73  				},
    74  				// The constant value to be loaded here will be inserted by the loader
    75  				//  it will become a pointer to the map, aka &xdp_stats_map
    76  				&ebpf.LoadConstant64bit{ // r1 = 0 ll
    77  					Dest: ebpf.BPF_REG_1,
    78  				},
    79  				&ebpf.Nop{}, // Nop is a dummy since LoadConstant64bit consumes 2 instructions to fit 64bits
    80  				// The actual function call which takes R1 and R2 as arguments and uses R0 as return value
    81  				//  where R1 is the pointer to the map in which the lookup should occur
    82  				//  and where R2 is the pointer to the key to use
    83  				//  R0 will become a pointer to the value
    84  				&ebpf.CallHelper{ // call 1
    85  					Function: 1, // 1 = bpf_map_lookup_elem
    86  				},
    87  
    88  				////
    89  				// if (!rec) return XDP_ABORTED;
    90  				////
    91  				&ebpf.Mov64{ // r1 = 0
    92  					Dest:  ebpf.BPF_REG_1,
    93  					Value: ebpf.XDP_ABORTED, // XDP_ABORTED = 0
    94  				},
    95  				// check if the return value is a null pointer, aka: if (!rec)
    96  				&ebpf.JumpEqual{ // if r0 == 0: goto pc+3 <LBL0>
    97  					Dest:   ebpf.BPF_REG_0,
    98  					Offset: 3, //
    99  					Value:  0,
   100  				},
   101  
   102  				////
   103  				// lock_xadd(&rec->rx_packets, 1);
   104  				////
   105  				&ebpf.Mov64{ // r1 = 1
   106  					Dest:  ebpf.BPF_REG_1,
   107  					Value: 1,
   108  				},
   109  				&ebpf.AtomicAdd{ // lock *(u64 *)(r0 + 0) += r1
   110  					Size: ebpf.BPF_DW,
   111  					Dest: ebpf.BPF_REG_0, // R0 contains the pointer to the value in the map
   112  					Src:  ebpf.BPF_REG_1,
   113  				},
   114  
   115  				////
   116  				// return XDP_PASS;
   117  				////
   118  				&ebpf.Mov64{ // r1 = 2
   119  					Dest:  ebpf.BPF_REG_1,
   120  					Value: ebpf.XDP_PASS, // XDP_PASS = 2
   121  				},
   122  
   123  				////
   124  				// Label LBL0, target of JumpEqual
   125  				////
   126  				&ebpf.Mov64Register{ // r0 = r1
   127  					Dest: ebpf.BPF_REG_0, // R0 is the return value of this program
   128  					Src:  ebpf.BPF_REG_1, // R1 is XDP_PASS or XDP_ABORTED depending on branching
   129  				},
   130  				&ebpf.Exit{}, // exit
   131  			}),
   132  		},
   133  	}
   134  
   135  	// All maps loaded from elf files are BPFGenericMaps
   136  	statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap)
   137  
   138  	log, err := program.Load(gobpfld.ProgXDPLoadOpts{
   139  		VerifierLogLevel: bpftypes.BPFLogLevelBasic,
   140  	})
   141  
   142  	fmt.Printf("BPF Verifier log:\n%s\n", log)
   143  
   144  	if err != nil {
   145  		program.DecodeToReader(os.Stdout)
   146  		fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error())
   147  		os.Exit(1)
   148  	}
   149  
   150  	sigChan := make(chan os.Signal, 1)
   151  	signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT)
   152  
   153  	err = program.Attach(gobpfld.ProgXDPAttachOpts{
   154  		InterfaceName: "lo",
   155  		Replace:       true,
   156  	})
   157  
   158  	if err != nil {
   159  		fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error())
   160  		os.Exit(1)
   161  	}
   162  
   163  	ticker := time.Tick(1 * time.Second)
   164  	for {
   165  		select {
   166  		case <-ticker:
   167  			// The key is 2 since the program puts stats in the XDP_PASS key which has value 2
   168  			// Tho this is specific to the XDP program we are using as an example.
   169  			key := uint32(2)
   170  			var value int64
   171  
   172  			err = statsMap.Get(key, &value)
   173  			if err != nil {
   174  				fmt.Fprintf(os.Stderr, "error while getting stats from map: %s\n", err.Error())
   175  				os.Exit(1)
   176  			}
   177  
   178  			fmt.Printf("%d packets were processed\n", value)
   179  
   180  		case <-sigChan:
   181  			fmt.Println("Detaching XPD program and stopping")
   182  
   183  			err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{
   184  				All: true,
   185  			})
   186  			if err != nil {
   187  				fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error())
   188  				os.Exit(1)
   189  			}
   190  
   191  			os.Exit(0)
   192  		}
   193  	}
   194  }