github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/parameters/bpf.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 parameters
    21  
    22  import (
    23  	"encoding/json"
    24  
    25  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    26  	"golang.org/x/net/bpf"
    27  )
    28  
    29  // BPFProgramSpec specifies a BPF program. The Name field is informational and
    30  // may be used for logging. The Instructions field is a list of values which
    31  // map to golang.org/x/net/bpf.Instruction and which can be marshaled.
    32  type BPFProgramSpec struct {
    33  	Name         string
    34  	Instructions []BPFInstructionSpec
    35  }
    36  
    37  // Validate validates a BPF program spec.
    38  func (s *BPFProgramSpec) Validate() error {
    39  	if s.Name == "" {
    40  		return errors.TraceNew("missing name")
    41  	}
    42  	if len(s.Instructions) < 1 {
    43  		return errors.TraceNew("missing instructions")
    44  	}
    45  	_, err := s.Assemble()
    46  	return errors.Trace(err)
    47  }
    48  
    49  // Assemble converts the Instructions to equivilent
    50  // golang.org/x/net/bpf.Instruction values and assembles these into raw
    51  // instructions suitable for attaching to a socket.
    52  func (s *BPFProgramSpec) Assemble() ([]bpf.RawInstruction, error) {
    53  
    54  	if len(s.Instructions) == 0 {
    55  		return nil, errors.TraceNew("empty program")
    56  	}
    57  
    58  	program := make([]bpf.Instruction, len(s.Instructions))
    59  	for i, instructionSpec := range s.Instructions {
    60  		instruction, err := instructionSpec.GetInstruction()
    61  		if err != nil {
    62  			return nil, errors.Trace(err)
    63  		}
    64  		program[i] = instruction
    65  	}
    66  
    67  	raw, err := bpf.Assemble(program)
    68  	if err != nil {
    69  		return nil, errors.Trace(err)
    70  	}
    71  
    72  	return raw, nil
    73  }
    74  
    75  // BPFInstructionSpec represents a golang.org/x/net/bpf.Instruction and can be
    76  // marshaled.
    77  type BPFInstructionSpec struct {
    78  	Op   string
    79  	Args json.RawMessage
    80  }
    81  
    82  // GetInstruction coverts a BPFInstructionSpec to the equivilent
    83  // golang.org/x/net/bpf.Instruction.
    84  func (s *BPFInstructionSpec) GetInstruction() (bpf.Instruction, error) {
    85  	switch s.Op {
    86  	case "ALUOpConstant":
    87  		var instruction bpf.ALUOpConstant
    88  		err := json.Unmarshal(s.Args, &instruction)
    89  		if err != nil {
    90  			return nil, errors.Trace(err)
    91  		}
    92  		return instruction, nil
    93  	case "ALUOpX":
    94  		var instruction bpf.ALUOpX
    95  		err := json.Unmarshal(s.Args, &instruction)
    96  		if err != nil {
    97  			return nil, errors.Trace(err)
    98  		}
    99  		return instruction, nil
   100  	case "Jump":
   101  		var instruction bpf.Jump
   102  		err := json.Unmarshal(s.Args, &instruction)
   103  		if err != nil {
   104  			return nil, errors.Trace(err)
   105  		}
   106  		return instruction, nil
   107  	case "JumpIf":
   108  		var instruction bpf.JumpIf
   109  		err := json.Unmarshal(s.Args, &instruction)
   110  		if err != nil {
   111  			return nil, errors.Trace(err)
   112  		}
   113  		return instruction, nil
   114  	case "JumpIfX":
   115  		var instruction bpf.JumpIfX
   116  		err := json.Unmarshal(s.Args, &instruction)
   117  		if err != nil {
   118  			return nil, errors.Trace(err)
   119  		}
   120  		return instruction, nil
   121  	case "LoadAbsolute":
   122  		var instruction bpf.LoadAbsolute
   123  		err := json.Unmarshal(s.Args, &instruction)
   124  		if err != nil {
   125  			return nil, errors.Trace(err)
   126  		}
   127  		return instruction, nil
   128  	case "LoadConstant":
   129  		var instruction bpf.LoadConstant
   130  		err := json.Unmarshal(s.Args, &instruction)
   131  		if err != nil {
   132  			return nil, errors.Trace(err)
   133  		}
   134  		return instruction, nil
   135  	case "LoadExtension":
   136  		var instruction bpf.LoadExtension
   137  		err := json.Unmarshal(s.Args, &instruction)
   138  		if err != nil {
   139  			return nil, errors.Trace(err)
   140  		}
   141  		return instruction, nil
   142  	case "LoadIndirect":
   143  		var instruction bpf.LoadIndirect
   144  		err := json.Unmarshal(s.Args, &instruction)
   145  		if err != nil {
   146  			return nil, errors.Trace(err)
   147  		}
   148  		return instruction, nil
   149  	case "LoadMemShift":
   150  		var instruction bpf.LoadMemShift
   151  		err := json.Unmarshal(s.Args, &instruction)
   152  		if err != nil {
   153  			return nil, errors.Trace(err)
   154  		}
   155  		return instruction, nil
   156  	case "LoadScratch":
   157  		var instruction bpf.LoadScratch
   158  		err := json.Unmarshal(s.Args, &instruction)
   159  		if err != nil {
   160  			return nil, errors.Trace(err)
   161  		}
   162  		return instruction, nil
   163  	case "NegateA":
   164  		var instruction bpf.NegateA
   165  		err := json.Unmarshal(s.Args, &instruction)
   166  		if err != nil {
   167  			return nil, errors.Trace(err)
   168  		}
   169  		return instruction, nil
   170  	case "RetA":
   171  		var instruction bpf.RetA
   172  		err := json.Unmarshal(s.Args, &instruction)
   173  		if err != nil {
   174  			return nil, errors.Trace(err)
   175  		}
   176  		return instruction, nil
   177  	case "RetConstant":
   178  		var instruction bpf.RetConstant
   179  		err := json.Unmarshal(s.Args, &instruction)
   180  		if err != nil {
   181  			return nil, errors.Trace(err)
   182  		}
   183  		return instruction, nil
   184  	case "StoreScratch":
   185  		var instruction bpf.StoreScratch
   186  		err := json.Unmarshal(s.Args, &instruction)
   187  		if err != nil {
   188  			return nil, errors.Trace(err)
   189  		}
   190  		return instruction, nil
   191  	case "TAX":
   192  		var instruction bpf.TAX
   193  		err := json.Unmarshal(s.Args, &instruction)
   194  		if err != nil {
   195  			return nil, errors.Trace(err)
   196  		}
   197  		return instruction, nil
   198  	case "TXA":
   199  		var instruction bpf.TXA
   200  		err := json.Unmarshal(s.Args, &instruction)
   201  		if err != nil {
   202  			return nil, errors.Trace(err)
   203  		}
   204  		return instruction, nil
   205  	}
   206  
   207  	return nil, errors.Tracef("unknown bpf instruction: %s", s.Op)
   208  }