github.com/cilium/cilium@v1.16.2/pkg/maps/lxcmap/lxcmap.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package lxcmap 5 6 import ( 7 "fmt" 8 "net" 9 "net/netip" 10 "sync" 11 12 "github.com/cilium/ebpf" 13 14 "github.com/cilium/cilium/pkg/bpf" 15 "github.com/cilium/cilium/pkg/identity" 16 "github.com/cilium/cilium/pkg/mac" 17 "github.com/cilium/cilium/pkg/option" 18 ) 19 20 const ( 21 MapName = "cilium_lxc" 22 23 // MaxEntries represents the maximum number of endpoints in the map 24 MaxEntries = 65535 25 26 // PortMapMax represents the maximum number of Ports Mapping per container. 27 PortMapMax = 16 28 ) 29 30 var ( 31 // LXCMap represents the BPF map for endpoints 32 lxcMap *bpf.Map 33 lxcMapOnce sync.Once 34 ) 35 36 func LXCMap() *bpf.Map { 37 lxcMapOnce.Do(func() { 38 lxcMap = bpf.NewMap(MapName, 39 ebpf.Hash, 40 &EndpointKey{}, 41 &EndpointInfo{}, 42 MaxEntries, 43 0, 44 ).WithCache().WithPressureMetric(). 45 WithEvents(option.Config.GetEventBufferConfig(MapName)) 46 }) 47 return lxcMap 48 } 49 50 const ( 51 // EndpointFlagHost indicates that this endpoint represents the host 52 EndpointFlagHost = 1 53 ) 54 55 // EndpointFrontend is the interface to implement for an object to synchronize 56 // with the endpoint BPF map. 57 type EndpointFrontend interface { 58 LXCMac() mac.MAC 59 GetNodeMAC() mac.MAC 60 GetIfIndex() int 61 GetID() uint64 62 IPv4Address() netip.Addr 63 IPv6Address() netip.Addr 64 GetIdentity() identity.NumericIdentity 65 } 66 67 // GetBPFKeys returns all keys which should represent this endpoint in the BPF 68 // endpoints map 69 func GetBPFKeys(e EndpointFrontend) []*EndpointKey { 70 keys := []*EndpointKey{} 71 if e.IPv6Address().IsValid() { 72 keys = append(keys, NewEndpointKey(e.IPv6Address().AsSlice())) 73 } 74 75 if e.IPv4Address().IsValid() { 76 keys = append(keys, NewEndpointKey(e.IPv4Address().AsSlice())) 77 } 78 79 return keys 80 } 81 82 // GetBPFValue returns the value which should represent this endpoint in the 83 // BPF endpoints map 84 // Must only be called if init() succeeded. 85 func GetBPFValue(e EndpointFrontend) (*EndpointInfo, error) { 86 tmp := e.LXCMac() 87 mac, err := tmp.Uint64() 88 if len(tmp) > 0 && err != nil { 89 return nil, fmt.Errorf("invalid LXC MAC: %w", err) 90 } 91 92 tmp = e.GetNodeMAC() 93 nodeMAC, err := tmp.Uint64() 94 if len(tmp) > 0 && err != nil { 95 return nil, fmt.Errorf("invalid node MAC: %w", err) 96 } 97 98 // Both lxc and node mac can be nil for the case of L3/NOARP devices. 99 info := &EndpointInfo{ 100 IfIndex: uint32(e.GetIfIndex()), 101 LxcID: uint16(e.GetID()), 102 MAC: mac, 103 NodeMAC: nodeMAC, 104 SecID: e.GetIdentity().Uint32(), // Host byte-order 105 } 106 107 return info, nil 108 109 } 110 111 type pad3uint32 [3]uint32 112 113 // EndpointInfo represents the value of the endpoints BPF map. 114 // 115 // Must be in sync with struct endpoint_info in <bpf/lib/common.h> 116 type EndpointInfo struct { 117 IfIndex uint32 `align:"ifindex"` 118 Unused uint16 `align:"unused"` 119 LxcID uint16 `align:"lxc_id"` 120 Flags uint32 `align:"flags"` 121 // go alignment 122 _ uint32 123 MAC mac.Uint64MAC `align:"mac"` 124 NodeMAC mac.Uint64MAC `align:"node_mac"` 125 SecID uint32 `align:"sec_id"` 126 Pad pad3uint32 `align:"pad"` 127 } 128 129 type EndpointKey struct { 130 bpf.EndpointKey 131 } 132 133 // NewEndpointKey returns an EndpointKey based on the provided IP address. The 134 // address family is automatically detected 135 func NewEndpointKey(ip net.IP) *EndpointKey { 136 return &EndpointKey{ 137 EndpointKey: bpf.NewEndpointKey(ip, 0), 138 } 139 } 140 141 func (k *EndpointKey) New() bpf.MapKey { return &EndpointKey{} } 142 143 // IsHost returns true if the EndpointInfo represents a host IP 144 func (v *EndpointInfo) IsHost() bool { 145 return v.Flags&EndpointFlagHost != 0 146 } 147 148 // String returns the human readable representation of an EndpointInfo 149 func (v *EndpointInfo) String() string { 150 if v.Flags&EndpointFlagHost != 0 { 151 return "(localhost)" 152 } 153 154 return fmt.Sprintf("id=%-5d sec_id=%-5d flags=0x%04X ifindex=%-3d mac=%s nodemac=%s", 155 v.LxcID, 156 v.SecID, 157 v.Flags, 158 v.IfIndex, 159 v.MAC, 160 v.NodeMAC, 161 ) 162 } 163 164 func (v *EndpointInfo) New() bpf.MapValue { return &EndpointInfo{} } 165 166 // WriteEndpoint updates the BPF map with the endpoint information and links 167 // the endpoint information to all keys provided. 168 func WriteEndpoint(f EndpointFrontend) error { 169 info, err := GetBPFValue(f) 170 if err != nil { 171 return err 172 } 173 174 // FIXME: Revert on failure 175 for _, v := range GetBPFKeys(f) { 176 if err := LXCMap().Update(v, info); err != nil { 177 return err 178 } 179 } 180 181 return nil 182 } 183 184 // AddHostEntry adds a special endpoint which represents the local host 185 func AddHostEntry(ip net.IP) error { 186 key := NewEndpointKey(ip) 187 ep := &EndpointInfo{Flags: EndpointFlagHost} 188 return LXCMap().Update(key, ep) 189 } 190 191 // SyncHostEntry checks if a host entry exists in the lxcmap and adds one if needed. 192 // Returns boolean indicating if a new entry was added and an error. 193 func SyncHostEntry(ip net.IP) (bool, error) { 194 key := NewEndpointKey(ip) 195 value, err := LXCMap().Lookup(key) 196 if err != nil || value.(*EndpointInfo).Flags&EndpointFlagHost == 0 { 197 err = AddHostEntry(ip) 198 if err == nil { 199 return true, nil 200 } 201 } 202 return false, err 203 } 204 205 // DeleteEntry deletes a single map entry 206 func DeleteEntry(ip net.IP) error { 207 return LXCMap().Delete(NewEndpointKey(ip)) 208 } 209 210 // DeleteElement deletes the endpoint using all keys which represent the 211 // endpoint. It returns the number of errors encountered during deletion. 212 func DeleteElement(f EndpointFrontend) []error { 213 var errors []error 214 for _, k := range GetBPFKeys(f) { 215 if err := LXCMap().Delete(k); err != nil { 216 errors = append(errors, fmt.Errorf("Unable to delete key %v from %s: %w", k, bpf.MapPath(MapName), err)) 217 } 218 } 219 220 return errors 221 } 222 223 // DumpToMap dumps the contents of the lxcmap into a map and returns it 224 func DumpToMap() (map[string]EndpointInfo, error) { 225 m := map[string]EndpointInfo{} 226 callback := func(key bpf.MapKey, value bpf.MapValue) { 227 if info, ok := value.(*EndpointInfo); ok { 228 if endpointKey, ok := key.(*EndpointKey); ok { 229 m[endpointKey.ToIP().String()] = *info 230 } 231 } 232 } 233 234 if err := LXCMap().DumpWithCallback(callback); err != nil { 235 return nil, fmt.Errorf("unable to read BPF endpoint list: %w", err) 236 } 237 238 return m, nil 239 }