github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/l4.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 policy
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"sort"
    22  	"strconv"
    23  	"sync/atomic"
    24  	"unsafe"
    25  
    26  	"github.com/cilium/cilium/api/v1/models"
    27  	"github.com/cilium/cilium/pkg/identity"
    28  	"github.com/cilium/cilium/pkg/labels"
    29  	"github.com/cilium/cilium/pkg/lock"
    30  	"github.com/cilium/cilium/pkg/logging/logfields"
    31  	"github.com/cilium/cilium/pkg/policy/api"
    32  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    33  	"github.com/cilium/cilium/pkg/u8proto"
    34  
    35  	"github.com/sirupsen/logrus"
    36  )
    37  
    38  // L7DataMap contains a map of L7 rules per endpoint where key is a CachedSelector
    39  type L7DataMap map[CachedSelector]api.L7Rules
    40  
    41  func (l7 L7DataMap) MarshalJSON() ([]byte, error) {
    42  	if len(l7) == 0 {
    43  		return []byte("[]"), nil
    44  	}
    45  
    46  	/* First, create a sorted slice of the selectors so we can get
    47  	 * consistent JSON output */
    48  	selectors := make(CachedSelectorSlice, 0, len(l7))
    49  	for cs := range l7 {
    50  		selectors = append(selectors, cs)
    51  	}
    52  	sort.Sort(selectors)
    53  
    54  	/* Now we can iterate the slice and generate JSON entries. */
    55  	var err error
    56  	buffer := bytes.NewBufferString("[")
    57  	for _, cs := range selectors {
    58  		buffer.WriteString("{\"")
    59  		buffer.WriteString(cs.String())
    60  		buffer.WriteString("\":")
    61  		b, err := json.Marshal(l7[cs])
    62  		if err == nil {
    63  			buffer.Write(b)
    64  		} else {
    65  			buffer.WriteString("\"L7DataMap error: ")
    66  			buffer.WriteString(err.Error())
    67  			buffer.WriteString("\"")
    68  		}
    69  		buffer.WriteString("},")
    70  	}
    71  	buffer.Truncate(buffer.Len() - 1) // Drop the final ","
    72  	buffer.WriteString("]")
    73  
    74  	return buffer.Bytes(), err
    75  }
    76  
    77  // L7ParserType is the type used to indicate what L7 parser to use.
    78  // Consts are defined for all well known L7 parsers.
    79  // Unknown string values are created for key-value pair policies, which
    80  // are then transparently used in redirect configuration.
    81  type L7ParserType string
    82  
    83  func (l7 L7ParserType) String() string {
    84  	return (string)(l7)
    85  }
    86  
    87  const (
    88  	// ParserTypeNone represents the case where no parser type is provided.
    89  	ParserTypeNone L7ParserType = ""
    90  	// ParserTypeHTTP specifies a HTTP parser type
    91  	ParserTypeHTTP L7ParserType = "http"
    92  	// ParserTypeKafka specifies a Kafka parser type
    93  	ParserTypeKafka L7ParserType = "kafka"
    94  	// ParserTypeDNS specifies a DNS parser type
    95  	ParserTypeDNS L7ParserType = "dns"
    96  )
    97  
    98  // L4Filter represents the policy (allowed remote sources / destinations of
    99  // traffic) that applies at a specific L4 port/protocol combination (including
   100  // all ports and protocols), at either ingress or egress. The policy here is
   101  // specified in terms of selectors that are mapped to security identities via
   102  // the selector cache.
   103  type L4Filter struct {
   104  	// Port is the destination port to allow. Port 0 indicates that all traffic
   105  	// is allowed at L4.
   106  	Port int `json:"port"`
   107  	// Protocol is the L4 protocol to allow or NONE
   108  	Protocol api.L4Proto `json:"protocol"`
   109  	// U8Proto is the Protocol in numeric format, or 0 for NONE
   110  	U8Proto u8proto.U8proto `json:"-"`
   111  	// allowsAllAtL3 indicates whether this filter allows all traffic at L3.
   112  	// This can be determined by checking whether 'Endpoints' contains
   113  	// 'wildcardCachedSelector', but caching this information instead is
   114  	// much more performant.
   115  	allowsAllAtL3 bool
   116  	// CachedSelectors limits the labels for allowing traffic (to / from).
   117  	// This includes selectors for destinations affected by entity-based
   118  	// and CIDR-based policy.
   119  	// Holds references to the CachedSelectors, which must be released!
   120  	CachedSelectors CachedSelectorSlice `json:"-"`
   121  	// L7Parser specifies the L7 protocol parser (optional). If specified as
   122  	// an empty string, then means that no L7 proxy redirect is performed.
   123  	L7Parser L7ParserType `json:"-"`
   124  	// L7RulesPerEp is a list of L7 rules per endpoint passed to the L7 proxy (optional)
   125  	L7RulesPerEp L7DataMap `json:"l7-rules,omitempty"`
   126  	// Ingress is true if filter applies at ingress; false if it applies at egress.
   127  	Ingress bool `json:"-"`
   128  	// The rule labels of this Filter
   129  	DerivedFromRules labels.LabelArrayList `json:"-"`
   130  
   131  	// This reference is circular, but it is cleaned up at Detach()
   132  	policy unsafe.Pointer // *L4Policy
   133  }
   134  
   135  // AllowsAllAtL3 returns whether this L4Filter applies to all endpoints at L3.
   136  func (l4 *L4Filter) AllowsAllAtL3() bool {
   137  	return l4.allowsAllAtL3
   138  }
   139  
   140  // HasL3DependentL7Rules returns true if this L4Filter is created from rules
   141  // that require an L3 match as well as specific L7 rules.
   142  func (l4 *L4Filter) HasL3DependentL7Rules() bool {
   143  	switch len(l4.L7RulesPerEp) {
   144  	case 0:
   145  		// No L7 rules.
   146  		return false
   147  	case 1:
   148  		// loop to get access to the first and only key in the map
   149  		for cs := range l4.L7RulesPerEp {
   150  			// If L3 is wildcarded, this filter corresponds to L4-only rule(s).
   151  			return !cs.IsWildcard()
   152  		}
   153  	}
   154  	return true
   155  }
   156  
   157  // ToKeys converts filter into a list of Keys.
   158  func (l4 *L4Filter) ToKeys(direction trafficdirection.TrafficDirection) []Key {
   159  	keysToAdd := []Key{}
   160  	port := uint16(l4.Port)
   161  	proto := uint8(l4.U8Proto)
   162  
   163  	if l4.AllowsAllAtL3() {
   164  		if l4.Port == 0 {
   165  			// Allow-all
   166  			log.WithFields(logrus.Fields{
   167  				logfields.TrafficDirection: direction,
   168  			}).Debug("ToKeys: allow all")
   169  
   170  			keyToAdd := Key{
   171  				DestPort:         0,
   172  				Nexthdr:          0,
   173  				TrafficDirection: direction.Uint8(),
   174  			}
   175  			keysToAdd = append(keysToAdd, keyToAdd)
   176  		} else {
   177  			// L4 allow
   178  			log.WithFields(logrus.Fields{
   179  				logfields.Port:             port,
   180  				logfields.Protocol:         proto,
   181  				logfields.TrafficDirection: direction,
   182  			}).Debug("ToKeys: L4 allow all")
   183  
   184  			keyToAdd := Key{
   185  				Identity: 0,
   186  				// NOTE: Port is in host byte-order!
   187  				DestPort:         port,
   188  				Nexthdr:          proto,
   189  				TrafficDirection: direction.Uint8(),
   190  			}
   191  			keysToAdd = append(keysToAdd, keyToAdd)
   192  		}
   193  		if !l4.HasL3DependentL7Rules() {
   194  			return keysToAdd
   195  		} // else we need to calculate all L3-dependent L4 peers below.
   196  	}
   197  
   198  	for _, cs := range l4.CachedSelectors {
   199  		identities := cs.GetSelections()
   200  		log.WithFields(logrus.Fields{
   201  			logfields.TrafficDirection: direction,
   202  			logfields.EndpointSelector: cs,
   203  			logfields.PolicyID:         identities,
   204  		}).Debug("ToKeys: Allowed remote IDs")
   205  		for _, id := range identities {
   206  			srcID := id.Uint32()
   207  			keyToAdd := Key{
   208  				Identity: srcID,
   209  				// NOTE: Port is in host byte-order!
   210  				DestPort:         port,
   211  				Nexthdr:          proto,
   212  				TrafficDirection: direction.Uint8(),
   213  			}
   214  			keysToAdd = append(keysToAdd, keyToAdd)
   215  		}
   216  	}
   217  
   218  	return keysToAdd
   219  }
   220  
   221  // IdentitySelectionUpdated implements CachedSelectionUser interface
   222  // This call is made while holding name manager and selector cache
   223  // locks, must beware of deadlocking!
   224  //
   225  // The caller is responsible for making sure the same identity is not
   226  // present in both 'added' and 'deleted'.
   227  func (l4 *L4Filter) IdentitySelectionUpdated(selector CachedSelector, selections, added, deleted []identity.NumericIdentity) {
   228  	log.WithFields(logrus.Fields{
   229  		logfields.EndpointSelector: selector,
   230  		logfields.PolicyID:         selections,
   231  		logfields.AddedPolicyID:    added,
   232  		logfields.DeletedPolicyID:  deleted,
   233  	}).Debug("identities selected by L4Filter updated")
   234  
   235  	// Skip updates on filter that wildcards L3.
   236  	// This logic mirrors the one in ToKeys().
   237  	if l4.AllowsAllAtL3() && !l4.HasL3DependentL7Rules() {
   238  		return
   239  	}
   240  
   241  	// Push endpoint policy changes.
   242  	//
   243  	// `l4.policy` is set to nil when the filter is detached so
   244  	// that we could not push updates on a stale policy.
   245  	l4Policy := (*L4Policy)(atomic.LoadPointer(&l4.policy))
   246  	if l4Policy != nil {
   247  		direction := trafficdirection.Egress
   248  		if l4.Ingress {
   249  			direction = trafficdirection.Ingress
   250  		}
   251  		l4Policy.AccumulateMapChanges(added, deleted, uint16(l4.Port), uint8(l4.U8Proto), direction)
   252  	}
   253  }
   254  
   255  func (l4 *L4Filter) cacheIdentitySelector(sel api.EndpointSelector, selectorCache *SelectorCache) CachedSelector {
   256  	cs, added := selectorCache.AddIdentitySelector(l4, sel)
   257  	if added {
   258  		l4.CachedSelectors = append(l4.CachedSelectors, cs)
   259  	}
   260  	return cs
   261  }
   262  
   263  func (l4 *L4Filter) cacheIdentitySelectors(selectors api.EndpointSelectorSlice, selectorCache *SelectorCache) {
   264  	for _, sel := range selectors {
   265  		l4.cacheIdentitySelector(sel, selectorCache)
   266  	}
   267  }
   268  
   269  func (l4 *L4Filter) cacheFQDNSelectors(selectors api.FQDNSelectorSlice, selectorCache *SelectorCache) {
   270  	for _, fqdnSel := range selectors {
   271  		l4.cacheFQDNSelector(fqdnSel, selectorCache)
   272  	}
   273  }
   274  
   275  func (l4 *L4Filter) cacheFQDNSelector(sel api.FQDNSelector, selectorCache *SelectorCache) CachedSelector {
   276  	cs, added := selectorCache.AddFQDNSelector(l4, sel)
   277  	if added {
   278  		l4.CachedSelectors = append(l4.CachedSelectors, cs)
   279  	}
   280  	return cs
   281  }
   282  
   283  // GetRelevantRulesForKafka returns the relevant rules based on the remote numeric identity.
   284  func (l7 L7DataMap) GetRelevantRulesForKafka(nid identity.NumericIdentity) []api.PortRuleKafka {
   285  	var rules []api.PortRuleKafka
   286  
   287  	for cs, r := range l7 {
   288  		if cs.IsWildcard() || cs.Selects(nid) {
   289  			rules = append(rules, r.Kafka...)
   290  		}
   291  	}
   292  	return rules
   293  }
   294  
   295  func (l7 L7DataMap) addRulesForEndpoints(rules api.L7Rules, endpoints []CachedSelector) {
   296  	if rules.Len() == 0 {
   297  		return
   298  	}
   299  
   300  	for _, epsel := range endpoints {
   301  		l7[epsel] = rules
   302  	}
   303  }
   304  
   305  // createL4Filter creates a filter for L4 policy that applies to the specified
   306  // endpoints and port/protocol, with reference to the original rules that the
   307  // filter is derived from. This filter may be associated with a series of L7
   308  // rules via the `rule` parameter.
   309  // Not called with an empty peerEndpoints.
   310  func createL4Filter(peerEndpoints api.EndpointSelectorSlice, rule api.PortRule, port api.PortProtocol,
   311  	protocol api.L4Proto, ruleLabels labels.LabelArray, ingress bool, selectorCache *SelectorCache, fqdns api.FQDNSelectorSlice) *L4Filter {
   312  
   313  	// already validated via PortRule.Validate()
   314  	p, _ := strconv.ParseUint(port.Port, 0, 16)
   315  	// already validated via L4Proto.Validate()
   316  	u8p, _ := u8proto.ParseProtocol(string(protocol))
   317  
   318  	l4 := &L4Filter{
   319  		Port:             int(p),
   320  		Protocol:         protocol,
   321  		U8Proto:          u8p,
   322  		L7RulesPerEp:     make(L7DataMap),
   323  		DerivedFromRules: labels.LabelArrayList{ruleLabels},
   324  		Ingress:          ingress,
   325  	}
   326  
   327  	if peerEndpoints.SelectsAllEndpoints() {
   328  		l4.cacheIdentitySelector(api.WildcardEndpointSelector, selectorCache)
   329  		l4.allowsAllAtL3 = true
   330  	} else {
   331  		l4.CachedSelectors = make(CachedSelectorSlice, 0, len(peerEndpoints))
   332  		l4.cacheIdentitySelectors(peerEndpoints, selectorCache)
   333  		l4.cacheFQDNSelectors(fqdns, selectorCache)
   334  	}
   335  
   336  	if protocol == api.ProtoTCP && rule.Rules != nil {
   337  		switch {
   338  		case len(rule.Rules.HTTP) > 0:
   339  			l4.L7Parser = ParserTypeHTTP
   340  		case len(rule.Rules.Kafka) > 0:
   341  			l4.L7Parser = ParserTypeKafka
   342  		case rule.Rules.L7Proto != "":
   343  			l4.L7Parser = (L7ParserType)(rule.Rules.L7Proto)
   344  		}
   345  		if !rule.Rules.IsEmpty() {
   346  			l4.L7RulesPerEp.addRulesForEndpoints(*rule.Rules, l4.CachedSelectors)
   347  		}
   348  	}
   349  
   350  	// we need this to redirect DNS UDP (or ANY, which is more useful)
   351  	if !rule.Rules.IsEmpty() && len(rule.Rules.DNS) > 0 {
   352  		l4.L7Parser = ParserTypeDNS
   353  		l4.L7RulesPerEp.addRulesForEndpoints(*rule.Rules, l4.CachedSelectors)
   354  	}
   355  
   356  	return l4
   357  }
   358  
   359  // detach releases the references held in the L4Filter and must be called before
   360  // the filter is left to be garbage collected.
   361  func (l4 *L4Filter) detach(selectorCache *SelectorCache) {
   362  	selectorCache.RemoveSelectors(l4.CachedSelectors, l4)
   363  	l4.attach(nil)
   364  }
   365  
   366  func (l4 *L4Filter) attach(l4Policy *L4Policy) {
   367  	atomic.StorePointer(&l4.policy, unsafe.Pointer(l4Policy))
   368  }
   369  
   370  // createL4IngressFilter creates a filter for L4 policy that applies to the
   371  // specified endpoints and port/protocol for ingress traffic, with reference
   372  // to the original rules that the filter is derived from. This filter may be
   373  // associated with a series of L7 rules via the `rule` parameter.
   374  //
   375  // hostWildcardL7 determines if L7 traffic from Host should be
   376  // wildcarded (in the relevant daemon mode).
   377  func createL4IngressFilter(fromEndpoints api.EndpointSelectorSlice, hostWildcardL7 bool, rule api.PortRule, port api.PortProtocol,
   378  	protocol api.L4Proto, ruleLabels labels.LabelArray, selectorCache *SelectorCache) *L4Filter {
   379  
   380  	filter := createL4Filter(fromEndpoints, rule, port, protocol, ruleLabels, true, selectorCache, nil)
   381  
   382  	// If the filter would apply L7 rules for the Host, when we should accept everything from host,
   383  	// then wildcard Host at L7.
   384  	if !rule.Rules.IsEmpty() && hostWildcardL7 {
   385  		for _, cs := range filter.CachedSelectors {
   386  			if cs.Selects(identity.ReservedIdentityHost) {
   387  				hostSelector := api.ReservedEndpointSelectors[labels.IDNameHost]
   388  				hcs := filter.cacheIdentitySelector(hostSelector, selectorCache)
   389  				filter.L7RulesPerEp[hcs] = api.L7Rules{}
   390  			}
   391  		}
   392  	}
   393  
   394  	return filter
   395  }
   396  
   397  // createL4EgressFilter creates a filter for L4 policy that applies to the
   398  // specified endpoints and port/protocol for egress traffic, with reference
   399  // to the original rules that the filter is derived from. This filter may be
   400  // associated with a series of L7 rules via the `rule` parameter.
   401  func createL4EgressFilter(toEndpoints api.EndpointSelectorSlice, rule api.PortRule, port api.PortProtocol,
   402  	protocol api.L4Proto, ruleLabels labels.LabelArray, selectorCache *SelectorCache, fqdns api.FQDNSelectorSlice) *L4Filter {
   403  
   404  	return createL4Filter(toEndpoints, rule, port, protocol, ruleLabels, false, selectorCache, fqdns)
   405  }
   406  
   407  // IsRedirect returns true if the L4 filter contains a port redirection
   408  func (l4 *L4Filter) IsRedirect() bool {
   409  	return l4.L7Parser != ParserTypeNone
   410  }
   411  
   412  // IsEnvoyRedirect returns true if the L4 filter contains a port redirected to Envoy
   413  func (l4 *L4Filter) IsEnvoyRedirect() bool {
   414  	return l4.IsRedirect() && l4.L7Parser != ParserTypeKafka && l4.L7Parser != ParserTypeDNS
   415  }
   416  
   417  // IsProxylibRedirect returns true if the L4 filter contains a port redirected to Proxylib (via Envoy)
   418  func (l4 *L4Filter) IsProxylibRedirect() bool {
   419  	return l4.IsEnvoyRedirect() && l4.L7Parser != ParserTypeHTTP
   420  }
   421  
   422  // MarshalIndent returns the `L4Filter` in indented JSON string.
   423  func (l4 *L4Filter) MarshalIndent() string {
   424  	b, err := json.MarshalIndent(l4, "", "  ")
   425  	if err != nil {
   426  		b = []byte("\"L4Filter error: " + err.Error() + "\"")
   427  	}
   428  	return string(b)
   429  }
   430  
   431  // String returns the `L4Filter` in a human-readable string.
   432  func (l4 *L4Filter) String() string {
   433  	b, err := json.Marshal(l4)
   434  	if err != nil {
   435  		return err.Error()
   436  	}
   437  	return string(b)
   438  }
   439  
   440  // Note: Only used for policy tracing
   441  func (l4 *L4Filter) matchesLabels(labels labels.LabelArray) bool {
   442  	if l4.AllowsAllAtL3() {
   443  		return true
   444  	} else if len(labels) == 0 {
   445  		return false
   446  	}
   447  
   448  	for _, sel := range l4.CachedSelectors {
   449  		// slow, but OK for tracing
   450  		if idSel, ok := sel.(*labelIdentitySelector); ok && idSel.xxxMatches(labels) {
   451  			return true
   452  		}
   453  	}
   454  
   455  	return false
   456  }
   457  
   458  // L4PolicyMap is a list of L4 filters indexable by protocol/port
   459  // key format: "port/proto"
   460  type L4PolicyMap map[string]*L4Filter
   461  
   462  // Detach removes the cached selectors held by L4PolicyMap from the
   463  // selectorCache, allowing the map to be garbage collected when there
   464  // are no more references to it.
   465  func (l4 L4PolicyMap) Detach(selectorCache *SelectorCache) {
   466  	for _, f := range l4 {
   467  		f.detach(selectorCache)
   468  	}
   469  }
   470  
   471  // Attach makes all the L4Filters to point back to the L4Policy that contains them.
   472  func (l4 L4PolicyMap) Attach(l4Policy *L4Policy) {
   473  	for _, f := range l4 {
   474  		f.attach(l4Policy)
   475  	}
   476  }
   477  
   478  // HasRedirect returns true if at least one L4 filter contains a port
   479  // redirection
   480  func (l4 L4PolicyMap) HasRedirect() bool {
   481  	for _, f := range l4 {
   482  		if f.IsRedirect() {
   483  			return true
   484  		}
   485  	}
   486  	return false
   487  }
   488  
   489  // HasEnvoyRedirect returns true if at least one L4 filter contains a port
   490  // redirection that is forwarded to Envoy
   491  func (l4 L4PolicyMap) HasEnvoyRedirect() bool {
   492  	for _, f := range l4 {
   493  		if f.IsEnvoyRedirect() {
   494  			return true
   495  		}
   496  	}
   497  	return false
   498  }
   499  
   500  // HasProxylibRedirect returns true if at least one L4 filter contains a port
   501  // redirection that is forwarded to Proxylib (via Envoy)
   502  func (l4 L4PolicyMap) HasProxylibRedirect() bool {
   503  	for _, f := range l4 {
   504  		if f.IsProxylibRedirect() {
   505  			return true
   506  		}
   507  	}
   508  	return false
   509  }
   510  
   511  // containsAllL3L4 checks if the L4PolicyMap contains all L4 ports in `ports`.
   512  // For L4Filters that specify ToEndpoints or FromEndpoints, uses `labels` to
   513  // determine whether the policy allows L4 communication between the corresponding
   514  // endpoints.
   515  // Returns api.Denied in the following conditions:
   516  // * If a single port is not present in the `L4PolicyMap` and is not allowed
   517  //   by the distilled L3 policy
   518  // * If a port is present in the `L4PolicyMap`, but it applies ToEndpoints or
   519  //   FromEndpoints constraints that require labels not present in `labels`.
   520  // Otherwise, returns api.Allowed.
   521  //
   522  // Note: Only used for policy tracing
   523  func (l4 L4PolicyMap) containsAllL3L4(labels labels.LabelArray, ports []*models.Port) api.Decision {
   524  	if len(l4) == 0 {
   525  		return api.Allowed
   526  	}
   527  
   528  	// Check L3-only filters first.
   529  	filter, match := l4[api.PortProtocolAny]
   530  	if match && filter.matchesLabels(labels) {
   531  		return api.Allowed
   532  	}
   533  
   534  	for _, l4Ctx := range ports {
   535  		lwrProtocol := l4Ctx.Protocol
   536  		switch lwrProtocol {
   537  		case "", models.PortProtocolANY:
   538  			tcpPort := fmt.Sprintf("%d/TCP", l4Ctx.Port)
   539  			tcpFilter, tcpmatch := l4[tcpPort]
   540  			if tcpmatch {
   541  				tcpmatch = tcpFilter.matchesLabels(labels)
   542  			}
   543  			udpPort := fmt.Sprintf("%d/UDP", l4Ctx.Port)
   544  			udpFilter, udpmatch := l4[udpPort]
   545  			if udpmatch {
   546  				udpmatch = udpFilter.matchesLabels(labels)
   547  			}
   548  			if !tcpmatch && !udpmatch {
   549  				return api.Denied
   550  			}
   551  		default:
   552  			port := fmt.Sprintf("%d/%s", l4Ctx.Port, lwrProtocol)
   553  			filter, match := l4[port]
   554  			if !match || !filter.matchesLabels(labels) {
   555  				return api.Denied
   556  			}
   557  		}
   558  	}
   559  	return api.Allowed
   560  }
   561  
   562  type L4Policy struct {
   563  	Ingress L4PolicyMap
   564  	Egress  L4PolicyMap
   565  
   566  	// Revision is the repository revision used to generate this policy.
   567  	Revision uint64
   568  
   569  	// Endpoint policies using this L4Policy
   570  	// These are circular references, cleaned up in Detach()
   571  	mutex lock.RWMutex
   572  	users map[*EndpointPolicy]struct{}
   573  }
   574  
   575  // NewL4Policy creates a new L4Policy
   576  func NewL4Policy(revision uint64) *L4Policy {
   577  	return &L4Policy{
   578  		Ingress:  L4PolicyMap{},
   579  		Egress:   L4PolicyMap{},
   580  		Revision: revision,
   581  		users:    make(map[*EndpointPolicy]struct{}),
   582  	}
   583  }
   584  
   585  // insertUser adds a user to the L4Policy so that incremental
   586  // updates of the L4Policy may be forwarded to the users of it.
   587  func (l4 *L4Policy) insertUser(user *EndpointPolicy) {
   588  	l4.mutex.Lock()
   589  
   590  	// 'users' is set to nil when the policy is detached. This
   591  	// happens to the old policy when it is being replaced with a
   592  	// new one, or when the last endpoint using this policy is
   593  	// removed.
   594  	// In the case of an policy update it is possible that an
   595  	// endpoint has started regeneration before the policy was
   596  	// updated, and that the policy was updated before the said
   597  	// endpoint reached this point. In this case the endpoint's
   598  	// policy is going to be recomputed soon after and we do
   599  	// nothing here.
   600  	if l4.users != nil {
   601  		l4.users[user] = struct{}{}
   602  	}
   603  
   604  	l4.mutex.Unlock()
   605  }
   606  
   607  // AccumulateMapChanges distributes the given changes to the registered users.
   608  //
   609  // The caller is responsible for making sure the same identity is not
   610  // present in both 'adds' and 'deletes'.
   611  func (l4 *L4Policy) AccumulateMapChanges(adds, deletes []identity.NumericIdentity,
   612  	port uint16, proto uint8, direction trafficdirection.TrafficDirection) {
   613  	l4.mutex.RLock()
   614  	for epPolicy := range l4.users {
   615  		epPolicy.PolicyMapChanges.AccumulateMapChanges(adds, deletes, port, proto, direction)
   616  	}
   617  	l4.mutex.RUnlock()
   618  }
   619  
   620  // Detach makes the L4Policy ready for garbage collection, removing
   621  // circular pointer references.
   622  // Note that the L4Policy itself is not modified in any way, so that it may still
   623  // be used concurrently.
   624  func (l4 *L4Policy) Detach(selectorCache *SelectorCache) {
   625  	l4.Ingress.Detach(selectorCache)
   626  	l4.Egress.Detach(selectorCache)
   627  
   628  	l4.mutex.Lock()
   629  	l4.users = nil
   630  	l4.mutex.Unlock()
   631  }
   632  
   633  // Attach makes all the L4Filters to point back to the L4Policy that contains them.
   634  // This is done before the L4Policy is exposed to concurrent access.
   635  func (l4 *L4Policy) Attach() {
   636  	l4.Ingress.Attach(l4)
   637  	l4.Egress.Attach(l4)
   638  }
   639  
   640  // IngressCoversContext checks if the receiver's ingress L4Policy contains
   641  // all `dPorts` and `labels`.
   642  //
   643  // Note: Only used for policy tracing
   644  func (l4 *L4PolicyMap) IngressCoversContext(ctx *SearchContext) api.Decision {
   645  	return l4.containsAllL3L4(ctx.From, ctx.DPorts)
   646  }
   647  
   648  // EgressCoversContext checks if the receiver's egress L4Policy contains
   649  // all `dPorts` and `labels`.
   650  //
   651  // Note: Only used for policy tracing
   652  func (l4 *L4PolicyMap) EgressCoversContext(ctx *SearchContext) api.Decision {
   653  	return l4.containsAllL3L4(ctx.To, ctx.DPorts)
   654  }
   655  
   656  // HasRedirect returns true if the L4 policy contains at least one port redirection
   657  func (l4 *L4Policy) HasRedirect() bool {
   658  	return l4 != nil && (l4.Ingress.HasRedirect() || l4.Egress.HasRedirect())
   659  }
   660  
   661  // HasEnvoyRedirect returns true if the L4 policy contains at least one port redirection to Envoy
   662  func (l4 *L4Policy) HasEnvoyRedirect() bool {
   663  	return l4 != nil && (l4.Ingress.HasEnvoyRedirect() || l4.Egress.HasEnvoyRedirect())
   664  }
   665  
   666  // HasProxylibRedirect returns true if the L4 policy contains at least one port redirection to Proxylib
   667  func (l4 *L4Policy) HasProxylibRedirect() bool {
   668  	return l4 != nil && (l4.Ingress.HasProxylibRedirect() || l4.Egress.HasProxylibRedirect())
   669  }
   670  
   671  // RequiresConntrack returns true if if the L4 configuration requires
   672  // connection tracking to be enabled.
   673  func (l4 *L4Policy) RequiresConntrack() bool {
   674  	return l4 != nil && (len(l4.Ingress) > 0 || len(l4.Egress) > 0)
   675  }
   676  
   677  func (l4 *L4Policy) GetModel() *models.L4Policy {
   678  	if l4 == nil {
   679  		return nil
   680  	}
   681  
   682  	ingress := []*models.PolicyRule{}
   683  	for _, v := range l4.Ingress {
   684  		ingress = append(ingress, &models.PolicyRule{
   685  			Rule:             v.MarshalIndent(),
   686  			DerivedFromRules: v.DerivedFromRules.GetModel(),
   687  		})
   688  	}
   689  
   690  	egress := []*models.PolicyRule{}
   691  	for _, v := range l4.Egress {
   692  		egress = append(egress, &models.PolicyRule{
   693  			Rule:             v.MarshalIndent(),
   694  			DerivedFromRules: v.DerivedFromRules.GetModel(),
   695  		})
   696  	}
   697  
   698  	return &models.L4Policy{
   699  		Ingress: ingress,
   700  		Egress:  egress,
   701  	}
   702  }