github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/policymap/policymap.go (about)

     1  // Copyright 2016-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package policymap
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"syscall"
    21  	"unsafe"
    22  
    23  	"github.com/cilium/cilium/pkg/bpf"
    24  	"github.com/cilium/cilium/pkg/byteorder"
    25  	"github.com/cilium/cilium/pkg/logging"
    26  	"github.com/cilium/cilium/pkg/logging/logfields"
    27  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    28  	"github.com/cilium/cilium/pkg/u8proto"
    29  )
    30  
    31  const (
    32  	// CallMapName is the name of the map to do tail calls into policy
    33  	// enforcement programs
    34  	CallMapName = "cilium_policy"
    35  
    36  	// MapName is the prefix for endpoint-specific policy maps which map
    37  	// identity+ports+direction to whether the policy allows communication
    38  	// with that identity on that port for that direction.
    39  	MapName = CallMapName + "_"
    40  
    41  	// ProgArrayMaxEntries is the upper limit of entries in the program
    42  	// array for the tail calls to jump into the endpoint specific policy
    43  	// programs. This number *MUST* be identical to the maximum endponit ID.
    44  	ProgArrayMaxEntries = ^uint16(0)
    45  
    46  	// AllPorts is used to ignore the L4 ports in PolicyMap lookups; all ports
    47  	// are allowed. In the datapath, this is represented with the value 0 in the
    48  	// port field of map elements.
    49  	AllPorts = uint16(0)
    50  )
    51  
    52  var (
    53  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-policy")
    54  
    55  	// MaxEntries is the upper limit of entries in the per endpoint policy
    56  	// table
    57  	MaxEntries = 16384
    58  )
    59  
    60  type PolicyMap struct {
    61  	*bpf.Map
    62  }
    63  
    64  func (pe *PolicyEntry) String() string {
    65  	return fmt.Sprintf("%d %d %d", pe.ProxyPort, pe.Packets, pe.Bytes)
    66  }
    67  
    68  // PolicyKey represents a key in the BPF policy map for an endpoint. It must
    69  // match the layout of policy_key in bpf/lib/common.h.
    70  // +k8s:deepcopy-gen=true
    71  // +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapKey
    72  type PolicyKey struct {
    73  	Identity         uint32
    74  	DestPort         uint16 // In network byte-order
    75  	Nexthdr          uint8
    76  	TrafficDirection uint8
    77  }
    78  
    79  // PolicyEntry represents an entry in the BPF policy map for an endpoint. It must
    80  // match the layout of policy_entry in bpf/lib/common.h.
    81  // +k8s:deepcopy-gen=true
    82  // +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapValue
    83  type PolicyEntry struct {
    84  	ProxyPort uint16 // In network byte-order
    85  	Pad0      uint16
    86  	Pad1      uint16
    87  	Pad2      uint16
    88  	Packets   uint64
    89  	Bytes     uint64
    90  }
    91  
    92  func (pe *PolicyEntry) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(pe) }
    93  func (pe *PolicyEntry) NewValue() bpf.MapValue      { return &PolicyEntry{} }
    94  
    95  func (pe *PolicyEntry) Add(oPe PolicyEntry) {
    96  	pe.Packets += oPe.Packets
    97  	pe.Bytes += oPe.Bytes
    98  }
    99  
   100  type PolicyEntryDump struct {
   101  	PolicyEntry
   102  	Key PolicyKey
   103  }
   104  
   105  // PolicyEntriesDump is a wrapper for a slice of PolicyEntryDump
   106  type PolicyEntriesDump []PolicyEntryDump
   107  
   108  // Less returns true if the element in index `i` has the value of
   109  // TrafficDirection lower than `j`'s TrafficDirection or if the element in index
   110  // `i` has the value of TrafficDirection lower and equal than `j`'s
   111  // TrafficDirection and the identity of element `i` is lower than the Identity
   112  // of element j.
   113  func (p PolicyEntriesDump) Less(i, j int) bool {
   114  	if p[i].Key.TrafficDirection < p[j].Key.TrafficDirection {
   115  		return true
   116  	}
   117  	return p[i].Key.TrafficDirection <= p[j].Key.TrafficDirection &&
   118  		p[i].Key.Identity < p[j].Key.Identity
   119  }
   120  
   121  func (key *PolicyKey) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(key) }
   122  func (key *PolicyKey) NewValue() bpf.MapValue    { return &PolicyEntry{} }
   123  
   124  func (key *PolicyKey) String() string {
   125  
   126  	trafficDirectionString := (trafficdirection.TrafficDirection)(key.TrafficDirection).String()
   127  	if key.DestPort != 0 {
   128  		return fmt.Sprintf("%s: %d %d/%d", trafficDirectionString, key.Identity, byteorder.NetworkToHost(key.DestPort), key.Nexthdr)
   129  	}
   130  	return fmt.Sprintf("%s: %d", trafficDirectionString, key.Identity)
   131  }
   132  
   133  // ToHost returns a copy of key with fields converted from network byte-order
   134  // to host-byte-order if necessary.
   135  func (key *PolicyKey) ToHost() PolicyKey {
   136  	if key == nil {
   137  		return PolicyKey{}
   138  	}
   139  
   140  	n := *key
   141  	n.DestPort = byteorder.NetworkToHost(n.DestPort).(uint16)
   142  	return n
   143  }
   144  
   145  // ToNetwork returns a copy of key with fields converted from host byte-order
   146  // to network-byte-order if necessary.
   147  func (key *PolicyKey) ToNetwork() PolicyKey {
   148  	if key == nil {
   149  		return PolicyKey{}
   150  	}
   151  
   152  	n := *key
   153  	n.DestPort = byteorder.HostToNetwork(n.DestPort).(uint16)
   154  	return n
   155  }
   156  
   157  // newKey returns a PolicyKey representing the specified parameters in network
   158  // byte-order.
   159  func newKey(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) PolicyKey {
   160  	return PolicyKey{
   161  		Identity:         id,
   162  		DestPort:         byteorder.HostToNetwork(dport).(uint16),
   163  		Nexthdr:          uint8(proto),
   164  		TrafficDirection: trafficDirection.Uint8(),
   165  	}
   166  }
   167  
   168  // newEntry returns a PolicyEntry representing the specified parameters in
   169  // network byte-order.
   170  func newEntry(proxyPort uint16) PolicyEntry {
   171  	return PolicyEntry{
   172  		ProxyPort: byteorder.HostToNetwork(proxyPort).(uint16),
   173  	}
   174  }
   175  
   176  // AllowKey pushes an entry into the PolicyMap for the given PolicyKey k.
   177  // Returns an error if the update of the PolicyMap fails.
   178  func (pm *PolicyMap) AllowKey(k PolicyKey, proxyPort uint16) error {
   179  	return pm.Allow(k.Identity, k.DestPort, u8proto.U8proto(k.Nexthdr), trafficdirection.TrafficDirection(k.TrafficDirection), proxyPort)
   180  }
   181  
   182  // Allow pushes an entry into the PolicyMap to allow traffic in the given
   183  // `trafficDirection` for identity `id` with destination port `dport` over
   184  // protocol `proto`. It is assumed that `dport` and `proxyPort` are in host byte-order.
   185  func (pm *PolicyMap) Allow(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection, proxyPort uint16) error {
   186  	key := newKey(id, dport, proto, trafficDirection)
   187  	entry := newEntry(proxyPort)
   188  	return pm.Update(&key, &entry)
   189  }
   190  
   191  // Exists determines whether PolicyMap currently contains an entry that
   192  // allows traffic in `trafficDirection` for identity `id` with destination port
   193  // `dport`over protocol `proto`. It is assumed that `dport` is in host byte-order.
   194  func (pm *PolicyMap) Exists(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) bool {
   195  	key := newKey(id, dport, proto, trafficDirection)
   196  	_, err := pm.Lookup(&key)
   197  	return err == nil
   198  }
   199  
   200  // DeleteKey deletes the key-value pair from the given PolicyMap with PolicyKey
   201  // k. Returns an error if deletion from the PolicyMap fails.
   202  func (pm *PolicyMap) DeleteKeyWithErrno(key PolicyKey) (error, syscall.Errno) {
   203  	k := key.ToNetwork()
   204  	return pm.Map.DeleteWithErrno(&k)
   205  }
   206  
   207  // Delete removes an entry from the PolicyMap for identity `id`
   208  // sending traffic in direction `trafficDirection` with destination port `dport`
   209  // over protocol `proto`. It is assumed that `dport` is in host byte-order.
   210  // Returns an error if the deletion did not succeed.
   211  func (pm *PolicyMap) Delete(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) error {
   212  	k := newKey(id, dport, proto, trafficDirection)
   213  	return pm.Map.Delete(&k)
   214  }
   215  
   216  // DeleteEntry removes an entry from the PolicyMap. It can be used in
   217  // conjunction with DumpToSlice() to inspect and delete map entries.
   218  func (pm *PolicyMap) DeleteEntry(entry *PolicyEntryDump) error {
   219  	return pm.Map.Delete(&entry.Key)
   220  }
   221  
   222  // String returns a human-readable string representing the policy map.
   223  func (pm *PolicyMap) String() string {
   224  	path, err := pm.Path()
   225  	if err != nil {
   226  		return err.Error()
   227  	}
   228  	return path
   229  }
   230  
   231  func (pm *PolicyMap) Dump() (string, error) {
   232  	var buffer bytes.Buffer
   233  	entries, err := pm.DumpToSlice()
   234  	if err != nil {
   235  		return "", err
   236  	}
   237  	for _, entry := range entries {
   238  		buffer.WriteString(fmt.Sprintf("%20s: %s\n",
   239  			entry.Key.String(), entry.PolicyEntry.String()))
   240  	}
   241  	return buffer.String(), nil
   242  }
   243  
   244  func (pm *PolicyMap) DumpToSlice() (PolicyEntriesDump, error) {
   245  	entries := PolicyEntriesDump{}
   246  
   247  	cb := func(key bpf.MapKey, value bpf.MapValue) {
   248  		eDump := PolicyEntryDump{
   249  			Key:         *key.DeepCopyMapKey().(*PolicyKey),
   250  			PolicyEntry: *value.DeepCopyMapValue().(*PolicyEntry),
   251  		}
   252  		entries = append(entries, eDump)
   253  	}
   254  	err := pm.DumpWithCallback(cb)
   255  
   256  	return entries, err
   257  }
   258  
   259  func newMap(path string) *PolicyMap {
   260  	mapType := bpf.MapType(bpf.BPF_MAP_TYPE_HASH)
   261  	flags := bpf.GetPreAllocateMapFlags(mapType)
   262  	return &PolicyMap{
   263  		Map: bpf.NewMap(
   264  			path,
   265  			mapType,
   266  			&PolicyKey{},
   267  			int(unsafe.Sizeof(PolicyKey{})),
   268  			&PolicyEntry{},
   269  			int(unsafe.Sizeof(PolicyEntry{})),
   270  			MaxEntries,
   271  			flags, 0,
   272  			bpf.ConvertKeyValue,
   273  		),
   274  	}
   275  }
   276  
   277  // OpenOrCreate opens (or creates) a policy map at the specified path, which
   278  // is used to govern which peer identities can communicate with the endpoint
   279  // protected by this map.
   280  func OpenOrCreate(path string) (*PolicyMap, bool, error) {
   281  	m := newMap(path)
   282  	isNewMap, err := m.OpenOrCreate()
   283  	return m, isNewMap, err
   284  }
   285  
   286  // Open opens the policymap at the specified path.
   287  func Open(path string) (*PolicyMap, error) {
   288  	m := newMap(path)
   289  	if err := m.Open(); err != nil {
   290  		return nil, err
   291  	}
   292  	return m, nil
   293  }
   294  
   295  // InitMapInfo updates the map info defaults for policy maps.
   296  func InitMapInfo(maxEntries int) {
   297  	MaxEntries = maxEntries
   298  }