github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/xdsresource/unmarshal_lds.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package xdsresource 19 20 import ( 21 "errors" 22 "fmt" 23 "strconv" 24 25 v1udpatypepb "github.com/cncf/udpa/go/udpa/type/v1" 26 v3cncftypepb "github.com/cncf/xds/go/xds/type/v3" 27 "github.com/golang/protobuf/proto" 28 "github.com/golang/protobuf/ptypes" 29 v3listenerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3" 30 v3routepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/route/v3" 31 v3httppb "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 32 "github.com/hxx258456/ccgo/grpc/internal/grpclog" 33 "github.com/hxx258456/ccgo/grpc/internal/pretty" 34 "github.com/hxx258456/ccgo/grpc/xds/internal/httpfilter" 35 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource/version" 36 "google.golang.org/protobuf/types/known/anypb" 37 ) 38 39 // UnmarshalListener processes resources received in an LDS response, validates 40 // them, and transforms them into a native struct which contains only fields we 41 // are interested in. 42 func UnmarshalListener(opts *UnmarshalOptions) (map[string]ListenerUpdateErrTuple, UpdateMetadata, error) { 43 update := make(map[string]ListenerUpdateErrTuple) 44 md, err := processAllResources(opts, update) 45 return update, md, err 46 } 47 48 func unmarshalListenerResource(r *anypb.Any, f UpdateValidatorFunc, logger *grpclog.PrefixLogger) (string, ListenerUpdate, error) { 49 if !IsListenerResource(r.GetTypeUrl()) { 50 return "", ListenerUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl()) 51 } 52 // TODO: Pass version.TransportAPI instead of relying upon the type URL 53 v2 := r.GetTypeUrl() == version.V2ListenerURL 54 lis := &v3listenerpb.Listener{} 55 if err := proto.Unmarshal(r.GetValue(), lis); err != nil { 56 return "", ListenerUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err) 57 } 58 logger.Infof("Resource with name: %v, type: %T, contains: %v", lis.GetName(), lis, pretty.ToJSON(lis)) 59 60 lu, err := processListener(lis, logger, v2) 61 if err != nil { 62 return lis.GetName(), ListenerUpdate{}, err 63 } 64 if f != nil { 65 if err := f(*lu); err != nil { 66 return lis.GetName(), ListenerUpdate{}, err 67 } 68 } 69 lu.Raw = r 70 return lis.GetName(), *lu, nil 71 } 72 73 func processListener(lis *v3listenerpb.Listener, logger *grpclog.PrefixLogger, v2 bool) (*ListenerUpdate, error) { 74 if lis.GetApiListener() != nil { 75 return processClientSideListener(lis, logger, v2) 76 } 77 return processServerSideListener(lis, logger) 78 } 79 80 // processClientSideListener checks if the provided Listener proto meets 81 // the expected criteria. If so, it returns a non-empty routeConfigName. 82 func processClientSideListener(lis *v3listenerpb.Listener, logger *grpclog.PrefixLogger, v2 bool) (*ListenerUpdate, error) { 83 update := &ListenerUpdate{} 84 85 apiLisAny := lis.GetApiListener().GetApiListener() 86 if !IsHTTPConnManagerResource(apiLisAny.GetTypeUrl()) { 87 return nil, fmt.Errorf("unexpected resource type: %q", apiLisAny.GetTypeUrl()) 88 } 89 apiLis := &v3httppb.HttpConnectionManager{} 90 if err := proto.Unmarshal(apiLisAny.GetValue(), apiLis); err != nil { 91 return nil, fmt.Errorf("failed to unmarshal api_listner: %v", err) 92 } 93 // "HttpConnectionManager.xff_num_trusted_hops must be unset or zero and 94 // HttpConnectionManager.original_ip_detection_extensions must be empty. If 95 // either field has an incorrect value, the Listener must be NACKed." - A41 96 if apiLis.XffNumTrustedHops != 0 { 97 return nil, fmt.Errorf("xff_num_trusted_hops must be unset or zero %+v", apiLis) 98 } 99 if len(apiLis.OriginalIpDetectionExtensions) != 0 { 100 return nil, fmt.Errorf("original_ip_detection_extensions must be empty %+v", apiLis) 101 } 102 103 switch apiLis.RouteSpecifier.(type) { 104 case *v3httppb.HttpConnectionManager_Rds: 105 if apiLis.GetRds().GetConfigSource().GetAds() == nil { 106 return nil, fmt.Errorf("ConfigSource is not ADS: %+v", lis) 107 } 108 name := apiLis.GetRds().GetRouteConfigName() 109 if name == "" { 110 return nil, fmt.Errorf("empty route_config_name: %+v", lis) 111 } 112 update.RouteConfigName = name 113 case *v3httppb.HttpConnectionManager_RouteConfig: 114 routeU, err := generateRDSUpdateFromRouteConfiguration(apiLis.GetRouteConfig(), logger, v2) 115 if err != nil { 116 return nil, fmt.Errorf("failed to parse inline RDS resp: %v", err) 117 } 118 update.InlineRouteConfig = &routeU 119 case nil: 120 return nil, fmt.Errorf("no RouteSpecifier: %+v", apiLis) 121 default: 122 return nil, fmt.Errorf("unsupported type %T for RouteSpecifier", apiLis.RouteSpecifier) 123 } 124 125 if v2 { 126 return update, nil 127 } 128 129 // The following checks and fields only apply to xDS protocol versions v3+. 130 131 update.MaxStreamDuration = apiLis.GetCommonHttpProtocolOptions().GetMaxStreamDuration().AsDuration() 132 133 var err error 134 if update.HTTPFilters, err = processHTTPFilters(apiLis.GetHttpFilters(), false); err != nil { 135 return nil, err 136 } 137 138 return update, nil 139 } 140 141 func unwrapHTTPFilterConfig(config *anypb.Any) (proto.Message, string, error) { 142 switch { 143 case ptypes.Is(config, &v3cncftypepb.TypedStruct{}): 144 // The real type name is inside the new TypedStruct message. 145 s := new(v3cncftypepb.TypedStruct) 146 if err := ptypes.UnmarshalAny(config, s); err != nil { 147 return nil, "", fmt.Errorf("error unmarshalling TypedStruct filter config: %v", err) 148 } 149 return s, s.GetTypeUrl(), nil 150 case ptypes.Is(config, &v1udpatypepb.TypedStruct{}): 151 // The real type name is inside the old TypedStruct message. 152 s := new(v1udpatypepb.TypedStruct) 153 if err := ptypes.UnmarshalAny(config, s); err != nil { 154 return nil, "", fmt.Errorf("error unmarshalling TypedStruct filter config: %v", err) 155 } 156 return s, s.GetTypeUrl(), nil 157 default: 158 return config, config.GetTypeUrl(), nil 159 } 160 } 161 162 func validateHTTPFilterConfig(cfg *anypb.Any, lds, optional bool) (httpfilter.Filter, httpfilter.FilterConfig, error) { 163 config, typeURL, err := unwrapHTTPFilterConfig(cfg) 164 if err != nil { 165 return nil, nil, err 166 } 167 filterBuilder := httpfilter.Get(typeURL) 168 if filterBuilder == nil { 169 if optional { 170 return nil, nil, nil 171 } 172 return nil, nil, fmt.Errorf("no filter implementation found for %q", typeURL) 173 } 174 parseFunc := filterBuilder.ParseFilterConfig 175 if !lds { 176 parseFunc = filterBuilder.ParseFilterConfigOverride 177 } 178 filterConfig, err := parseFunc(config) 179 if err != nil { 180 return nil, nil, fmt.Errorf("error parsing config for filter %q: %v", typeURL, err) 181 } 182 return filterBuilder, filterConfig, nil 183 } 184 185 func processHTTPFilterOverrides(cfgs map[string]*anypb.Any) (map[string]httpfilter.FilterConfig, error) { 186 if len(cfgs) == 0 { 187 return nil, nil 188 } 189 m := make(map[string]httpfilter.FilterConfig) 190 for name, cfg := range cfgs { 191 optional := false 192 s := new(v3routepb.FilterConfig) 193 if ptypes.Is(cfg, s) { 194 if err := ptypes.UnmarshalAny(cfg, s); err != nil { 195 return nil, fmt.Errorf("filter override %q: error unmarshalling FilterConfig: %v", name, err) 196 } 197 cfg = s.GetConfig() 198 optional = s.GetIsOptional() 199 } 200 201 httpFilter, config, err := validateHTTPFilterConfig(cfg, false, optional) 202 if err != nil { 203 return nil, fmt.Errorf("filter override %q: %v", name, err) 204 } 205 if httpFilter == nil { 206 // Optional configs are ignored. 207 continue 208 } 209 m[name] = config 210 } 211 return m, nil 212 } 213 214 func processHTTPFilters(filters []*v3httppb.HttpFilter, server bool) ([]HTTPFilter, error) { 215 ret := make([]HTTPFilter, 0, len(filters)) 216 seenNames := make(map[string]bool, len(filters)) 217 for _, filter := range filters { 218 name := filter.GetName() 219 if name == "" { 220 return nil, errors.New("filter missing name field") 221 } 222 if seenNames[name] { 223 return nil, fmt.Errorf("duplicate filter name %q", name) 224 } 225 seenNames[name] = true 226 227 httpFilter, config, err := validateHTTPFilterConfig(filter.GetTypedConfig(), true, filter.GetIsOptional()) 228 if err != nil { 229 return nil, err 230 } 231 if httpFilter == nil { 232 // Optional configs are ignored. 233 continue 234 } 235 if server { 236 if _, ok := httpFilter.(httpfilter.ServerInterceptorBuilder); !ok { 237 if filter.GetIsOptional() { 238 continue 239 } 240 return nil, fmt.Errorf("HTTP filter %q not supported server-side", name) 241 } 242 } else if _, ok := httpFilter.(httpfilter.ClientInterceptorBuilder); !ok { 243 if filter.GetIsOptional() { 244 continue 245 } 246 return nil, fmt.Errorf("HTTP filter %q not supported client-side", name) 247 } 248 249 // Save name/config 250 ret = append(ret, HTTPFilter{Name: name, Filter: httpFilter, Config: config}) 251 } 252 // "Validation will fail if a terminal filter is not the last filter in the 253 // chain or if a non-terminal filter is the last filter in the chain." - A39 254 if len(ret) == 0 { 255 return nil, fmt.Errorf("http filters list is empty") 256 } 257 var i int 258 for ; i < len(ret)-1; i++ { 259 if ret[i].Filter.IsTerminal() { 260 return nil, fmt.Errorf("http filter %q is a terminal filter but it is not last in the filter chain", ret[i].Name) 261 } 262 } 263 if !ret[i].Filter.IsTerminal() { 264 return nil, fmt.Errorf("http filter %q is not a terminal filter", ret[len(ret)-1].Name) 265 } 266 return ret, nil 267 } 268 269 func processServerSideListener(lis *v3listenerpb.Listener, logger *grpclog.PrefixLogger) (*ListenerUpdate, error) { 270 if n := len(lis.ListenerFilters); n != 0 { 271 return nil, fmt.Errorf("unsupported field 'listener_filters' contains %d entries", n) 272 } 273 if useOrigDst := lis.GetUseOriginalDst(); useOrigDst != nil && useOrigDst.GetValue() { 274 return nil, errors.New("unsupported field 'use_original_dst' is present and set to true") 275 } 276 addr := lis.GetAddress() 277 if addr == nil { 278 return nil, fmt.Errorf("no address field in LDS response: %+v", lis) 279 } 280 sockAddr := addr.GetSocketAddress() 281 if sockAddr == nil { 282 return nil, fmt.Errorf("no socket_address field in LDS response: %+v", lis) 283 } 284 lu := &ListenerUpdate{ 285 InboundListenerCfg: &InboundListenerConfig{ 286 Address: sockAddr.GetAddress(), 287 Port: strconv.Itoa(int(sockAddr.GetPortValue())), 288 }, 289 } 290 291 fcMgr, err := NewFilterChainManager(lis, logger) 292 if err != nil { 293 return nil, err 294 } 295 lu.InboundListenerCfg.FilterChains = fcMgr 296 return lu, nil 297 }