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 }