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  }