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 }