github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/map_xsk.go (about)

     1  package gobpfld
     2  
     3  import (
     4  	"fmt"
     5  	"unsafe"
     6  
     7  	"github.com/dylandreimerink/gobpfld/bpfsys"
     8  	"github.com/dylandreimerink/gobpfld/bpftypes"
     9  )
    10  
    11  var _ BPFMap = (*XSKMap)(nil)
    12  
    13  // XSKMap is a specialized map type designed to work in conjunction with XSKSocket's.
    14  type XSKMap struct {
    15  	AbstractMap
    16  
    17  	// we record the pointers to the XSKSockets seperatly since the kernel doesn't
    18  	// allow us to 'get' from the map after setting values
    19  	userspaceMap map[uint32]*XSKSocket
    20  }
    21  
    22  func (m *XSKMap) Load() error {
    23  	if m.Definition.Type != bpftypes.BPF_MAP_TYPE_XSKMAP {
    24  		return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_XSKMAP when using an XSKMap")
    25  	}
    26  
    27  	m.userspaceMap = make(map[uint32]*XSKSocket)
    28  	err := m.load(nil)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	err = mapRegister.add(m)
    34  	if err != nil {
    35  		return fmt.Errorf("map register: %w", err)
    36  	}
    37  
    38  	return nil
    39  }
    40  
    41  // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel
    42  // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map.
    43  func (m *XSKMap) Close() error {
    44  	err := mapRegister.delete(m)
    45  	if err != nil {
    46  		return fmt.Errorf("map register: %w", err)
    47  	}
    48  
    49  	return m.close()
    50  }
    51  
    52  // Get performs a lookup in the xskmap based on the key and returns the file descriptor of the socket
    53  func (m *XSKMap) Get(key uint32) (*XSKSocket, error) {
    54  	if !m.loaded {
    55  		return nil, fmt.Errorf("can't read from an unloaded map")
    56  	}
    57  
    58  	return m.userspaceMap[key], nil
    59  }
    60  
    61  func (m *XSKMap) Set(key uint32, value *XSKSocket) error {
    62  	if !m.loaded {
    63  		return fmt.Errorf("can't write to an unloaded map")
    64  	}
    65  
    66  	if value == nil {
    67  		return fmt.Errorf("can't write a nil socket to the map")
    68  	}
    69  
    70  	attr := &bpfsys.BPFAttrMapElem{
    71  		MapFD:         m.fd,
    72  		Key:           uintptr(unsafe.Pointer(&key)),
    73  		Value_NextKey: uintptr(unsafe.Pointer(&value.fd)),
    74  	}
    75  
    76  	err := bpfsys.MapUpdateElem(attr)
    77  	if err != nil {
    78  		return fmt.Errorf("bpf syscall error: %w", err)
    79  	}
    80  
    81  	m.userspaceMap[key] = value
    82  
    83  	return nil
    84  }
    85  
    86  func (m *XSKMap) Delete(key uint32) error {
    87  	if !m.loaded {
    88  		return fmt.Errorf("can't delete elements in an unloaded map")
    89  	}
    90  
    91  	attr := &bpfsys.BPFAttrMapElem{
    92  		MapFD: m.fd,
    93  		Key:   uintptr(unsafe.Pointer(&key)),
    94  	}
    95  
    96  	err := bpfsys.MapDeleteElem(attr)
    97  	if err != nil {
    98  		return fmt.Errorf("bpf syscall error: %w", err)
    99  	}
   100  
   101  	delete(m.userspaceMap, key)
   102  
   103  	return nil
   104  }
   105  
   106  func (m *XSKMap) Iterator() MapIterator {
   107  	return &XSKIterator{
   108  		xskMap: m,
   109  	}
   110  }
   111  
   112  type XSKIterator struct {
   113  	xskMap *XSKMap
   114  
   115  	key   *uint32
   116  	value **XSKSocket
   117  
   118  	keyArray []uint32
   119  	index    int
   120  
   121  	done bool
   122  }
   123  
   124  func (xi *XSKIterator) Init(key, value interface{}) error {
   125  	var ok bool
   126  	xi.key, ok = key.(*uint32)
   127  	if !ok {
   128  		return fmt.Errorf("key must be a pointer to a uint32")
   129  	}
   130  
   131  	xi.value, ok = value.(**XSKSocket)
   132  	if !ok {
   133  		return fmt.Errorf("key must be a double pointer to a gobpfld.XSKSocket")
   134  	}
   135  
   136  	for key := range xi.xskMap.userspaceMap {
   137  		xi.keyArray = append(xi.keyArray, key)
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  // Next gets the key and value at the current location and writes them to the pointers given to the iterator
   144  // during initialization. It then advances the internal pointer to the next key and value.
   145  // If the iterator can't get the key and value at the current location since we are done iterating or an error
   146  // was encountered 'updated' is false.
   147  func (xi *XSKIterator) Next() (updated bool, err error) {
   148  	// TODO change iterator so we do use the next_key syscall but lookup the value using the userspace map
   149  	if xi.done {
   150  		return false, fmt.Errorf("iterator is done")
   151  	}
   152  
   153  	if len(xi.keyArray) == 0 {
   154  		xi.done = true
   155  		return false, nil
   156  	}
   157  
   158  	if xi.index >= len(xi.keyArray) {
   159  		xi.done = true
   160  		return false, nil
   161  	}
   162  
   163  	key := xi.keyArray[uint32(xi.index)]
   164  	value := xi.xskMap.userspaceMap[key]
   165  
   166  	*xi.key = key
   167  	*xi.value = value
   168  
   169  	xi.index++
   170  
   171  	return true, nil
   172  }