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 }