github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/packetman.go (about)

     1  /*
     2   * Copyright (c) 2020, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package server
    21  
    22  import (
    23  	"net"
    24  
    25  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    26  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/packetman"
    27  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
    28  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
    29  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    30  )
    31  
    32  func makePacketManipulatorConfig(
    33  	support *SupportServices) (*packetman.Config, error) {
    34  
    35  	// Packet interception is configured for any tunnel protocol port that _may_
    36  	// use packet manipulation. A future hot reload of tactics may apply specs to
    37  	// any of these protocols.
    38  
    39  	var ports []int
    40  	for tunnelProtocol, port := range support.Config.TunnelProtocolPorts {
    41  		if protocol.TunnelProtocolMayUseServerPacketManipulation(tunnelProtocol) {
    42  			ports = append(ports, port)
    43  		}
    44  	}
    45  
    46  	selectSpecName := func(protocolPort int, clientIP net.IP) (string, interface{}) {
    47  
    48  		specName, extraData, err := selectPacketManipulationSpec(
    49  			support, protocolPort, clientIP)
    50  		if err != nil {
    51  			log.WithTraceFields(
    52  				LogFields{"error": err}).Warning(
    53  				"failed to get tactics for packet manipulation")
    54  			return "", nil
    55  		}
    56  
    57  		return specName, extraData
    58  	}
    59  
    60  	specs, err := getPacketManipulationSpecs(support)
    61  	if err != nil {
    62  		return nil, errors.Trace(err)
    63  	}
    64  
    65  	return &packetman.Config{
    66  		Logger:                    CommonLogger(log),
    67  		SudoNetworkConfigCommands: support.Config.PacketTunnelSudoNetworkConfigCommands,
    68  		QueueNumber:               1,
    69  		ProtocolPorts:             ports,
    70  		Specs:                     specs,
    71  		SelectSpecName:            selectSpecName,
    72  	}, nil
    73  }
    74  
    75  func getPacketManipulationSpecs(support *SupportServices) ([]*packetman.Spec, error) {
    76  
    77  	// By convention, parameters.ServerPacketManipulationSpecs should be in
    78  	// DefaultTactics, not FilteredTactics; and ServerTacticsParametersCache
    79  	// ignores Tactics.Probability.
    80  
    81  	p, err := support.ServerTacticsParametersCache.Get(NewGeoIPData())
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  
    86  	if p.IsNil() {
    87  		// No tactics are configured; return an empty spec list.
    88  		return nil, nil
    89  	}
    90  
    91  	paramSpecs := p.PacketManipulationSpecs(parameters.ServerPacketManipulationSpecs)
    92  
    93  	specs := make([]*packetman.Spec, len(paramSpecs))
    94  	for i, spec := range paramSpecs {
    95  		packetmanSpec := packetman.Spec(*spec)
    96  		specs[i] = &packetmanSpec
    97  	}
    98  
    99  	return specs, nil
   100  }
   101  
   102  func reloadPacketManipulationSpecs(support *SupportServices) error {
   103  
   104  	specs, err := getPacketManipulationSpecs(support)
   105  	if err != nil {
   106  		return errors.Trace(err)
   107  	}
   108  
   109  	err = support.PacketManipulator.SetSpecs(specs)
   110  	if err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func selectPacketManipulationSpec(
   118  	support *SupportServices,
   119  	protocolPort int,
   120  	clientIP net.IP) (string, interface{}, error) {
   121  
   122  	// First check for replay, then check tactics.
   123  
   124  	// The intercepted packet source/protocol port is used to determine the
   125  	// tunnel protocol name, which is used to lookup first replay and then
   126  	// enabled packet manipulation specs in ServerProtocolPacketManipulations.
   127  	//
   128  	// This assumes that all TunnelProtocolMayUseServerPacketManipulation
   129  	// protocols run on distinct ports, which is true when all such protocols run
   130  	// over TCP.
   131  
   132  	targetTunnelProtocol := ""
   133  	for tunnelProtocol, port := range support.Config.TunnelProtocolPorts {
   134  		if port == protocolPort && protocol.TunnelProtocolMayUseServerPacketManipulation(tunnelProtocol) {
   135  			targetTunnelProtocol = tunnelProtocol
   136  			break
   137  		}
   138  	}
   139  	if targetTunnelProtocol == "" {
   140  		return "", nil, errors.Tracef(
   141  			"packet manipulation protocol port not found: %d", protocolPort)
   142  	}
   143  
   144  	geoIPData := support.GeoIPService.LookupIP(clientIP)
   145  
   146  	specName, doReplay := support.ReplayCache.GetReplayPacketManipulation(
   147  		targetTunnelProtocol, geoIPData)
   148  
   149  	// extraData records the is_server_replay metric.
   150  	extraData := doReplay
   151  
   152  	if doReplay {
   153  		return specName, extraData, nil
   154  	}
   155  
   156  	// GeoIP tactics filtering is applied when getting
   157  	// ServerPacketManipulationProbability and ServerProtocolPacketManipulations.
   158  	//
   159  	// When there are multiple enabled specs, one is selected at random.
   160  	//
   161  	// Specs under the key "All" apply to all protocols. Duplicate specs per
   162  	// entry are allowed, enabling weighted selection. If a spec appears in both
   163  	// "All" and a specific protocol, the duplicate(s) are retained.
   164  
   165  	p, err := support.ServerTacticsParametersCache.Get(geoIPData)
   166  	if err != nil {
   167  		return "", nil, errors.Trace(err)
   168  	}
   169  
   170  	if p.IsNil() {
   171  		// No tactics are configured; select no spec.
   172  		return "", extraData, nil
   173  	}
   174  
   175  	if !p.WeightedCoinFlip(parameters.ServerPacketManipulationProbability) {
   176  		return "", extraData, nil
   177  	}
   178  
   179  	protocolSpecs := p.ProtocolPacketManipulations(
   180  		parameters.ServerProtocolPacketManipulations)
   181  
   182  	// TODO: cache merged per-protocol + "All" lists?
   183  	specNames, ok := protocolSpecs[targetTunnelProtocol]
   184  	if !ok {
   185  		specNames = []string{}
   186  	}
   187  
   188  	allProtocolsSpecNames, ok := protocolSpecs[protocol.TUNNEL_PROTOCOLS_ALL]
   189  	if ok {
   190  		specNames = append(specNames, allProtocolsSpecNames...)
   191  	}
   192  
   193  	if len(specNames) < 1 {
   194  		// Tactics contains no candidate specs for this protocol.
   195  		return "", extraData, nil
   196  	}
   197  
   198  	return specNames[prng.Range(0, len(specNames)-1)], extraData, nil
   199  }