github.com/cilium/cilium@v1.16.2/pkg/maps/lbmap/maglev.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  	"os"
    10  
    11  	"github.com/cilium/cilium/pkg/bpf"
    12  	"github.com/cilium/cilium/pkg/ebpf"
    13  	"github.com/cilium/cilium/pkg/loadbalancer"
    14  )
    15  
    16  const (
    17  	// Both outer maps are pinned though given we need to insert
    18  	// inner maps into them.
    19  	MaglevOuter4MapName = "cilium_lb4_maglev"
    20  	MaglevOuter6MapName = "cilium_lb6_maglev"
    21  )
    22  
    23  var (
    24  	maglevOuter4Map     *MaglevOuterMap
    25  	maglevOuter6Map     *MaglevOuterMap
    26  	maglevRecreatedIPv4 bool
    27  	maglevRecreatedIPv6 bool
    28  	maglevTableSize     uint32
    29  )
    30  
    31  // InitMaglevMaps inits the ipv4 and/or ipv6 maglev outer and inner maps.
    32  func InitMaglevMaps(ipv4, ipv6 bool, tableSize uint32) error {
    33  	// Always try to delete old maps with the wrong M parameter, otherwise
    34  	// we may end up in a case where there are 2 maps (one for IPv4 and
    35  	// one for IPv6), one of which is not used, with 2 different table
    36  	// sizes.
    37  	// This would confuse the MaybeInitMaglevMaps() function, which would
    38  	// not be able to figure out the correct table size.
    39  	r, err := deleteMapIfMNotMatch(MaglevOuter4MapName, tableSize)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	maglevRecreatedIPv4 = r
    44  
    45  	r, err = deleteMapIfMNotMatch(MaglevOuter6MapName, tableSize)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	maglevRecreatedIPv6 = r
    50  
    51  	dummyInnerMapSpec := newMaglevInnerMapSpec(tableSize)
    52  	if ipv4 {
    53  		outer, err := NewMaglevOuterMap(MaglevOuter4MapName, MaglevMapMaxEntries, tableSize, dummyInnerMapSpec)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		maglevOuter4Map = outer
    58  	}
    59  
    60  	if ipv6 {
    61  		outer, err := NewMaglevOuterMap(MaglevOuter6MapName, MaglevMapMaxEntries, tableSize, dummyInnerMapSpec)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		maglevOuter6Map = outer
    66  	}
    67  
    68  	maglevTableSize = tableSize
    69  
    70  	return nil
    71  }
    72  
    73  // deleteMapIfMNotMatch removes the outer maglev maps if it's a legacy map or
    74  // the M param (MaglevTableSize) has changed. This is to avoid a verifier
    75  // error when loading BPF programs which access the map.
    76  func deleteMapIfMNotMatch(mapName string, tableSize uint32) (bool, error) {
    77  	m, err := ebpf.LoadPinnedMap(bpf.MapPath(mapName))
    78  	if errors.Is(err, os.ErrNotExist) {
    79  		// No existing maglev outer map found.
    80  		// Return true so the caller will create a new one.
    81  		return true, nil
    82  	}
    83  	if err != nil {
    84  		return false, err
    85  	}
    86  	defer m.Close()
    87  
    88  	// Attempt to determine the outer map's table size.
    89  	size, err := (&MaglevOuterMap{Map: m}).TableSize()
    90  	if err == nil && size == tableSize {
    91  		// An outer map with the correct table size already exists.
    92  		// Return false as there no need to delete and recreate it.
    93  		return false, nil
    94  	}
    95  
    96  	// An outer map already exists but it has the wrong table size (or we
    97  	// can't determine it). Delete it.
    98  	if err := m.Unpin(); err != nil {
    99  		return false, fmt.Errorf("error unpinning existing outer map: %w", err)
   100  	}
   101  
   102  	return true, nil
   103  }
   104  
   105  // updateMaglevTable creates a new inner Maglev map containing the given backend IDs
   106  // and sets it as the active lookup table for the given service ID.
   107  func updateMaglevTable(ipv6 bool, revNATID uint16, backendIDs []loadbalancer.BackendID) error {
   108  	outer := maglevOuter4Map
   109  	if ipv6 {
   110  		outer = maglevOuter6Map
   111  	}
   112  
   113  	if outer == nil {
   114  		return errors.New("outer maglev maps not yet initialized")
   115  	}
   116  
   117  	inner, err := createMaglevInnerMap(maglevTableSize)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	defer inner.Close()
   122  
   123  	if err := inner.UpdateBackends(backendIDs); err != nil {
   124  		return fmt.Errorf("updating backends: %w", err)
   125  	}
   126  
   127  	if err := outer.UpdateService(revNATID, inner); err != nil {
   128  		return fmt.Errorf("updating service: %w", err)
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  // deleteMaglevTable deletes the inner Maglev lookup table for the given service ID.
   135  func deleteMaglevTable(ipv6 bool, revNATID uint16) error {
   136  	outerMap := maglevOuter4Map
   137  	if ipv6 {
   138  		outerMap = maglevOuter6Map
   139  	}
   140  
   141  	outerKey := MaglevOuterKey{RevNatID: revNATID}
   142  	if err := outerMap.Delete(outerKey.toNetwork()); err != nil {
   143  		return err
   144  	}
   145  
   146  	return nil
   147  }