1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
package tracer
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"sync"
"github.com/rootless-containers/bypass4netns/pkg/util"
"golang.org/x/sys/unix"
)
type Tracer struct {
logPath string
tracerCmd *exec.Cmd
reader io.Reader
writer io.Writer
lock sync.Mutex
}
func NewTracer(logPath string) *Tracer {
return &Tracer{
logPath: logPath,
lock: sync.Mutex{},
}
}
// StartTracer starts tracer in NS associated with the PID.
func (x *Tracer) StartTracer(ctx context.Context, pid int) error {
selfExe, err := os.Executable()
if err != nil {
return err
}
nsenter, err := exec.LookPath("nsenter")
if err != nil {
return err
}
nsenterFlags := []string{
"-t", strconv.Itoa(pid),
"-F",
"-n",
}
selfPid := os.Getpid()
ok, err := util.SameUserNS(pid, selfPid)
if err != nil {
return fmt.Errorf("failed to check sameUserNS(%d, %d)", pid, selfPid)
}
if !ok {
nsenterFlags = append(nsenterFlags, "-U", "--preserve-credentials")
}
nsenterFlags = append(nsenterFlags, "--", selfExe, "--tracer-agent", "--log-file", x.logPath)
x.tracerCmd = exec.CommandContext(ctx, nsenter, nsenterFlags...)
x.tracerCmd.SysProcAttr = &unix.SysProcAttr{
Pdeathsig: unix.SIGTERM,
}
x.tracerCmd.Stderr = os.Stderr
x.reader, x.tracerCmd.Stdout = io.Pipe()
x.tracerCmd.Stdin, x.writer = io.Pipe()
if err := x.tracerCmd.Start(); err != nil {
return fmt.Errorf("failed to start %v: %w", x.tracerCmd.Args, err)
}
return nil
}
func (x *Tracer) RegisterForwardPorts(ports []int) error {
cmd := TracerCommand{
Cmd: RegisterForwardPorts,
ForwardingPorts: ports,
}
m, err := json.Marshal(cmd)
if err != nil {
return err
}
writeSize, err := x.writer.Write(m)
if err != nil {
return err
}
if writeSize != len(m) {
return fmt.Errorf("unexpected written size expected=%d actual=%d", len(m), writeSize)
}
dec := json.NewDecoder(x.reader)
var resp TracerCommand
err = dec.Decode(&resp)
if err != nil {
return fmt.Errorf("invalid response: %q", err)
}
if resp.Cmd != Ok {
return fmt.Errorf("unexpected response: %d", resp.Cmd)
}
return nil
}
func (x *Tracer) ConnectToAddress(addrs []string) ([]string, error) {
x.lock.Lock()
defer x.lock.Unlock()
cmd := TracerCommand{
Cmd: ConnectToAddress,
DestinationAddress: addrs,
}
m, err := json.Marshal(cmd)
if err != nil {
return nil, err
}
writeSize, err := x.writer.Write(m)
if err != nil {
return nil, err
}
if writeSize != len(m) {
return nil, fmt.Errorf("unexpected written size expected=%d actual=%d", len(m), writeSize)
}
dec := json.NewDecoder(x.reader)
var resp TracerCommand
err = dec.Decode(&resp)
if err != nil {
return nil, fmt.Errorf("invalid response: %q", err)
}
if resp.Cmd != Ok {
return nil, fmt.Errorf("unexpected response: %d", resp.Cmd)
}
return resp.DestinationAddress, nil
}
|