istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/match/match.go (about)

     1  // Copyright Istio Authors
     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 match
    16  
    17  import (
    18  	xds "github.com/cncf/xds/go/xds/core/v3"
    19  	matcher "github.com/cncf/xds/go/xds/type/matcher/v3"
    20  	network "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/network/v3"
    21  	wrappers "google.golang.org/protobuf/types/known/wrapperspb"
    22  
    23  	"istio.io/istio/pilot/pkg/features"
    24  	"istio.io/istio/pilot/pkg/util/protoconv"
    25  	"istio.io/istio/pkg/log"
    26  )
    27  
    28  var (
    29  	DestinationPort = &xds.TypedExtensionConfig{
    30  		Name:        "port",
    31  		TypedConfig: protoconv.MessageToAny(&network.DestinationPortInput{}),
    32  	}
    33  	DestinationIP = &xds.TypedExtensionConfig{
    34  		Name:        "ip",
    35  		TypedConfig: protoconv.MessageToAny(&network.DestinationIPInput{}),
    36  	}
    37  	SourceIP = &xds.TypedExtensionConfig{
    38  		Name:        "source-ip",
    39  		TypedConfig: protoconv.MessageToAny(&network.SourceIPInput{}),
    40  	}
    41  	SNI = &xds.TypedExtensionConfig{
    42  		Name:        "sni",
    43  		TypedConfig: protoconv.MessageToAny(&network.ServerNameInput{}),
    44  	}
    45  	ApplicationProtocolInput = &xds.TypedExtensionConfig{
    46  		Name:        "application-protocol",
    47  		TypedConfig: protoconv.MessageToAny(&network.ApplicationProtocolInput{}),
    48  	}
    49  	TransportProtocolInput = &xds.TypedExtensionConfig{
    50  		Name:        "transport-protocol",
    51  		TypedConfig: protoconv.MessageToAny(&network.TransportProtocolInput{}),
    52  	}
    53  )
    54  
    55  type Mapper struct {
    56  	*matcher.Matcher
    57  	Map map[string]*matcher.Matcher_OnMatch
    58  }
    59  
    60  func newMapper(input *xds.TypedExtensionConfig) Mapper {
    61  	m := map[string]*matcher.Matcher_OnMatch{}
    62  	match := &matcher.Matcher{
    63  		MatcherType: &matcher.Matcher_MatcherTree_{
    64  			MatcherTree: &matcher.Matcher_MatcherTree{
    65  				Input: input,
    66  				TreeType: &matcher.Matcher_MatcherTree_ExactMatchMap{
    67  					ExactMatchMap: &matcher.Matcher_MatcherTree_MatchMap{
    68  						Map: m,
    69  					},
    70  				},
    71  			},
    72  		},
    73  		OnNoMatch: nil,
    74  	}
    75  	return Mapper{Matcher: match, Map: m}
    76  }
    77  
    78  func NewDestinationIP() Mapper {
    79  	return newMapper(DestinationIP)
    80  }
    81  
    82  func NewSourceIP() Mapper {
    83  	return newMapper(SourceIP)
    84  }
    85  
    86  func NewDestinationPort() Mapper {
    87  	return newMapper(DestinationPort)
    88  }
    89  
    90  type ProtocolMatch struct {
    91  	TCP, HTTP *matcher.Matcher_OnMatch
    92  }
    93  
    94  func NewAppProtocol(pm ProtocolMatch) *matcher.Matcher {
    95  	m := newMapper(ApplicationProtocolInput)
    96  	m.Map["'h2c'"] = pm.HTTP
    97  	m.Map["'http/1.1'"] = pm.HTTP
    98  	if features.HTTP10 {
    99  		m.Map["'http/1.0'"] = pm.HTTP
   100  	}
   101  	m.OnNoMatch = pm.TCP
   102  	return m.Matcher
   103  }
   104  
   105  func ToChain(name string) *matcher.Matcher_OnMatch {
   106  	return &matcher.Matcher_OnMatch{
   107  		OnMatch: &matcher.Matcher_OnMatch_Action{
   108  			Action: &xds.TypedExtensionConfig{
   109  				Name:        name,
   110  				TypedConfig: protoconv.MessageToAny(&wrappers.StringValue{Value: name}),
   111  			},
   112  		},
   113  	}
   114  }
   115  
   116  func ToMatcher(match *matcher.Matcher) *matcher.Matcher_OnMatch {
   117  	return &matcher.Matcher_OnMatch{
   118  		OnMatch: &matcher.Matcher_OnMatch_Matcher{
   119  			Matcher: match,
   120  		},
   121  	}
   122  }
   123  
   124  // BuildMatcher cleans the entire match tree to avoid empty maps and returns a viable top-level matcher.
   125  // Note: this mutates the internal mappers/matchers that make up the tree.
   126  func (m Mapper) BuildMatcher() *matcher.Matcher {
   127  	root := m
   128  	for len(root.Map) == 0 {
   129  		// the top level matcher is empty; if its fallback goes to a matcher, return that
   130  		// TODO is there a way we can just say "always go to action"?
   131  		if fallback := root.GetOnNoMatch(); fallback != nil {
   132  			if replacement, ok := mapperFromMatch(fallback.GetMatcher()); ok {
   133  				root = replacement
   134  				continue
   135  			}
   136  		}
   137  		// no fallback or fallback isn't a mapper
   138  		log.Warnf("could not repair invalid matcher; empty map at root matcher does not have a map fallback")
   139  		return nil
   140  	}
   141  	q := []*matcher.Matcher_OnMatch{m.OnNoMatch}
   142  	for _, onMatch := range root.Map {
   143  		q = append(q, onMatch)
   144  	}
   145  
   146  	// fix the matchers, add child mappers OnMatch to the queue
   147  	for len(q) > 0 {
   148  		head := q[0]
   149  		q = q[1:]
   150  		q = append(q, fixEmptyOnMatchMap(head)...)
   151  	}
   152  	return root.Matcher
   153  }
   154  
   155  // if the onMatch sends to an empty mapper, make the onMatch send directly to the onNoMatch of that empty mapper
   156  // returns mapper if it doesn't need to be fixed, or can't be fixed
   157  func fixEmptyOnMatchMap(onMatch *matcher.Matcher_OnMatch) []*matcher.Matcher_OnMatch {
   158  	if onMatch == nil {
   159  		return nil
   160  	}
   161  	innerMatcher := onMatch.GetMatcher()
   162  	if innerMatcher == nil {
   163  		// this already just performs an Action
   164  		return nil
   165  	}
   166  	innerMapper, ok := mapperFromMatch(innerMatcher)
   167  	if !ok {
   168  		// this isn't a mapper or action, not supported by this func
   169  		return nil
   170  	}
   171  	if len(innerMapper.Map) > 0 {
   172  		return innerMapper.allOnMatches()
   173  	}
   174  
   175  	if fallback := innerMapper.GetOnNoMatch(); fallback != nil {
   176  		// change from: onMatch -> map (empty with fallback) to onMatch -> fallback
   177  		// that fallback may be an empty map, so we re-queue onMatch in case it still needs fixing
   178  		onMatch.OnMatch = fallback.OnMatch
   179  		return []*matcher.Matcher_OnMatch{onMatch} // the inner mapper is gone
   180  	}
   181  
   182  	// envoy will nack this eventually
   183  	log.Warnf("empty mapper %v with no fallback", innerMapper.Matcher)
   184  	return innerMapper.allOnMatches()
   185  }
   186  
   187  func (m Mapper) allOnMatches() []*matcher.Matcher_OnMatch {
   188  	var out []*matcher.Matcher_OnMatch
   189  	out = append(out, m.OnNoMatch)
   190  	if m.Map == nil {
   191  		return out
   192  	}
   193  	for _, match := range m.Map {
   194  		out = append(out, match)
   195  	}
   196  	return out
   197  }
   198  
   199  func mapperFromMatch(mmatcher *matcher.Matcher) (Mapper, bool) {
   200  	if mmatcher == nil {
   201  		return Mapper{}, false
   202  	}
   203  	switch m := mmatcher.MatcherType.(type) {
   204  	case *matcher.Matcher_MatcherTree_:
   205  		var mmap *matcher.Matcher_MatcherTree_MatchMap
   206  		switch t := m.MatcherTree.TreeType.(type) {
   207  		case *matcher.Matcher_MatcherTree_PrefixMatchMap:
   208  			mmap = t.PrefixMatchMap
   209  		case *matcher.Matcher_MatcherTree_ExactMatchMap:
   210  			mmap = t.ExactMatchMap
   211  		default:
   212  			return Mapper{}, false
   213  		}
   214  		return Mapper{Matcher: mmatcher, Map: mmap.Map}, true
   215  	}
   216  	return Mapper{}, false
   217  }