github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tapmanager/fdserver.go (about)

     1  /*
     2  Copyright 2017 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tapmanager
    18  
    19  import (
    20  	"encoding/binary"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  	"strings"
    27  	"sync"
    28  	"syscall"
    29  	"time"
    30  
    31  	"github.com/golang/glog"
    32  )
    33  
    34  const (
    35  	minAcceptErrorDelay = 5 * time.Millisecond
    36  	maxAcceptErrorDelay = 1 * time.Second
    37  	receiveFdTimeout    = 5 * time.Second
    38  	fdMagic             = 0x42424242
    39  	fdAdd               = 0
    40  	fdRelease           = 1
    41  	fdGet               = 2
    42  	fdRecover           = 3
    43  	fdResponse          = 0x80
    44  	fdAddResponse       = fdAdd | fdResponse
    45  	fdReleaseResponse   = fdRelease | fdResponse
    46  	fdGetResponse       = fdGet | fdResponse
    47  	fdRecoverResponse   = fdRecover | fdResponse
    48  	fdError             = 0xff
    49  )
    50  
    51  // FDManager denotes an object that provides 'master'-side
    52  // functionality of FDClient
    53  type FDManager interface {
    54  	// AddFDs adds new file descriptor to the FDManager and returns
    55  	// the associated data
    56  	AddFDs(key string, data interface{}) ([]byte, error)
    57  	// ReleaseFDs makes FDManager close the file descriptor and destroy
    58  	// any associated resources
    59  	ReleaseFDs(key string) error
    60  	// Recover recovers the state regarding the
    61  	// specified key. It's intended to be called after
    62  	// Virtlet restart.
    63  	Recover(key string, data interface{}) error
    64  }
    65  
    66  type fdHeader struct {
    67  	Magic    uint32
    68  	Command  uint8
    69  	DataSize uint32
    70  	OobSize  uint32
    71  	Key      [64]byte
    72  }
    73  
    74  func (hdr *fdHeader) getKey() string {
    75  	return strings.TrimSpace(string(hdr.Key[:]))
    76  }
    77  
    78  func fdKey(key string) [64]byte {
    79  	var r [64]byte
    80  	for n := range r {
    81  		if n < len(key) {
    82  			r[n] = key[n]
    83  		} else {
    84  			r[n] = 32
    85  		}
    86  	}
    87  	return r
    88  }
    89  
    90  // FDSource denotes an 'executive' part for FDServer which
    91  // creates and destroys (closes) the file descriptors and
    92  // associated resources
    93  type FDSource interface {
    94  	// GetFDs sets up a file descriptors based on key
    95  	// and extra data. It should return the file descriptor list,
    96  	// any data that should be passed back to the client
    97  	// invoking AddFDs() and an error, if any.
    98  	GetFDs(key string, data []byte) ([]int, []byte, error)
    99  	// Release destroys (closes) the file descriptor and
   100  	// any associated resources
   101  	Release(key string) error
   102  	// GetInfo returns the information which needs to be
   103  	// propagated back the FDClient upon GetFDs() call
   104  	GetInfo(key string) ([]byte, error)
   105  	// Recover recovers FDSource's state regarding the
   106  	// specified key. It's intended to be called after
   107  	// Virtlet restart.
   108  	Recover(key string, data []byte) error
   109  	// RetrieveFDs retrieves FDs in case the FD is null
   110  	RetrieveFDs(key string) ([]int, error)
   111  	// Stop stops any goroutines associated with FDSource
   112  	// but doesn't release the namespaces
   113  	Stop() error
   114  }
   115  
   116  // FDServer listens on a Unix domain socket, serving requests to
   117  // create, destroy and obtain file descriptors. It serves the purpose
   118  // of sending the file descriptors across mount namespace boundaries,
   119  // as well as making it easier to work around the Go namespace problem
   120  // (to be fixed in Go 1.10):
   121  // https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix When
   122  // the Go namespace problem is resolved, it should be possible to dumb
   123  // down FDServer by making it only serve GetFDs() requests, performing
   124  // other actions within the process boundary.
   125  type FDServer struct {
   126  	sync.Mutex
   127  	lst        *net.UnixListener
   128  	socketPath string
   129  	source     FDSource
   130  	fds        map[string][]int
   131  	stopCh     chan struct{}
   132  }
   133  
   134  // NewFDServer returns an FDServer for the specified socket path and
   135  // an FDSource
   136  func NewFDServer(socketPath string, source FDSource) *FDServer {
   137  	return &FDServer{
   138  		socketPath: socketPath,
   139  		source:     source,
   140  		fds:        make(map[string][]int),
   141  	}
   142  }
   143  
   144  func (s *FDServer) addFDs(key string, fds []int) bool {
   145  	s.Lock()
   146  	defer s.Unlock()
   147  	if _, found := s.fds[key]; found {
   148  		return false
   149  	}
   150  	s.fds[key] = fds
   151  	return true
   152  }
   153  
   154  func (s *FDServer) removeFDs(key string) {
   155  	s.Lock()
   156  	defer s.Unlock()
   157  	delete(s.fds, key)
   158  }
   159  
   160  func (s *FDServer) getFDs(key string) ([]int, error) {
   161  	s.Lock()
   162  	defer s.Unlock()
   163  	fds, found := s.fds[key]
   164  	if !found {
   165  		return nil, fmt.Errorf("bad fd key: %q", key)
   166  	}
   167  
   168  	var err error
   169  	if fds == nil {
   170  		// Run here means:
   171  		// first: the virtlet gets restarted and recoverNetworkNamespaces is called
   172  		//        but tap fd is missing
   173  		// then: VM gets restarted for some reasons
   174  		fds, err = s.source.RetrieveFDs(key)
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		s.fds[key] = fds
   179  	}
   180  	return fds, nil
   181  }
   182  
   183  func (s *FDServer) markAsRecovered(key string) error {
   184  	s.Lock()
   185  	defer s.Unlock()
   186  	if _, found := s.fds[key]; found {
   187  		return fmt.Errorf("fd key %q is already present and thus can't be properly recovered", key)
   188  	}
   189  	s.fds[key] = nil
   190  	return nil
   191  }
   192  
   193  // Serve makes FDServer listen on its socket in a new goroutine.
   194  // It returns immediately. Use Stop() to stop listening.
   195  func (s *FDServer) Serve() error {
   196  	s.Lock()
   197  	defer s.Unlock()
   198  	if s.stopCh != nil {
   199  		return errors.New("already listening")
   200  	}
   201  	addr, err := net.ResolveUnixAddr("unix", s.socketPath)
   202  	if err != nil {
   203  		return fmt.Errorf("failed to resolve unix addr %q: %v", s.socketPath, err)
   204  	}
   205  	l, err := net.ListenUnix("unix", addr)
   206  	if err != nil {
   207  		l.Close()
   208  		return fmt.Errorf("failed to listen on socket %q: %v", s.socketPath, err)
   209  	}
   210  	// Accept error handling is inspired by server.go in grpc
   211  	s.stopCh = make(chan struct{})
   212  	var delay time.Duration
   213  	go func() {
   214  		for {
   215  			conn, err := l.AcceptUnix()
   216  			if err != nil {
   217  				if temp, ok := err.(interface {
   218  					Temporary() bool
   219  				}); ok && temp.Temporary() {
   220  					glog.Warningf("Accept error: %v", err)
   221  					if delay == 0 {
   222  						delay = minAcceptErrorDelay
   223  					} else {
   224  						delay *= 2
   225  					}
   226  					if delay > maxAcceptErrorDelay {
   227  						delay = maxAcceptErrorDelay
   228  					}
   229  					select {
   230  					case <-time.After(delay):
   231  						continue
   232  					case <-s.stopCh:
   233  						return
   234  					}
   235  				}
   236  				select {
   237  				case <-s.stopCh:
   238  					// this error is expected
   239  					return
   240  				default:
   241  				}
   242  				glog.Errorf("Accept failed: %v", err)
   243  				break
   244  			}
   245  			go func() {
   246  				err := s.serveConn(conn)
   247  				if err != nil {
   248  					glog.Error(err)
   249  				}
   250  			}()
   251  		}
   252  	}()
   253  	return nil
   254  }
   255  
   256  func (s *FDServer) serveAdd(c *net.UnixConn, hdr *fdHeader) (*fdHeader, []byte, error) {
   257  	data := make([]byte, hdr.DataSize)
   258  	if len(data) > 0 {
   259  		if _, err := io.ReadFull(c, data); err != nil {
   260  			return nil, nil, fmt.Errorf("error reading payload: %v", err)
   261  		}
   262  	}
   263  	key := hdr.getKey()
   264  	fds, respData, err := s.source.GetFDs(key, data)
   265  	if err != nil {
   266  		return nil, nil, fmt.Errorf("error getting fd: %v", err)
   267  	}
   268  	if !s.addFDs(key, fds) {
   269  		return nil, nil, fmt.Errorf("fd key already exists: %q", err)
   270  	}
   271  	return &fdHeader{
   272  		Magic:    fdMagic,
   273  		Command:  fdAddResponse,
   274  		DataSize: uint32(len(respData)),
   275  		Key:      hdr.Key,
   276  	}, respData, nil
   277  }
   278  
   279  func (s *FDServer) serveRelease(hdr *fdHeader) (*fdHeader, error) {
   280  	key := hdr.getKey()
   281  	if err := s.source.Release(key); err != nil {
   282  		return nil, fmt.Errorf("error releasing fd: %v", err)
   283  	}
   284  	s.removeFDs(key)
   285  	return &fdHeader{
   286  		Magic:   fdMagic,
   287  		Command: fdReleaseResponse,
   288  		Key:     hdr.Key,
   289  	}, nil
   290  }
   291  
   292  func (s *FDServer) serveGet(c *net.UnixConn, hdr *fdHeader) (*fdHeader, []byte, []byte, error) {
   293  	key := hdr.getKey()
   294  	fds, err := s.getFDs(key)
   295  	if err != nil {
   296  		return nil, nil, nil, err
   297  	}
   298  	info, err := s.source.GetInfo(key)
   299  	if err != nil {
   300  		return nil, nil, nil, fmt.Errorf("can't get key info: %v", err)
   301  	}
   302  
   303  	rights := syscall.UnixRights(fds...)
   304  	return &fdHeader{
   305  		Magic:    fdMagic,
   306  		Command:  fdGetResponse,
   307  		DataSize: uint32(len(info)),
   308  		OobSize:  uint32(len(rights)),
   309  		Key:      hdr.Key,
   310  	}, info, rights, nil
   311  }
   312  
   313  func (s *FDServer) serveRecover(c *net.UnixConn, hdr *fdHeader) (*fdHeader, error) {
   314  	data := make([]byte, hdr.DataSize)
   315  	if len(data) > 0 {
   316  		if _, err := io.ReadFull(c, data); err != nil {
   317  			return nil, fmt.Errorf("error reading payload: %v", err)
   318  		}
   319  	}
   320  	key := hdr.getKey()
   321  	if err := s.source.Recover(key, data); err != nil {
   322  		return nil, fmt.Errorf("error recovering %q: %v", key, err)
   323  	}
   324  	if err := s.markAsRecovered(key); err != nil {
   325  		return nil, err
   326  	}
   327  	return &fdHeader{
   328  		Magic:   fdMagic,
   329  		Command: fdRecoverResponse,
   330  		Key:     hdr.Key,
   331  	}, nil
   332  }
   333  
   334  func (s *FDServer) serveConn(c *net.UnixConn) error {
   335  	defer c.Close()
   336  	for {
   337  		var hdr fdHeader
   338  		if err := binary.Read(c, binary.BigEndian, &hdr); err != nil {
   339  			if err == io.EOF {
   340  				break
   341  			}
   342  			return fmt.Errorf("error reading the header: %v", err)
   343  		}
   344  		if hdr.Magic != fdMagic {
   345  			return errors.New("bad magic")
   346  		}
   347  
   348  		var err error
   349  		var respHdr *fdHeader
   350  		var data, oobData []byte
   351  		switch hdr.Command {
   352  		case fdAdd:
   353  			respHdr, data, err = s.serveAdd(c, &hdr)
   354  		case fdRelease:
   355  			respHdr, err = s.serveRelease(&hdr)
   356  		case fdGet:
   357  			respHdr, data, oobData, err = s.serveGet(c, &hdr)
   358  		case fdRecover:
   359  			respHdr, err = s.serveRecover(c, &hdr)
   360  		default:
   361  			err = errors.New("bad command")
   362  		}
   363  
   364  		if err != nil {
   365  			data = []byte(err.Error())
   366  			oobData = nil
   367  			respHdr = &fdHeader{
   368  				Magic:    fdMagic,
   369  				Command:  fdError,
   370  				DataSize: uint32(len(data)),
   371  				OobSize:  0,
   372  			}
   373  		}
   374  
   375  		if err := binary.Write(c, binary.BigEndian, respHdr); err != nil {
   376  			return fmt.Errorf("error writing response header: %v", err)
   377  		}
   378  		if len(data) > 0 || len(oobData) > 0 {
   379  			if data == nil {
   380  				data = []byte{}
   381  			}
   382  			if oobData == nil {
   383  				oobData = []byte{}
   384  			}
   385  			if _, _, err = c.WriteMsgUnix(data, oobData, nil); err != nil {
   386  				return fmt.Errorf("error writing payload: %v", err)
   387  			}
   388  		}
   389  	}
   390  	return nil
   391  }
   392  
   393  // Stop makes FDServer stop listening and close its socket
   394  func (s *FDServer) Stop() error {
   395  	s.Lock()
   396  	defer s.Unlock()
   397  	if s.stopCh != nil {
   398  		close(s.stopCh)
   399  		s.lst.Close()
   400  		s.stopCh = nil
   401  		return s.source.Stop()
   402  	}
   403  	return nil
   404  }
   405  
   406  // FDClient can be used to connect to an FDServer listening on a Unix
   407  // domain socket
   408  type FDClient struct {
   409  	socketPath string
   410  }
   411  
   412  var _ FDManager = &FDClient{}
   413  
   414  // NewFDClient returns an FDClient for specified socket path
   415  func NewFDClient(socketPath string) *FDClient {
   416  	return &FDClient{socketPath: socketPath}
   417  }
   418  
   419  // IsRunning check if the fdserver is running.
   420  // It will return nil when it is running.
   421  func (c *FDClient) IsRunning() error {
   422  	conn, err := c.connect()
   423  	if err == nil {
   424  		c.close(conn)
   425  	}
   426  	return err
   427  }
   428  
   429  func (c *FDClient) connect() (*net.UnixConn, error) {
   430  	addr, err := net.ResolveUnixAddr("unix", c.socketPath)
   431  	if err != nil {
   432  		return nil, fmt.Errorf("failed to resolve unix addr %q: %v", c.socketPath, err)
   433  	}
   434  
   435  	conn, err := net.DialUnix("unix", nil, addr)
   436  	if err != nil {
   437  		return nil, fmt.Errorf("can't connect to %q: %v", c.socketPath, err)
   438  	}
   439  	return conn, nil
   440  }
   441  
   442  // Close closes the connection to FDServer
   443  func (c *FDClient) close(conn *net.UnixConn) error {
   444  	var err error
   445  	if conn != nil {
   446  		err = conn.Close()
   447  	}
   448  	return err
   449  }
   450  
   451  func (c *FDClient) request(hdr *fdHeader, data []byte) (*fdHeader, []byte, []byte, error) {
   452  	conn, err := c.connect()
   453  	if err != nil {
   454  		return nil, nil, nil, fmt.Errorf("not connected: %v", err)
   455  	}
   456  	defer c.close(conn)
   457  
   458  	hdr.Magic = fdMagic
   459  	if err := binary.Write(conn, binary.BigEndian, hdr); err != nil {
   460  		return nil, nil, nil, fmt.Errorf("error writing request header: %v", err)
   461  	}
   462  
   463  	if len(data) > 0 {
   464  		if err := binary.Write(conn, binary.BigEndian, data); err != nil {
   465  			return nil, nil, nil, fmt.Errorf("error writing request payload: %v", err)
   466  		}
   467  	}
   468  
   469  	var respHdr fdHeader
   470  	if err := binary.Read(conn, binary.BigEndian, &respHdr); err != nil {
   471  		return nil, nil, nil, fmt.Errorf("error reading response header: %v", err)
   472  	}
   473  	if respHdr.Magic != fdMagic {
   474  		return nil, nil, nil, errors.New("bad magic")
   475  	}
   476  
   477  	respData := make([]byte, respHdr.DataSize)
   478  	oobData := make([]byte, respHdr.OobSize)
   479  	if len(respData) > 0 || len(oobData) > 0 {
   480  		n, oobn, _, _, err := conn.ReadMsgUnix(respData, oobData)
   481  		if err != nil {
   482  			return nil, nil, nil, fmt.Errorf("error reading the message: %v", err)
   483  		}
   484  		// ReadMsgUnix will read & discard a single byte if len(respData) == 0
   485  		if n != len(respData) && (len(respData) != 0 || n != 1) {
   486  			return nil, nil, nil, fmt.Errorf("bad data size: %d instead of %d", n, len(respData))
   487  		}
   488  		if oobn != len(oobData) {
   489  			return nil, nil, nil, fmt.Errorf("bad oob data size: %d instead of %d", oobn, len(oobData))
   490  		}
   491  	}
   492  
   493  	if respHdr.Command == fdError {
   494  		return nil, nil, nil, fmt.Errorf("server returned error: %s", respData)
   495  	}
   496  
   497  	if respHdr.Command != hdr.Command|fdResponse {
   498  		return nil, nil, nil, fmt.Errorf("unexpected command %02x", respHdr.Command)
   499  	}
   500  
   501  	return &respHdr, respData, oobData, nil
   502  }
   503  
   504  // AddFDs requests the FDServer to add a new file descriptor
   505  // using its FDSource. It returns the data which are returned
   506  // by FDSource's GetFDs() call
   507  func (c *FDClient) AddFDs(key string, data interface{}) ([]byte, error) {
   508  	bs, ok := data.([]byte)
   509  	if !ok {
   510  		var err error
   511  		bs, err = json.Marshal(data)
   512  		if err != nil {
   513  			return nil, fmt.Errorf("error marshalling json: %v", err)
   514  		}
   515  	}
   516  	respHdr, respData, _, err := c.request(&fdHeader{
   517  		Command:  fdAdd,
   518  		DataSize: uint32(len(bs)),
   519  		Key:      fdKey(key),
   520  	}, bs)
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  	if respHdr.getKey() != key {
   525  		return nil, fmt.Errorf("fd key mismatch in the server response. Expected %q but received %q",
   526  			key, respHdr.getKey())
   527  	}
   528  	return respData, nil
   529  }
   530  
   531  // ReleaseFDs makes FDServer close the file descriptor and destroy
   532  // any associated resources
   533  func (c *FDClient) ReleaseFDs(key string) error {
   534  	respHdr, _, _, err := c.request(&fdHeader{
   535  		Command: fdRelease,
   536  		Key:     fdKey(key),
   537  	}, nil)
   538  	if err != nil {
   539  		return err
   540  	}
   541  	if respHdr.getKey() != key {
   542  		return fmt.Errorf("fd key mismatch in the server response")
   543  	}
   544  	return nil
   545  }
   546  
   547  // GetFDs requests file descriptors from the FDServer. It returns a
   548  // list of file descriptors which is valid for current process and any
   549  // associated data that was returned from FDSource's GetInfo() call.
   550  func (c *FDClient) GetFDs(key string) ([]int, []byte, error) {
   551  	_, respData, oobData, err := c.request(&fdHeader{
   552  		Command: fdGet,
   553  		Key:     fdKey(key),
   554  	}, nil)
   555  	if err != nil {
   556  		return nil, nil, err
   557  	}
   558  
   559  	scms, err := syscall.ParseSocketControlMessage(oobData)
   560  	if err != nil {
   561  		return nil, nil, fmt.Errorf("couldn't parse socket control message: %v", err)
   562  	}
   563  	if len(scms) != 1 {
   564  		return nil, nil, fmt.Errorf("unexpected number of socket control messages: %d instead of 1", len(scms))
   565  	}
   566  
   567  	fds, err := syscall.ParseUnixRights(&scms[0])
   568  	if err != nil {
   569  		return nil, nil, fmt.Errorf("can't decode file descriptors: %v", err)
   570  	}
   571  	return fds, respData, nil
   572  }
   573  
   574  // Recover requests FDServer to recover the state regarding the
   575  // specified key. It's intended to be called after Virtlet restart.
   576  func (c *FDClient) Recover(key string, data interface{}) error {
   577  	bs, ok := data.([]byte)
   578  	if !ok {
   579  		var err error
   580  		bs, err = json.Marshal(data)
   581  		if err != nil {
   582  			return fmt.Errorf("error marshalling json: %v", err)
   583  		}
   584  	}
   585  	respHdr, _, _, err := c.request(&fdHeader{
   586  		Command:  fdRecover,
   587  		DataSize: uint32(len(bs)),
   588  		Key:      fdKey(key),
   589  	}, bs)
   590  	if err != nil {
   591  		return err
   592  	}
   593  	if respHdr.getKey() != key {
   594  		return fmt.Errorf("fd key mismatch in the server response")
   595  	}
   596  	return nil
   597  }