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 }