github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/bpf.go (about)

     1  // +build linux
     2  
     3  /*
     4   * Copyright (c) 2020, Psiphon Inc.
     5   * All rights reserved.
     6   *
     7   * This program is free software: you can redistribute it and/or modify
     8   * it under the terms of the GNU General Public License as published by
     9   * the Free Software Foundation, either version 3 of the License, or
    10   * (at your option) any later version.
    11   *
    12   * This program is distributed in the hope that it will be useful,
    13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15   * GNU General Public License for more details.
    16   *
    17   * You should have received a copy of the GNU General Public License
    18   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19   *
    20   */
    21  
    22  package server
    23  
    24  import (
    25  	"context"
    26  	"net"
    27  	"syscall"
    28  	"unsafe"
    29  
    30  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    31  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
    32  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
    33  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    34  	"golang.org/x/net/bpf"
    35  	"golang.org/x/sys/unix"
    36  )
    37  
    38  // ServerBPFEnabled indicates if BPF functionality is enabled.
    39  func ServerBPFEnabled() bool {
    40  	return true
    41  }
    42  
    43  // newTCPListenerWithBPF creates a TCP net.Listener, optionally attaching
    44  // the BPF program specified by the tactics parameter BPFServerTCPProgram.
    45  func newTCPListenerWithBPF(
    46  	support *SupportServices,
    47  	localAddress string) (net.Listener, string, error) {
    48  
    49  	// Limitations:
    50  	// - BPFServerTCPProgram must be set unconditionally as neither client GeoIP
    51  	//   nor API parameters are checked before the BPF is attached.
    52  	// - Currently, lhe listener BPF is not reset upon tactics hot reload.
    53  
    54  	havePBFProgram, programName, rawInstructions, err := getBPFProgram(support)
    55  	if err != nil {
    56  		log.WithTraceFields(
    57  			LogFields{"error": err}).Warning("failed to get BPF program for listener")
    58  		// If tactics is somehow misconfigured, keep running.
    59  	}
    60  
    61  	listenConfig := &net.ListenConfig{}
    62  
    63  	if havePBFProgram {
    64  
    65  		programName = programName
    66  
    67  		// Tactics parameters validation ensures BPFProgramInstructions has len >= 1.
    68  		listenConfig.Control = func(network, address string, c syscall.RawConn) error {
    69  			var setSockOptError error
    70  			err := c.Control(func(fd uintptr) {
    71  				setSockOptError = unix.SetsockoptSockFprog(
    72  					int(fd),
    73  					unix.SOL_SOCKET,
    74  					unix.SO_ATTACH_FILTER,
    75  					&unix.SockFprog{
    76  						Len:    uint16(len(rawInstructions)),
    77  						Filter: (*unix.SockFilter)(unsafe.Pointer(&rawInstructions[0])),
    78  					})
    79  			})
    80  			if err == nil {
    81  				err = setSockOptError
    82  			}
    83  			return errors.Trace(err)
    84  		}
    85  	}
    86  
    87  	listener, err := listenConfig.Listen(context.Background(), "tcp", localAddress)
    88  
    89  	return listener, programName, errors.Trace(err)
    90  }
    91  
    92  func getBPFProgram(support *SupportServices) (bool, string, []bpf.RawInstruction, error) {
    93  
    94  	p, err := support.ServerTacticsParametersCache.Get(NewGeoIPData())
    95  	if err != nil {
    96  		return false, "", nil, errors.Trace(err)
    97  	}
    98  
    99  	if p.IsNil() {
   100  		// No tactics are configured; BPF is disabled.
   101  		return false, "", nil, nil
   102  	}
   103  
   104  	seed, err := protocol.DeriveBPFServerProgramPRNGSeed(support.Config.ObfuscatedSSHKey)
   105  	if err != nil {
   106  		return false, "", nil, errors.Trace(err)
   107  	}
   108  
   109  	PRNG := prng.NewPRNGWithSeed(seed)
   110  
   111  	if !PRNG.FlipWeightedCoin(
   112  		p.Float(parameters.BPFServerTCPProbability)) {
   113  		return false, "", nil, nil
   114  	}
   115  
   116  	ok, name, rawInstructions := p.BPFProgram(parameters.BPFServerTCPProgram)
   117  	return ok, name, rawInstructions, nil
   118  }