github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/router/config.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package router
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  
    10  	"github.com/golang/protobuf/jsonpb"
    11  
    12  	"github.com/v2fly/v2ray-core/v5/app/router/routercommon"
    13  	"github.com/v2fly/v2ray-core/v5/common/net"
    14  	"github.com/v2fly/v2ray-core/v5/common/serial"
    15  	"github.com/v2fly/v2ray-core/v5/features/outbound"
    16  	"github.com/v2fly/v2ray-core/v5/features/routing"
    17  	"github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg"
    18  )
    19  
    20  type Rule struct {
    21  	Tag       string
    22  	Balancer  *Balancer
    23  	Condition Condition
    24  }
    25  
    26  func (r *Rule) GetTag() (string, error) {
    27  	if r.Balancer != nil {
    28  		return r.Balancer.PickOutbound()
    29  	}
    30  	return r.Tag, nil
    31  }
    32  
    33  // Apply checks rule matching of current routing context.
    34  func (r *Rule) Apply(ctx routing.Context) bool {
    35  	return r.Condition.Apply(ctx)
    36  }
    37  
    38  func (rr *RoutingRule) BuildCondition() (Condition, error) {
    39  	conds := NewConditionChan()
    40  
    41  	if len(rr.Domain) > 0 {
    42  		cond, err := NewDomainMatcher(rr.DomainMatcher, rr.Domain)
    43  		if err != nil {
    44  			return nil, newError("failed to build domain condition").Base(err)
    45  		}
    46  		conds.Add(cond)
    47  	}
    48  
    49  	var geoDomains []*routercommon.Domain
    50  	for _, geo := range rr.GeoDomain {
    51  		geoDomains = append(geoDomains, geo.Domain...)
    52  	}
    53  	if len(geoDomains) > 0 {
    54  		cond, err := NewDomainMatcher(rr.DomainMatcher, geoDomains)
    55  		if err != nil {
    56  			return nil, newError("failed to build geo domain condition").Base(err)
    57  		}
    58  		conds.Add(cond)
    59  	}
    60  
    61  	if len(rr.UserEmail) > 0 {
    62  		conds.Add(NewUserMatcher(rr.UserEmail))
    63  	}
    64  
    65  	if len(rr.InboundTag) > 0 {
    66  		conds.Add(NewInboundTagMatcher(rr.InboundTag))
    67  	}
    68  
    69  	if rr.PortList != nil {
    70  		conds.Add(NewPortMatcher(rr.PortList, false))
    71  	} else if rr.PortRange != nil {
    72  		conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false))
    73  	}
    74  
    75  	if rr.SourcePortList != nil {
    76  		conds.Add(NewPortMatcher(rr.SourcePortList, true))
    77  	}
    78  
    79  	if len(rr.Networks) > 0 {
    80  		conds.Add(NewNetworkMatcher(rr.Networks))
    81  	} else if rr.NetworkList != nil {
    82  		conds.Add(NewNetworkMatcher(rr.NetworkList.Network))
    83  	}
    84  
    85  	if len(rr.Geoip) > 0 {
    86  		cond, err := NewMultiGeoIPMatcher(rr.Geoip, false)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		conds.Add(cond)
    91  	} else if len(rr.Cidr) > 0 {
    92  		cond, err := NewMultiGeoIPMatcher([]*routercommon.GeoIP{{Cidr: rr.Cidr}}, false)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		conds.Add(cond)
    97  	}
    98  
    99  	if len(rr.SourceGeoip) > 0 {
   100  		cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		conds.Add(cond)
   105  	} else if len(rr.SourceCidr) > 0 {
   106  		cond, err := NewMultiGeoIPMatcher([]*routercommon.GeoIP{{Cidr: rr.SourceCidr}}, true)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  		conds.Add(cond)
   111  	}
   112  
   113  	if len(rr.Protocol) > 0 {
   114  		conds.Add(NewProtocolMatcher(rr.Protocol))
   115  	}
   116  
   117  	if len(rr.Attributes) > 0 {
   118  		cond, err := NewAttributeMatcher(rr.Attributes)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		conds.Add(cond)
   123  	}
   124  
   125  	if conds.Len() == 0 {
   126  		return nil, newError("this rule has no effective fields").AtWarning()
   127  	}
   128  
   129  	return conds, nil
   130  }
   131  
   132  // Build builds the balancing rule
   133  func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) {
   134  	switch br.Strategy {
   135  	case "leastping":
   136  		i, err := serial.GetInstanceOf(br.StrategySettings)
   137  		if err != nil {
   138  			return nil, err
   139  		}
   140  		s, ok := i.(*StrategyLeastPingConfig)
   141  		if !ok {
   142  			return nil, newError("not a StrategyLeastPingConfig").AtError()
   143  		}
   144  		return &Balancer{
   145  			selectors: br.OutboundSelector,
   146  			strategy:  &LeastPingStrategy{config: s},
   147  			ohm:       ohm,
   148  		}, nil
   149  	case "leastload":
   150  		i, err := serial.GetInstanceOf(br.StrategySettings)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		s, ok := i.(*StrategyLeastLoadConfig)
   155  		if !ok {
   156  			return nil, newError("not a StrategyLeastLoadConfig").AtError()
   157  		}
   158  		leastLoadStrategy := NewLeastLoadStrategy(s)
   159  		return &Balancer{
   160  			selectors: br.OutboundSelector,
   161  			ohm:       ohm, fallbackTag: br.FallbackTag,
   162  			strategy: leastLoadStrategy,
   163  		}, nil
   164  	case "random":
   165  		fallthrough
   166  	case "":
   167  		var randomStrategy *RandomStrategy
   168  		if br.StrategySettings != nil {
   169  			i, err := serial.GetInstanceOf(br.StrategySettings)
   170  			if err != nil {
   171  				return nil, err
   172  			}
   173  			s, ok := i.(*StrategyRandomConfig)
   174  			if !ok {
   175  				return nil, newError("not a StrategyRandomConfig").AtError()
   176  			}
   177  			randomStrategy = NewRandomStrategy(s)
   178  		}
   179  		return &Balancer{
   180  			selectors: br.OutboundSelector,
   181  			ohm:       ohm, fallbackTag: br.FallbackTag,
   182  			strategy: randomStrategy,
   183  		}, nil
   184  	default:
   185  		return nil, newError("unrecognized balancer type")
   186  	}
   187  }
   188  
   189  func (br *BalancingRule) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error {
   190  	type BalancingRuleStub struct {
   191  		Tag              string          `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
   192  		OutboundSelector []string        `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"`
   193  		Strategy         string          `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"`
   194  		StrategySettings json.RawMessage `protobuf:"bytes,4,opt,name=strategy_settings,json=strategySettings,proto3" json:"strategy_settings,omitempty"`
   195  		FallbackTag      string          `protobuf:"bytes,5,opt,name=fallback_tag,json=fallbackTag,proto3" json:"fallback_tag,omitempty"`
   196  	}
   197  
   198  	var stub BalancingRuleStub
   199  	if err := json.Unmarshal(bytes, &stub); err != nil {
   200  		return err
   201  	}
   202  	if stub.Strategy == "" {
   203  		stub.Strategy = "random"
   204  	}
   205  	settingsPack, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(context.TODO(), "balancer", stub.Strategy, stub.StrategySettings)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	br.StrategySettings = serial.ToTypedMessage(settingsPack)
   210  
   211  	br.Tag = stub.Tag
   212  	br.Strategy = stub.Strategy
   213  	br.OutboundSelector = stub.OutboundSelector
   214  	br.FallbackTag = stub.FallbackTag
   215  
   216  	return nil
   217  }