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  }