github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/port/slirp4netns/slirp4netns.go (about) 1 package slirp4netns 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "strings" 11 "sync" 12 13 "github.com/rootless-containers/rootlesskit/v2/pkg/api" 14 "github.com/rootless-containers/rootlesskit/v2/pkg/port" 15 "github.com/rootless-containers/rootlesskit/v2/pkg/port/portutil" 16 ) 17 18 func NewParentDriver(logWriter io.Writer, apiSocketPath string) (port.ParentDriver, error) { 19 if apiSocketPath == "" { 20 return nil, errors.New("api socket path is not set") 21 } 22 d := driver{ 23 logWriter: logWriter, 24 ports: make(map[int]*port.Status, 0), 25 apiSocketPath: apiSocketPath, 26 } 27 return &d, nil 28 } 29 30 type driver struct { 31 logWriter io.Writer 32 apiSocketPath string 33 mu sync.Mutex 34 childIP string // can be empty 35 ports map[int]*port.Status 36 } 37 38 func (d *driver) Info(ctx context.Context) (*api.PortDriverInfo, error) { 39 info := &api.PortDriverInfo{ 40 Driver: "slirp4netns", 41 // No IPv6 support yet 42 Protos: []string{"tcp", "tcp4", "udp", "udp4"}, 43 DisallowLoopbackChildIP: true, 44 } 45 return info, nil 46 } 47 48 func (d *driver) OpaqueForChild() map[string]string { 49 // NOP, as this driver does not have child-side logic. 50 return nil 51 } 52 53 func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{}, cctx *port.ChildContext) error { 54 if cctx != nil && cctx.IP != nil && cctx.IP.To4() != nil { 55 d.childIP = cctx.IP.To4().String() 56 } 57 initComplete <- struct{}{} 58 <-quit 59 return nil 60 } 61 62 func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, error) { 63 d.mu.Lock() 64 defer d.mu.Unlock() 65 err := portutil.ValidatePortSpec(spec, d.ports) 66 if err != nil { 67 return nil, err 68 } 69 if strings.HasSuffix(spec.Proto, "6") { 70 return nil, fmt.Errorf("unsupported protocol %q", spec.Proto) 71 } 72 proto := strings.TrimSuffix(spec.Proto, "4") 73 ip := spec.ChildIP 74 if ip == "" { 75 ip = d.childIP 76 } else { 77 p := net.ParseIP(ip) 78 if p == nil { 79 return nil, fmt.Errorf("invalid IP: %q", ip) 80 } 81 p = p.To4() 82 if p == nil { 83 return nil, fmt.Errorf("unsupported IP (v6?): %s", ip) 84 } 85 ip = p.String() 86 } 87 req := request{ 88 Execute: "add_hostfwd", 89 Arguments: addHostFwdArguments{ 90 Proto: proto, 91 HostAddr: spec.ParentIP, 92 HostPort: spec.ParentPort, 93 GuestAddr: ip, 94 GuestPort: spec.ChildPort, 95 }, 96 } 97 rep, err := callAPI(d.apiSocketPath, req) 98 if err != nil { 99 return nil, err 100 } 101 if len(rep.Error) != 0 { 102 return nil, fmt.Errorf("reply.Error: %+v", rep.Error) 103 } 104 idIntf, ok := rep.Return["id"] 105 if !ok { 106 return nil, fmt.Errorf("unexpected reply: %+v", rep) 107 } 108 idFloat, ok := idIntf.(float64) 109 if !ok { 110 return nil, fmt.Errorf("unexpected id: %+v", idIntf) 111 } 112 id := int(idFloat) 113 st := port.Status{ 114 ID: id, 115 Spec: spec, 116 } 117 d.ports[id] = &st 118 return &st, nil 119 } 120 121 func (d *driver) ListPorts(ctx context.Context) ([]port.Status, error) { 122 var ports []port.Status 123 d.mu.Lock() 124 for _, p := range d.ports { 125 ports = append(ports, *p) 126 } 127 d.mu.Unlock() 128 return ports, nil 129 } 130 131 func (d *driver) RemovePort(ctx context.Context, id int) error { 132 d.mu.Lock() 133 defer d.mu.Unlock() 134 req := request{ 135 Execute: "remove_hostfwd", 136 Arguments: removeHostFwdArguments{ 137 ID: id, 138 }, 139 } 140 rep, err := callAPI(d.apiSocketPath, req) 141 if err != nil { 142 return err 143 } 144 if len(rep.Error) != 0 { 145 return fmt.Errorf("reply.Error: %v", rep.Error) 146 } 147 delete(d.ports, id) 148 return nil 149 } 150 151 type addHostFwdArguments struct { 152 Proto string `json:"proto"` 153 HostAddr string `json:"host_addr"` 154 HostPort int `json:"host_port"` 155 GuestAddr string `json:"guest_addr"` 156 GuestPort int `json:"guest_port"` 157 } 158 159 type removeHostFwdArguments struct { 160 ID int `json:"id"` 161 } 162 163 type request struct { 164 Execute string `json:"execute"` 165 Arguments interface{} `json:"arguments"` 166 } 167 168 type reply struct { 169 Return map[string]interface{} `json:"return,omitempty"` 170 Error map[string]interface{} `json:"error,omitempty"` 171 } 172 173 func callAPI(apiSocketPath string, req request) (*reply, error) { 174 addr := &net.UnixAddr{Net: "unix", Name: apiSocketPath} 175 conn, err := net.DialUnix("unix", nil, addr) 176 if err != nil { 177 return nil, err 178 } 179 defer conn.Close() 180 if err := json.NewEncoder(conn).Encode(req); err != nil { 181 return nil, err 182 } 183 if err := conn.CloseWrite(); err != nil { 184 return nil, err 185 } 186 b, err := io.ReadAll(conn) 187 if err != nil { 188 return nil, err 189 } 190 var rep reply 191 if err := json.Unmarshal(b, &rep); err != nil { 192 return nil, err 193 } 194 return &rep, nil 195 } 196 197 func NewChildDriver() port.ChildDriver { 198 return &childDriver{} 199 } 200 201 type childDriver struct { 202 } 203 204 func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}, detachedNetNSPath string) error { 205 // NOP 206 <-quit 207 return nil 208 }