github.com/cilium/cilium@v1.16.2/pkg/maps/lbmap/maglev_outer_map.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package lbmap
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"unsafe"
    10  
    11  	"github.com/cilium/cilium/pkg/byteorder"
    12  	"github.com/cilium/cilium/pkg/ebpf"
    13  )
    14  
    15  // MaglevOuterMap represents a Maglev outer map.
    16  type MaglevOuterMap struct {
    17  	*ebpf.Map
    18  }
    19  
    20  // UpdateService sets the given inner map to be the Maglev lookup table for
    21  // the service with the given id.
    22  func (m *MaglevOuterMap) UpdateService(id uint16, inner *MaglevInnerMap) error {
    23  	key := MaglevOuterKey{RevNatID: id}.toNetwork()
    24  	val := MaglevOuterVal{FD: uint32(inner.FD())}
    25  	return m.Map.Update(key, val, 0)
    26  }
    27  
    28  // MaglevOuterKey is the key of a maglev outer map.
    29  type MaglevOuterKey struct {
    30  	RevNatID uint16
    31  }
    32  
    33  // toNetwork converts a maglev outer map's key to network byte order.
    34  // The key is in network byte order in the eBPF maps.
    35  func (k MaglevOuterKey) toNetwork() MaglevOuterKey {
    36  	return MaglevOuterKey{
    37  		RevNatID: byteorder.HostToNetwork16(k.RevNatID),
    38  	}
    39  }
    40  
    41  // MaglevOuterVal is the value of a maglev outer map.
    42  type MaglevOuterVal struct {
    43  	FD uint32
    44  }
    45  
    46  // NewMaglevOuterMap returns a new object representing a maglev outer map.
    47  func NewMaglevOuterMap(name string, maxEntries int, tableSize uint32, innerMap *ebpf.MapSpec) (*MaglevOuterMap, error) {
    48  	m := ebpf.NewMap(&ebpf.MapSpec{
    49  		Name:       name,
    50  		Type:       ebpf.HashOfMaps,
    51  		KeySize:    uint32(unsafe.Sizeof(MaglevOuterKey{})),
    52  		ValueSize:  uint32(unsafe.Sizeof(MaglevOuterVal{})),
    53  		MaxEntries: uint32(maxEntries),
    54  		InnerMap:   innerMap,
    55  		Pinning:    ebpf.PinByName,
    56  	})
    57  
    58  	if err := m.OpenOrCreate(); err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	return &MaglevOuterMap{m}, nil
    63  }
    64  
    65  // OpenMaglevOuterMap opens an existing pinned maglev outer map and returns an
    66  // object representing it.
    67  func OpenMaglevOuterMap(name string) (*MaglevOuterMap, error) {
    68  	m, err := ebpf.LoadRegisterMap(name)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return &MaglevOuterMap{m}, nil
    74  }
    75  
    76  // TableSize tries to determine the table size of the Maglev map.
    77  // It does so by opening the first-available service's inner map and reading
    78  // its size. For this to work, at least one service entry must be available.
    79  func (m *MaglevOuterMap) TableSize() (uint32, error) {
    80  	var firstKey MaglevOuterKey
    81  	if err := m.NextKey(nil, &firstKey); err != nil {
    82  		// The outer map exists but it's empty.
    83  		return 0, fmt.Errorf("getting first key: %w", err)
    84  	}
    85  
    86  	var firstVal MaglevOuterVal
    87  	if err := m.Lookup(&firstKey, &firstVal); err != nil {
    88  		// The outer map exists but we can't read the first entry.
    89  		return 0, fmt.Errorf("getting first value: %w", err)
    90  	}
    91  
    92  	inner, err := MaglevInnerMapFromID(firstVal.FD)
    93  	if err != nil {
    94  		// The outer map exists but we can't access the inner map
    95  		// associated with the first entry.
    96  		return 0, fmt.Errorf("opening first inner map: %w", err)
    97  	}
    98  	defer inner.Close()
    99  
   100  	return inner.TableSize(), nil
   101  }
   102  
   103  // GetService gets the maglev backend lookup table for the given service id.
   104  func (m *MaglevOuterMap) GetService(id uint16) (*MaglevInnerMap, error) {
   105  	key := MaglevOuterKey{RevNatID: id}.toNetwork()
   106  	var val MaglevOuterVal
   107  
   108  	err := m.Lookup(key, &val)
   109  	if errors.Is(err, ebpf.ErrKeyNotExist) {
   110  		return nil, fmt.Errorf("no maglev table entry for service id %d: %w", id, err)
   111  	}
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	inner, err := MaglevInnerMapFromID(val.FD)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("cannot open inner map with id %d: %w", val.FD, err)
   119  	}
   120  
   121  	return inner, nil
   122  }
   123  
   124  // DumpBackends iterates through all of the Maglev map's entries,
   125  // opening each entry's inner map, and dumps their contents in a format
   126  // expected by Cilium's table printer.
   127  func (m *MaglevOuterMap) DumpBackends(ipv6 bool) (map[string][]string, error) {
   128  	out := make(map[string][]string)
   129  
   130  	var key MaglevOuterKey
   131  	var val MaglevOuterVal
   132  	which := "v4"
   133  	if ipv6 {
   134  		which = "v6"
   135  	}
   136  	iter := m.Iterate()
   137  	for iter.Next(&key, &val) {
   138  		inner, err := MaglevInnerMapFromID(val.FD)
   139  		if err != nil {
   140  			return nil, fmt.Errorf("cannot open inner map with id %d: %w", val.FD, err)
   141  		}
   142  		defer inner.Close()
   143  
   144  		backends, err := inner.DumpBackends()
   145  		if err != nil {
   146  			return nil, fmt.Errorf("dumping inner map id %d: %w", val.FD, err)
   147  		}
   148  
   149  		// The service ID is read from the map in network byte order,
   150  		// convert to host byte order before displaying to the user.
   151  		key.RevNatID = byteorder.NetworkToHost16(key.RevNatID)
   152  
   153  		out[fmt.Sprintf("[%d]/%s", key.RevNatID, which)] = []string{backends}
   154  	}
   155  
   156  	return out, nil
   157  }