github.com/google/netstack@v0.0.0-20191123085552-55fcc16cd0eb/tcpip/link/rawfile/rawfile_unsafe.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build linux
    16  
    17  // Package rawfile contains utilities for using the netstack with raw host
    18  // files on Linux hosts.
    19  package rawfile
    20  
    21  import (
    22  	"syscall"
    23  	"unsafe"
    24  
    25  	"github.com/google/netstack/tcpip"
    26  	"golang.org/x/sys/unix"
    27  )
    28  
    29  // GetMTU determines the MTU of a network interface device.
    30  func GetMTU(name string) (uint32, error) {
    31  	fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
    32  	if err != nil {
    33  		return 0, err
    34  	}
    35  
    36  	defer syscall.Close(fd)
    37  
    38  	var ifreq struct {
    39  		name [16]byte
    40  		mtu  int32
    41  		_    [20]byte
    42  	}
    43  
    44  	copy(ifreq.name[:], name)
    45  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
    46  	if errno != 0 {
    47  		return 0, errno
    48  	}
    49  
    50  	return uint32(ifreq.mtu), nil
    51  }
    52  
    53  // NonBlockingWrite writes the given buffer to a file descriptor. It fails if
    54  // partial data is written.
    55  func NonBlockingWrite(fd int, buf []byte) *tcpip.Error {
    56  	var ptr unsafe.Pointer
    57  	if len(buf) > 0 {
    58  		ptr = unsafe.Pointer(&buf[0])
    59  	}
    60  
    61  	_, _, e := syscall.RawSyscall(syscall.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf)))
    62  	if e != 0 {
    63  		return TranslateErrno(e)
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  // NonBlockingWrite3 writes up to three byte slices to a file descriptor in a
    70  // single syscall. It fails if partial data is written.
    71  func NonBlockingWrite3(fd int, b1, b2, b3 []byte) *tcpip.Error {
    72  	// If the is no second buffer, issue a regular write.
    73  	if len(b2) == 0 {
    74  		return NonBlockingWrite(fd, b1)
    75  	}
    76  
    77  	// We have two buffers. Build the iovec that represents them and issue
    78  	// a writev syscall.
    79  	iovec := [3]syscall.Iovec{
    80  		{
    81  			Base: &b1[0],
    82  			Len:  uint64(len(b1)),
    83  		},
    84  		{
    85  			Base: &b2[0],
    86  			Len:  uint64(len(b2)),
    87  		},
    88  	}
    89  	iovecLen := uintptr(2)
    90  
    91  	if len(b3) > 0 {
    92  		iovecLen++
    93  		iovec[2].Base = &b3[0]
    94  		iovec[2].Len = uint64(len(b3))
    95  	}
    96  
    97  	_, _, e := syscall.RawSyscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovec[0])), iovecLen)
    98  	if e != 0 {
    99  		return TranslateErrno(e)
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  // NonBlockingSendMMsg sends multiple messages on a socket.
   106  func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
   107  	n, _, e := syscall.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
   108  	if e != 0 {
   109  		return 0, TranslateErrno(e)
   110  	}
   111  
   112  	return int(n), nil
   113  }
   114  
   115  // PollEvent represents the pollfd structure passed to a poll() system call.
   116  type PollEvent struct {
   117  	FD      int32
   118  	Events  int16
   119  	Revents int16
   120  }
   121  
   122  // BlockingRead reads from a file descriptor that is set up as non-blocking. If
   123  // no data is available, it will block in a poll() syscall until the file
   124  // descriptor becomes readable.
   125  func BlockingRead(fd int, b []byte) (int, *tcpip.Error) {
   126  	for {
   127  		n, _, e := syscall.RawSyscall(syscall.SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
   128  		if e == 0 {
   129  			return int(n), nil
   130  		}
   131  
   132  		event := PollEvent{
   133  			FD:     int32(fd),
   134  			Events: 1, // POLLIN
   135  		}
   136  
   137  		_, e = BlockingPoll(&event, 1, nil)
   138  		if e != 0 && e != syscall.EINTR {
   139  			return 0, TranslateErrno(e)
   140  		}
   141  	}
   142  }
   143  
   144  // BlockingReadv reads from a file descriptor that is set up as non-blocking and
   145  // stores the data in a list of iovecs buffers. If no data is available, it will
   146  // block in a poll() syscall until the file descriptor becomes readable.
   147  func BlockingReadv(fd int, iovecs []syscall.Iovec) (int, *tcpip.Error) {
   148  	for {
   149  		n, _, e := syscall.RawSyscall(syscall.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
   150  		if e == 0 {
   151  			return int(n), nil
   152  		}
   153  
   154  		event := PollEvent{
   155  			FD:     int32(fd),
   156  			Events: 1, // POLLIN
   157  		}
   158  
   159  		_, e = BlockingPoll(&event, 1, nil)
   160  		if e != 0 && e != syscall.EINTR {
   161  			return 0, TranslateErrno(e)
   162  		}
   163  	}
   164  }
   165  
   166  // MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
   167  type MMsgHdr struct {
   168  	Msg syscall.Msghdr
   169  	Len uint32
   170  	_   [4]byte
   171  }
   172  
   173  // BlockingRecvMMsg reads from a file descriptor that is set up as non-blocking
   174  // and stores the received messages in a slice of MMsgHdr structures. If no data
   175  // is available, it will block in a poll() syscall until the file descriptor
   176  // becomes readable.
   177  func BlockingRecvMMsg(fd int, msgHdrs []MMsgHdr) (int, *tcpip.Error) {
   178  	for {
   179  		n, _, e := syscall.RawSyscall6(syscall.SYS_RECVMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), syscall.MSG_DONTWAIT, 0, 0)
   180  		if e == 0 {
   181  			return int(n), nil
   182  		}
   183  
   184  		event := PollEvent{
   185  			FD:     int32(fd),
   186  			Events: 1, // POLLIN
   187  		}
   188  
   189  		if _, e := BlockingPoll(&event, 1, nil); e != 0 && e != syscall.EINTR {
   190  			return 0, TranslateErrno(e)
   191  		}
   192  	}
   193  }