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  }