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 }