github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/port/builtin/child/child.go (about) 1 package child 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net" 8 "os" 9 "strconv" 10 "strings" 11 12 "golang.org/x/sys/unix" 13 14 "github.com/containernetworking/plugins/pkg/ns" 15 "github.com/rootless-containers/rootlesskit/v2/pkg/lowlevelmsgutil" 16 "github.com/rootless-containers/rootlesskit/v2/pkg/port" 17 "github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin/msg" 18 opaquepkg "github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin/opaque" 19 ) 20 21 func NewDriver(logWriter io.Writer) port.ChildDriver { 22 return &childDriver{ 23 logWriter: logWriter, 24 } 25 } 26 27 type childDriver struct { 28 logWriter io.Writer 29 } 30 31 func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}, detachedNetNSPath string) error { 32 socketPath := opaque[opaquepkg.SocketPath] 33 if socketPath == "" { 34 return errors.New("socket path not set") 35 } 36 childReadyPipePath := opaque[opaquepkg.ChildReadyPipePath] 37 if childReadyPipePath == "" { 38 return errors.New("child ready pipe path not set") 39 } 40 childReadyPipeW, err := os.OpenFile(childReadyPipePath, os.O_WRONLY, os.ModeNamedPipe) 41 if err != nil { 42 return err 43 } 44 ln, err := net.ListenUnix("unix", &net.UnixAddr{ 45 Name: socketPath, 46 Net: "unix", 47 }) 48 if err != nil { 49 return err 50 } 51 // write nothing, just close 52 if err = childReadyPipeW.Close(); err != nil { 53 return err 54 } 55 stopAccept := make(chan struct{}, 1) 56 go func() { 57 <-quit 58 stopAccept <- struct{}{} 59 ln.Close() 60 }() 61 for { 62 c, err := ln.AcceptUnix() 63 if err != nil { 64 select { 65 case <-stopAccept: 66 return nil 67 default: 68 } 69 return err 70 } 71 go func() { 72 if rerr := d.routine(c, detachedNetNSPath); rerr != nil { 73 rep := msg.Reply{ 74 Error: rerr.Error(), 75 } 76 lowlevelmsgutil.MarshalToWriter(c, &rep) 77 } 78 c.Close() 79 }() 80 } 81 } 82 83 func (d *childDriver) routine(c *net.UnixConn, detachedNetNSPath string) error { 84 var req msg.Request 85 if _, err := lowlevelmsgutil.UnmarshalFromReader(c, &req); err != nil { 86 return err 87 } 88 switch req.Type { 89 case msg.RequestTypeInit: 90 return d.handleConnectInit(c, &req) 91 case msg.RequestTypeConnect: 92 if detachedNetNSPath == "" { 93 return d.handleConnectRequest(c, &req) 94 } else { 95 return ns.WithNetNSPath(detachedNetNSPath, func(_ ns.NetNS) error { 96 return d.handleConnectRequest(c, &req) 97 }) 98 } 99 default: 100 return fmt.Errorf("unknown request type %q", req.Type) 101 } 102 } 103 104 func (d *childDriver) handleConnectInit(c *net.UnixConn, req *msg.Request) error { 105 _, err := lowlevelmsgutil.MarshalToWriter(c, nil) 106 return err 107 } 108 109 func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) error { 110 switch req.Proto { 111 case "tcp": 112 case "tcp4": 113 case "tcp6": 114 case "udp": 115 case "udp4": 116 case "udp6": 117 default: 118 return fmt.Errorf("unknown proto: %q", req.Proto) 119 } 120 // dialProto does not need "4", "6" suffix 121 dialProto := strings.TrimSuffix(strings.TrimSuffix(req.Proto, "6"), "4") 122 var dialer net.Dialer 123 ip := req.IP 124 if ip == "" { 125 ip = "127.0.0.1" 126 if req.ParentIP != "" { 127 if req.ParentIP != req.HostGatewayIP && req.ParentIP != "0.0.0.0" { 128 ip = req.ParentIP 129 } 130 } 131 } else { 132 p := net.ParseIP(ip) 133 if p == nil { 134 return fmt.Errorf("invalid IP: %q", ip) 135 } 136 ip = p.String() 137 } 138 targetConn, err := dialer.Dial(dialProto, net.JoinHostPort(ip, strconv.Itoa(req.Port))) 139 if err != nil { 140 return err 141 } 142 defer targetConn.Close() // no effect on duplicated FD 143 targetConnFiler, ok := targetConn.(filer) 144 if !ok { 145 return fmt.Errorf("unknown target connection: %+v", targetConn) 146 } 147 targetConnFile, err := targetConnFiler.File() 148 if err != nil { 149 return err 150 } 151 defer targetConnFile.Close() 152 oob := unix.UnixRights(int(targetConnFile.Fd())) 153 f, err := c.File() 154 if err != nil { 155 return err 156 } 157 defer f.Close() 158 for { 159 err = unix.Sendmsg(int(f.Fd()), []byte("dummy"), oob, nil, 0) 160 if err != unix.EINTR { 161 break 162 } 163 } 164 return err 165 } 166 167 // filer is implemented by *net.TCPConn and *net.UDPConn 168 type filer interface { 169 File() (f *os.File, err error) 170 }