diff options
Diffstat (limited to 'pkg/sentry/socket')
47 files changed, 14438 insertions, 0 deletions
diff --git a/pkg/sentry/socket/control/control.go b/pkg/sentry/socket/control/control.go new file mode 100644 index 000000000..c0238691d --- /dev/null +++ b/pkg/sentry/socket/control/control.go @@ -0,0 +1,421 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package control provides internal representations of socket control +// messages. +package control + +import ( + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +const maxInt = int(^uint(0) >> 1) + +// SCMCredentials represents a SCM_CREDENTIALS socket control message. +type SCMCredentials interface { + transport.CredentialsControlMessage + + // Credentials returns properly namespaced values for the sender's pid, uid + // and gid. + Credentials(t *kernel.Task) (kernel.ThreadID, auth.UID, auth.GID) +} + +// SCMRights represents a SCM_RIGHTS socket control message. +type SCMRights interface { + transport.RightsControlMessage + + // Files returns up to max RightsFiles. + // + // Returned files are consumed and ownership is transferred to the caller. + // Subsequent calls to Files will return the next files. + Files(ctx context.Context, max int) (rf RightsFiles, truncated bool) +} + +// RightsFiles represents a SCM_RIGHTS socket control message. A reference is +// maintained for each fs.File and is release either when an FD is created or +// when the Release method is called. +// +// +stateify savable +type RightsFiles []*fs.File + +// NewSCMRights creates a new SCM_RIGHTS socket control message representation +// using local sentry FDs. +func NewSCMRights(t *kernel.Task, fds []int32) (SCMRights, error) { + files := make(RightsFiles, 0, len(fds)) + for _, fd := range fds { + file, _ := t.FDMap().GetDescriptor(kdefs.FD(fd)) + if file == nil { + files.Release() + return nil, syserror.EBADF + } + files = append(files, file) + } + return &files, nil +} + +// Files implements SCMRights.Files. +func (fs *RightsFiles) Files(ctx context.Context, max int) (RightsFiles, bool) { + n := max + var trunc bool + if l := len(*fs); n > l { + n = l + } else if n < l { + trunc = true + } + rf := (*fs)[:n] + *fs = (*fs)[n:] + return rf, trunc +} + +// Clone implements transport.RightsControlMessage.Clone. +func (fs *RightsFiles) Clone() transport.RightsControlMessage { + nfs := append(RightsFiles(nil), *fs...) + for _, nf := range nfs { + nf.IncRef() + } + return &nfs +} + +// Release implements transport.RightsControlMessage.Release. +func (fs *RightsFiles) Release() { + for _, f := range *fs { + f.DecRef() + } + *fs = nil +} + +// rightsFDs gets up to the specified maximum number of FDs. +func rightsFDs(t *kernel.Task, rights SCMRights, cloexec bool, max int) ([]int32, bool) { + files, trunc := rights.Files(t, max) + fds := make([]int32, 0, len(files)) + for i := 0; i < max && len(files) > 0; i++ { + fd, err := t.FDMap().NewFDFrom(0, files[0], kernel.FDFlags{cloexec}, t.ThreadGroup().Limits()) + files[0].DecRef() + files = files[1:] + if err != nil { + t.Warningf("Error inserting FD: %v", err) + // This is what Linux does. + break + } + + fds = append(fds, int32(fd)) + } + return fds, trunc +} + +// PackRights packs as many FDs as will fit into the unused capacity of buf. +func PackRights(t *kernel.Task, rights SCMRights, cloexec bool, buf []byte, flags int) ([]byte, int) { + maxFDs := (cap(buf) - len(buf) - linux.SizeOfControlMessageHeader) / 4 + // Linux does not return any FDs if none fit. + if maxFDs <= 0 { + flags |= linux.MSG_CTRUNC + return buf, flags + } + fds, trunc := rightsFDs(t, rights, cloexec, maxFDs) + if trunc { + flags |= linux.MSG_CTRUNC + } + align := t.Arch().Width() + return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds) +} + +// scmCredentials represents an SCM_CREDENTIALS socket control message. +// +// +stateify savable +type scmCredentials struct { + t *kernel.Task + kuid auth.KUID + kgid auth.KGID +} + +// NewSCMCredentials creates a new SCM_CREDENTIALS socket control message +// representation. +func NewSCMCredentials(t *kernel.Task, cred linux.ControlMessageCredentials) (SCMCredentials, error) { + tcred := t.Credentials() + kuid, err := tcred.UseUID(auth.UID(cred.UID)) + if err != nil { + return nil, err + } + kgid, err := tcred.UseGID(auth.GID(cred.GID)) + if err != nil { + return nil, err + } + if kernel.ThreadID(cred.PID) != t.ThreadGroup().ID() && !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.PIDNamespace().UserNamespace()) { + return nil, syserror.EPERM + } + return &scmCredentials{t, kuid, kgid}, nil +} + +// Equals implements transport.CredentialsControlMessage.Equals. +func (c *scmCredentials) Equals(oc transport.CredentialsControlMessage) bool { + if oc, _ := oc.(*scmCredentials); oc != nil && *c == *oc { + return true + } + return false +} + +func putUint64(buf []byte, n uint64) []byte { + usermem.ByteOrder.PutUint64(buf[len(buf):len(buf)+8], n) + return buf[:len(buf)+8] +} + +func putUint32(buf []byte, n uint32) []byte { + usermem.ByteOrder.PutUint32(buf[len(buf):len(buf)+4], n) + return buf[:len(buf)+4] +} + +// putCmsg writes a control message header and as much data as will fit into +// the unused capacity of a buffer. +func putCmsg(buf []byte, flags int, msgType uint32, align uint, data []int32) ([]byte, int) { + space := AlignDown(cap(buf)-len(buf), 4) + + // We can't write to space that doesn't exist, so if we are going to align + // the available space, we must align down. + // + // align must be >= 4 and each data int32 is 4 bytes. The length of the + // header is already aligned, so if we align to the with of the data there + // are two cases: + // 1. The aligned length is less than the length of the header. The + // unaligned length was also less than the length of the header, so we + // can't write anything. + // 2. The aligned length is greater than or equal to the length of the + // header. We can write the header plus zero or more datas. We can't write + // a partial int32, so the length of the message will be + // min(aligned length, header + datas). + if space < linux.SizeOfControlMessageHeader { + flags |= linux.MSG_CTRUNC + return buf, flags + } + + length := 4*len(data) + linux.SizeOfControlMessageHeader + if length > space { + length = space + } + buf = putUint64(buf, uint64(length)) + buf = putUint32(buf, linux.SOL_SOCKET) + buf = putUint32(buf, msgType) + for _, d := range data { + if len(buf)+4 > cap(buf) { + flags |= linux.MSG_CTRUNC + break + } + buf = putUint32(buf, uint32(d)) + } + return alignSlice(buf, align), flags +} + +func putCmsgStruct(buf []byte, msgType uint32, align uint, data interface{}) []byte { + if cap(buf)-len(buf) < linux.SizeOfControlMessageHeader { + return buf + } + ob := buf + + buf = putUint64(buf, uint64(linux.SizeOfControlMessageHeader)) + buf = putUint32(buf, linux.SOL_SOCKET) + buf = putUint32(buf, msgType) + + hdrBuf := buf + + buf = binary.Marshal(buf, usermem.ByteOrder, data) + + // Check if we went over. + if cap(buf) != cap(ob) { + return hdrBuf + } + + // Fix up length. + putUint64(ob, uint64(len(buf)-len(ob))) + + return alignSlice(buf, align) +} + +// Credentials implements SCMCredentials.Credentials. +func (c *scmCredentials) Credentials(t *kernel.Task) (kernel.ThreadID, auth.UID, auth.GID) { + // "When a process's user and group IDs are passed over a UNIX domain + // socket to a process in a different user namespace (see the description + // of SCM_CREDENTIALS in unix(7)), they are translated into the + // corresponding values as per the receiving process's user and group ID + // mappings." - user_namespaces(7) + pid := t.PIDNamespace().IDOfTask(c.t) + uid := c.kuid.In(t.UserNamespace()).OrOverflow() + gid := c.kgid.In(t.UserNamespace()).OrOverflow() + + return pid, uid, gid +} + +// PackCredentials packs the credentials in the control message (or default +// credentials if none) into a buffer. +func PackCredentials(t *kernel.Task, creds SCMCredentials, buf []byte, flags int) ([]byte, int) { + align := t.Arch().Width() + + // Default credentials if none are available. + pid := kernel.ThreadID(0) + uid := auth.UID(auth.NobodyKUID) + gid := auth.GID(auth.NobodyKGID) + + if creds != nil { + pid, uid, gid = creds.Credentials(t) + } + c := []int32{int32(pid), int32(uid), int32(gid)} + return putCmsg(buf, flags, linux.SCM_CREDENTIALS, align, c) +} + +// AlignUp rounds a length up to an alignment. align must be a power of 2. +func AlignUp(length int, align uint) int { + return (length + int(align) - 1) & ^(int(align) - 1) +} + +// AlignDown rounds a down to an alignment. align must be a power of 2. +func AlignDown(length int, align uint) int { + return length & ^(int(align) - 1) +} + +// alignSlice extends a slice's length (up to the capacity) to align it. +func alignSlice(buf []byte, align uint) []byte { + aligned := AlignUp(len(buf), align) + if aligned > cap(buf) { + // Linux allows unaligned data if there isn't room for alignment. + // Since there isn't room for alignment, there isn't room for any + // additional messages either. + return buf + } + return buf[:aligned] +} + +// PackTimestamp packs a SO_TIMESTAMP socket control message. +func PackTimestamp(t *kernel.Task, timestamp int64, buf []byte) []byte { + return putCmsgStruct( + buf, + linux.SO_TIMESTAMP, + t.Arch().Width(), + linux.NsecToTimeval(timestamp), + ) +} + +// Parse parses a raw socket control message into portable objects. +func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte) (transport.ControlMessages, error) { + var ( + fds linux.ControlMessageRights + + haveCreds bool + creds linux.ControlMessageCredentials + ) + + for i := 0; i < len(buf); { + if i+linux.SizeOfControlMessageHeader > len(buf) { + return transport.ControlMessages{}, syserror.EINVAL + } + + var h linux.ControlMessageHeader + binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageHeader], usermem.ByteOrder, &h) + + if h.Length < uint64(linux.SizeOfControlMessageHeader) { + return transport.ControlMessages{}, syserror.EINVAL + } + if h.Length > uint64(len(buf)-i) { + return transport.ControlMessages{}, syserror.EINVAL + } + if h.Level != linux.SOL_SOCKET { + return transport.ControlMessages{}, syserror.EINVAL + } + + i += linux.SizeOfControlMessageHeader + length := int(h.Length) - linux.SizeOfControlMessageHeader + + // The use of t.Arch().Width() is analogous to Linux's use of + // sizeof(long) in CMSG_ALIGN. + width := t.Arch().Width() + + switch h.Type { + case linux.SCM_RIGHTS: + rightsSize := AlignDown(length, linux.SizeOfControlMessageRight) + numRights := rightsSize / linux.SizeOfControlMessageRight + + if len(fds)+numRights > linux.SCM_MAX_FD { + return transport.ControlMessages{}, syserror.EINVAL + } + + for j := i; j < i+rightsSize; j += linux.SizeOfControlMessageRight { + fds = append(fds, int32(usermem.ByteOrder.Uint32(buf[j:j+linux.SizeOfControlMessageRight]))) + } + + i += AlignUp(length, width) + + case linux.SCM_CREDENTIALS: + if length < linux.SizeOfControlMessageCredentials { + return transport.ControlMessages{}, syserror.EINVAL + } + + binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds) + haveCreds = true + i += AlignUp(length, width) + + default: + // Unknown message type. + return transport.ControlMessages{}, syserror.EINVAL + } + } + + var credentials SCMCredentials + if haveCreds { + var err error + if credentials, err = NewSCMCredentials(t, creds); err != nil { + return transport.ControlMessages{}, err + } + } else { + credentials = makeCreds(t, socketOrEndpoint) + } + + var rights SCMRights + if len(fds) > 0 { + var err error + if rights, err = NewSCMRights(t, fds); err != nil { + return transport.ControlMessages{}, err + } + } + + if credentials == nil && rights == nil { + return transport.ControlMessages{}, nil + } + + return transport.ControlMessages{Credentials: credentials, Rights: rights}, nil +} + +func makeCreds(t *kernel.Task, socketOrEndpoint interface{}) SCMCredentials { + if t == nil || socketOrEndpoint == nil { + return nil + } + if cr, ok := socketOrEndpoint.(transport.Credentialer); ok && (cr.Passcred() || cr.ConnectedPasscred()) { + tcred := t.Credentials() + return &scmCredentials{t, tcred.EffectiveKUID, tcred.EffectiveKGID} + } + return nil +} + +// New creates default control messages if needed. +func New(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRights) transport.ControlMessages { + return transport.ControlMessages{ + Credentials: makeCreds(t, socketOrEndpoint), + Rights: rights, + } +} diff --git a/pkg/sentry/socket/control/control_state_autogen.go b/pkg/sentry/socket/control/control_state_autogen.go new file mode 100755 index 000000000..4554692a7 --- /dev/null +++ b/pkg/sentry/socket/control/control_state_autogen.go @@ -0,0 +1,36 @@ +// automatically generated by stateify. + +package control + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" +) + +func (x *RightsFiles) save(m state.Map) { + m.SaveValue("", ([]*fs.File)(*x)) +} + +func (x *RightsFiles) load(m state.Map) { + m.LoadValue("", new([]*fs.File), func(y interface{}) { *x = (RightsFiles)(y.([]*fs.File)) }) +} + +func (x *scmCredentials) beforeSave() {} +func (x *scmCredentials) save(m state.Map) { + x.beforeSave() + m.Save("t", &x.t) + m.Save("kuid", &x.kuid) + m.Save("kgid", &x.kgid) +} + +func (x *scmCredentials) afterLoad() {} +func (x *scmCredentials) load(m state.Map) { + m.Load("t", &x.t) + m.Load("kuid", &x.kuid) + m.Load("kgid", &x.kgid) +} + +func init() { + state.Register("control.RightsFiles", (*RightsFiles)(nil), state.Fns{Save: (*RightsFiles).save, Load: (*RightsFiles).load}) + state.Register("control.scmCredentials", (*scmCredentials)(nil), state.Fns{Save: (*scmCredentials).save, Load: (*scmCredentials).load}) +} diff --git a/pkg/sentry/socket/epsocket/device.go b/pkg/sentry/socket/epsocket/device.go new file mode 100644 index 000000000..ab4083efe --- /dev/null +++ b/pkg/sentry/socket/epsocket/device.go @@ -0,0 +1,20 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package epsocket + +import "gvisor.googlesource.com/gvisor/pkg/sentry/device" + +// epsocketDevice is the endpoint socket virtual device. +var epsocketDevice = device.NewAnonDevice() diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go new file mode 100644 index 000000000..de4b963da --- /dev/null +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -0,0 +1,2283 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package epsocket provides an implementation of the socket.Socket interface +// that is backed by a tcpip.Endpoint. +// +// It does not depend on any particular endpoint implementation, and thus can +// be used to expose certain endpoints to the sentry while leaving others out, +// for example, TCP endpoints and Unix-domain endpoints. +// +// Lock ordering: netstack => mm: ioSequencePayload copies user memory inside +// tcpip.Endpoint.Write(). Netstack is allowed to (and does) hold locks during +// this operation. +package epsocket + +import ( + "bytes" + "math" + "sync" + "syscall" + "time" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/metric" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/inet" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/safemem" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" + "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +func mustCreateMetric(name, description string) *tcpip.StatCounter { + var cm tcpip.StatCounter + metric.MustRegisterCustomUint64Metric(name, false /* sync */, description, cm.Value) + return &cm +} + +// Metrics contains metrics exported by netstack. +var Metrics = tcpip.Stats{ + UnknownProtocolRcvdPackets: mustCreateMetric("/netstack/unknown_protocol_received_packets", "Number of packets received by netstack that were for an unknown or unsupported protocol."), + MalformedRcvdPackets: mustCreateMetric("/netstack/malformed_received_packets", "Number of packets received by netstack that were deemed malformed."), + DroppedPackets: mustCreateMetric("/netstack/dropped_packets", "Number of packets dropped by netstack due to full queues."), + ICMP: tcpip.ICMPStats{ + V4PacketsSent: tcpip.ICMPv4SentPacketStats{ + ICMPv4PacketStats: tcpip.ICMPv4PacketStats{ + Echo: mustCreateMetric("/netstack/icmp/v4/packets_sent/echo", "Total number of ICMPv4 echo packets sent by netstack."), + EchoReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/echo_reply", "Total number of ICMPv4 echo reply packets sent by netstack."), + DstUnreachable: mustCreateMetric("/netstack/icmp/v4/packets_sent/dst_unreachable", "Total number of ICMPv4 destination unreachable packets sent by netstack."), + SrcQuench: mustCreateMetric("/netstack/icmp/v4/packets_sent/src_quench", "Total number of ICMPv4 source quench packets sent by netstack."), + Redirect: mustCreateMetric("/netstack/icmp/v4/packets_sent/redirect", "Total number of ICMPv4 redirect packets sent by netstack."), + TimeExceeded: mustCreateMetric("/netstack/icmp/v4/packets_sent/time_exceeded", "Total number of ICMPv4 time exceeded packets sent by netstack."), + ParamProblem: mustCreateMetric("/netstack/icmp/v4/packets_sent/param_problem", "Total number of ICMPv4 parameter problem packets sent by netstack."), + Timestamp: mustCreateMetric("/netstack/icmp/v4/packets_sent/timestamp", "Total number of ICMPv4 timestamp packets sent by netstack."), + TimestampReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/timestamp_reply", "Total number of ICMPv4 timestamp reply packets sent by netstack."), + InfoRequest: mustCreateMetric("/netstack/icmp/v4/packets_sent/info_request", "Total number of ICMPv4 information request packets sent by netstack."), + InfoReply: mustCreateMetric("/netstack/icmp/v4/packets_sent/info_reply", "Total number of ICMPv4 information reply packets sent by netstack."), + }, + Dropped: mustCreateMetric("/netstack/icmp/v4/packets_sent/dropped", "Total number of ICMPv4 packets dropped by netstack due to link layer errors."), + }, + V4PacketsReceived: tcpip.ICMPv4ReceivedPacketStats{ + ICMPv4PacketStats: tcpip.ICMPv4PacketStats{ + Echo: mustCreateMetric("/netstack/icmp/v4/packets_received/echo", "Total number of ICMPv4 echo packets received by netstack."), + EchoReply: mustCreateMetric("/netstack/icmp/v4/packets_received/echo_reply", "Total number of ICMPv4 echo reply packets received by netstack."), + DstUnreachable: mustCreateMetric("/netstack/icmp/v4/packets_received/dst_unreachable", "Total number of ICMPv4 destination unreachable packets received by netstack."), + SrcQuench: mustCreateMetric("/netstack/icmp/v4/packets_received/src_quench", "Total number of ICMPv4 source quench packets received by netstack."), + Redirect: mustCreateMetric("/netstack/icmp/v4/packets_received/redirect", "Total number of ICMPv4 redirect packets received by netstack."), + TimeExceeded: mustCreateMetric("/netstack/icmp/v4/packets_received/time_exceeded", "Total number of ICMPv4 time exceeded packets received by netstack."), + ParamProblem: mustCreateMetric("/netstack/icmp/v4/packets_received/param_problem", "Total number of ICMPv4 parameter problem packets received by netstack."), + Timestamp: mustCreateMetric("/netstack/icmp/v4/packets_received/timestamp", "Total number of ICMPv4 timestamp packets received by netstack."), + TimestampReply: mustCreateMetric("/netstack/icmp/v4/packets_received/timestamp_reply", "Total number of ICMPv4 timestamp reply packets received by netstack."), + InfoRequest: mustCreateMetric("/netstack/icmp/v4/packets_received/info_request", "Total number of ICMPv4 information request packets received by netstack."), + InfoReply: mustCreateMetric("/netstack/icmp/v4/packets_received/info_reply", "Total number of ICMPv4 information reply packets received by netstack."), + }, + Invalid: mustCreateMetric("/netstack/icmp/v4/packets_received/invalid", "Total number of ICMPv4 packets received that the transport layer could not parse."), + }, + V6PacketsSent: tcpip.ICMPv6SentPacketStats{ + ICMPv6PacketStats: tcpip.ICMPv6PacketStats{ + EchoRequest: mustCreateMetric("/netstack/icmp/v6/packets_sent/echo_request", "Total number of ICMPv6 echo request packets sent by netstack."), + EchoReply: mustCreateMetric("/netstack/icmp/v6/packets_sent/echo_reply", "Total number of ICMPv6 echo reply packets sent by netstack."), + DstUnreachable: mustCreateMetric("/netstack/icmp/v6/packets_sent/dst_unreachable", "Total number of ICMPv6 destination unreachable packets sent by netstack."), + PacketTooBig: mustCreateMetric("/netstack/icmp/v6/packets_sent/packet_too_big", "Total number of ICMPv6 packet too big packets sent by netstack."), + TimeExceeded: mustCreateMetric("/netstack/icmp/v6/packets_sent/time_exceeded", "Total number of ICMPv6 time exceeded packets sent by netstack."), + ParamProblem: mustCreateMetric("/netstack/icmp/v6/packets_sent/param_problem", "Total number of ICMPv6 parameter problem packets sent by netstack."), + RouterSolicit: mustCreateMetric("/netstack/icmp/v6/packets_sent/router_solicit", "Total number of ICMPv6 router solicit packets sent by netstack."), + RouterAdvert: mustCreateMetric("/netstack/icmp/v6/packets_sent/router_advert", "Total number of ICMPv6 router advert packets sent by netstack."), + NeighborSolicit: mustCreateMetric("/netstack/icmp/v6/packets_sent/neighbor_solicit", "Total number of ICMPv6 neighbor solicit packets sent by netstack."), + NeighborAdvert: mustCreateMetric("/netstack/icmp/v6/packets_sent/neighbor_advert", "Total number of ICMPv6 neighbor advert packets sent by netstack."), + RedirectMsg: mustCreateMetric("/netstack/icmp/v6/packets_sent/redirect_msg", "Total number of ICMPv6 redirect message packets sent by netstack."), + }, + Dropped: mustCreateMetric("/netstack/icmp/v6/packets_sent/dropped", "Total number of ICMPv6 packets dropped by netstack due to link layer errors."), + }, + V6PacketsReceived: tcpip.ICMPv6ReceivedPacketStats{ + ICMPv6PacketStats: tcpip.ICMPv6PacketStats{ + EchoRequest: mustCreateMetric("/netstack/icmp/v6/packets_received/echo_request", "Total number of ICMPv6 echo request packets received by netstack."), + EchoReply: mustCreateMetric("/netstack/icmp/v6/packets_received/echo_reply", "Total number of ICMPv6 echo reply packets received by netstack."), + DstUnreachable: mustCreateMetric("/netstack/icmp/v6/packets_received/dst_unreachable", "Total number of ICMPv6 destination unreachable packets received by netstack."), + PacketTooBig: mustCreateMetric("/netstack/icmp/v6/packets_received/packet_too_big", "Total number of ICMPv6 packet too big packets received by netstack."), + TimeExceeded: mustCreateMetric("/netstack/icmp/v6/packets_received/time_exceeded", "Total number of ICMPv6 time exceeded packets received by netstack."), + ParamProblem: mustCreateMetric("/netstack/icmp/v6/packets_received/param_problem", "Total number of ICMPv6 parameter problem packets received by netstack."), + RouterSolicit: mustCreateMetric("/netstack/icmp/v6/packets_received/router_solicit", "Total number of ICMPv6 router solicit packets received by netstack."), + RouterAdvert: mustCreateMetric("/netstack/icmp/v6/packets_received/router_advert", "Total number of ICMPv6 router advert packets received by netstack."), + NeighborSolicit: mustCreateMetric("/netstack/icmp/v6/packets_received/neighbor_solicit", "Total number of ICMPv6 neighbor solicit packets received by netstack."), + NeighborAdvert: mustCreateMetric("/netstack/icmp/v6/packets_received/neighbor_advert", "Total number of ICMPv6 neighbor advert packets received by netstack."), + RedirectMsg: mustCreateMetric("/netstack/icmp/v6/packets_received/redirect_msg", "Total number of ICMPv6 redirect message packets received by netstack."), + }, + Invalid: mustCreateMetric("/netstack/icmp/v6/packets_received/invalid", "Total number of ICMPv6 packets received that the transport layer could not parse."), + }, + }, + IP: tcpip.IPStats{ + PacketsReceived: mustCreateMetric("/netstack/ip/packets_received", "Total number of IP packets received from the link layer in nic.DeliverNetworkPacket."), + InvalidAddressesReceived: mustCreateMetric("/netstack/ip/invalid_addresses_received", "Total number of IP packets received with an unknown or invalid destination address."), + PacketsDelivered: mustCreateMetric("/netstack/ip/packets_delivered", "Total number of incoming IP packets that are successfully delivered to the transport layer via HandlePacket."), + PacketsSent: mustCreateMetric("/netstack/ip/packets_sent", "Total number of IP packets sent via WritePacket."), + OutgoingPacketErrors: mustCreateMetric("/netstack/ip/outgoing_packet_errors", "Total number of IP packets which failed to write to a link-layer endpoint."), + }, + TCP: tcpip.TCPStats{ + ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."), + PassiveConnectionOpenings: mustCreateMetric("/netstack/tcp/passive_connection_openings", "Number of connections opened successfully via Listen."), + ListenOverflowSynDrop: mustCreateMetric("/netstack/tcp/listen_overflow_syn_drop", "Number of times the listen queue overflowed and a SYN was dropped."), + ListenOverflowAckDrop: mustCreateMetric("/netstack/tcp/listen_overflow_ack_drop", "Number of times the listen queue overflowed and the final ACK in the handshake was dropped."), + ListenOverflowSynCookieSent: mustCreateMetric("/netstack/tcp/listen_overflow_syn_cookie_sent", "Number of times a SYN cookie was sent."), + ListenOverflowSynCookieRcvd: mustCreateMetric("/netstack/tcp/listen_overflow_syn_cookie_rcvd", "Number of times a SYN cookie was received."), + ListenOverflowInvalidSynCookieRcvd: mustCreateMetric("/netstack/tcp/listen_overflow_invalid_syn_cookie_rcvd", "Number of times an invalid SYN cookie was received."), + FailedConnectionAttempts: mustCreateMetric("/netstack/tcp/failed_connection_attempts", "Number of calls to Connect or Listen (active and passive openings, respectively) that end in an error."), + ValidSegmentsReceived: mustCreateMetric("/netstack/tcp/valid_segments_received", "Number of TCP segments received that the transport layer successfully parsed."), + InvalidSegmentsReceived: mustCreateMetric("/netstack/tcp/invalid_segments_received", "Number of TCP segments received that the transport layer could not parse."), + SegmentsSent: mustCreateMetric("/netstack/tcp/segments_sent", "Number of TCP segments sent."), + ResetsSent: mustCreateMetric("/netstack/tcp/resets_sent", "Number of TCP resets sent."), + ResetsReceived: mustCreateMetric("/netstack/tcp/resets_received", "Number of TCP resets received."), + Retransmits: mustCreateMetric("/netstack/tcp/retransmits", "Number of TCP segments retransmitted."), + FastRecovery: mustCreateMetric("/netstack/tcp/fast_recovery", "Number of times fast recovery was used to recover from packet loss."), + SACKRecovery: mustCreateMetric("/netstack/tcp/sack_recovery", "Number of times SACK recovery was used to recover from packet loss."), + SlowStartRetransmits: mustCreateMetric("/netstack/tcp/slow_start_retransmits", "Number of segments retransmitted in slow start mode."), + FastRetransmit: mustCreateMetric("/netstack/tcp/fast_retransmit", "Number of TCP segments which were fast retransmitted."), + Timeouts: mustCreateMetric("/netstack/tcp/timeouts", "Number of times RTO expired."), + ChecksumErrors: mustCreateMetric("/netstack/tcp/checksum_errors", "Number of segments dropped due to bad checksums."), + }, + UDP: tcpip.UDPStats{ + PacketsReceived: mustCreateMetric("/netstack/udp/packets_received", "Number of UDP datagrams received via HandlePacket."), + UnknownPortErrors: mustCreateMetric("/netstack/udp/unknown_port_errors", "Number of incoming UDP datagrams dropped because they did not have a known destination port."), + ReceiveBufferErrors: mustCreateMetric("/netstack/udp/receive_buffer_errors", "Number of incoming UDP datagrams dropped due to the receiving buffer being in an invalid state."), + MalformedPacketsReceived: mustCreateMetric("/netstack/udp/malformed_packets_received", "Number of incoming UDP datagrams dropped due to the UDP header being in a malformed state."), + PacketsSent: mustCreateMetric("/netstack/udp/packets_sent", "Number of UDP datagrams sent via sendUDP."), + }, +} + +const sizeOfInt32 int = 4 + +var errStackType = syserr.New("expected but did not receive an epsocket.Stack", linux.EINVAL) + +// ntohs converts a 16-bit number from network byte order to host byte order. It +// assumes that the host is little endian. +func ntohs(v uint16) uint16 { + return v<<8 | v>>8 +} + +// htons converts a 16-bit number from host byte order to network byte order. It +// assumes that the host is little endian. +func htons(v uint16) uint16 { + return ntohs(v) +} + +// commonEndpoint represents the intersection of a tcpip.Endpoint and a +// transport.Endpoint. +type commonEndpoint interface { + // GetLocalAddress implements tcpip.Endpoint.GetLocalAddress and + // transport.Endpoint.GetLocalAddress. + GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) + + // GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress and + // transport.Endpoint.GetRemoteAddress. + GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) + + // Readiness implements tcpip.Endpoint.Readiness and + // transport.Endpoint.Readiness. + Readiness(mask waiter.EventMask) waiter.EventMask + + // SetSockOpt implements tcpip.Endpoint.SetSockOpt and + // transport.Endpoint.SetSockOpt. + SetSockOpt(interface{}) *tcpip.Error + + // GetSockOpt implements tcpip.Endpoint.GetSockOpt and + // transport.Endpoint.GetSockOpt. + GetSockOpt(interface{}) *tcpip.Error +} + +// SocketOperations encapsulates all the state needed to represent a network stack +// endpoint in the kernel context. +// +// +stateify savable +type SocketOperations struct { + fsutil.FilePipeSeek `state:"nosave"` + fsutil.FileNotDirReaddir `state:"nosave"` + fsutil.FileNoopFlush `state:"nosave"` + fsutil.FileNoFsync `state:"nosave"` + fsutil.FileNoMMap `state:"nosave"` + fsutil.FileNoSplice `state:"nosave"` + fsutil.FileUseInodeUnstableAttr `state:"nosave"` + socket.SendReceiveTimeout + *waiter.Queue + + family int + Endpoint tcpip.Endpoint + skType transport.SockType + + // readMu protects access to the below fields. + readMu sync.Mutex `state:"nosave"` + // readView contains the remaining payload from the last packet. + readView buffer.View + // readCM holds control message information for the last packet read + // from Endpoint. + readCM tcpip.ControlMessages + sender tcpip.FullAddress + + // sockOptTimestamp corresponds to SO_TIMESTAMP. When true, timestamps + // of returned messages can be returned via control messages. When + // false, the same timestamp is instead stored and can be read via the + // SIOCGSTAMP ioctl. It is protected by readMu. See socket(7). + sockOptTimestamp bool + // timestampValid indicates whether timestamp for SIOCGSTAMP has been + // set. It is protected by readMu. + timestampValid bool + // timestampNS holds the timestamp to use with SIOCTSTAMP. It is only + // valid when timestampValid is true. It is protected by readMu. + timestampNS int64 +} + +// New creates a new endpoint socket. +func New(t *kernel.Task, family int, skType transport.SockType, queue *waiter.Queue, endpoint tcpip.Endpoint) (*fs.File, *syserr.Error) { + if skType == transport.SockStream { + if err := endpoint.SetSockOpt(tcpip.DelayOption(1)); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + } + + dirent := socket.NewDirent(t, epsocketDevice) + defer dirent.DecRef() + return fs.NewFile(t, dirent, fs.FileFlags{Read: true, Write: true}, &SocketOperations{ + Queue: queue, + family: family, + Endpoint: endpoint, + skType: skType, + }), nil +} + +var sockAddrInetSize = int(binary.Size(linux.SockAddrInet{})) +var sockAddrInet6Size = int(binary.Size(linux.SockAddrInet6{})) + +// bytesToIPAddress converts an IPv4 or IPv6 address from the user to the +// netstack representation taking any addresses into account. +func bytesToIPAddress(addr []byte) tcpip.Address { + if bytes.Equal(addr, make([]byte, 4)) || bytes.Equal(addr, make([]byte, 16)) { + return "" + } + return tcpip.Address(addr) +} + +// GetAddress reads an sockaddr struct from the given address and converts it +// to the FullAddress format. It supports AF_UNIX, AF_INET and AF_INET6 +// addresses. +func GetAddress(sfamily int, addr []byte) (tcpip.FullAddress, *syserr.Error) { + // Make sure we have at least 2 bytes for the address family. + if len(addr) < 2 { + return tcpip.FullAddress{}, syserr.ErrInvalidArgument + } + + family := usermem.ByteOrder.Uint16(addr) + if family != uint16(sfamily) { + return tcpip.FullAddress{}, syserr.ErrAddressFamilyNotSupported + } + + // Get the rest of the fields based on the address family. + switch family { + case linux.AF_UNIX: + path := addr[2:] + if len(path) > linux.UnixPathMax { + return tcpip.FullAddress{}, syserr.ErrInvalidArgument + } + // Drop the terminating NUL (if one exists) and everything after + // it for filesystem (non-abstract) addresses. + if len(path) > 0 && path[0] != 0 { + if n := bytes.IndexByte(path[1:], 0); n >= 0 { + path = path[:n+1] + } + } + return tcpip.FullAddress{ + Addr: tcpip.Address(path), + }, nil + + case linux.AF_INET: + var a linux.SockAddrInet + if len(addr) < sockAddrInetSize { + return tcpip.FullAddress{}, syserr.ErrBadAddress + } + binary.Unmarshal(addr[:sockAddrInetSize], usermem.ByteOrder, &a) + + out := tcpip.FullAddress{ + Addr: bytesToIPAddress(a.Addr[:]), + Port: ntohs(a.Port), + } + return out, nil + + case linux.AF_INET6: + var a linux.SockAddrInet6 + if len(addr) < sockAddrInet6Size { + return tcpip.FullAddress{}, syserr.ErrBadAddress + } + binary.Unmarshal(addr[:sockAddrInet6Size], usermem.ByteOrder, &a) + + out := tcpip.FullAddress{ + Addr: bytesToIPAddress(a.Addr[:]), + Port: ntohs(a.Port), + } + if isLinkLocal(out.Addr) { + out.NIC = tcpip.NICID(a.Scope_id) + } + return out, nil + + default: + return tcpip.FullAddress{}, syserr.ErrAddressFamilyNotSupported + } +} + +func (s *SocketOperations) isPacketBased() bool { + return s.skType == linux.SOCK_DGRAM || s.skType == linux.SOCK_SEQPACKET || s.skType == linux.SOCK_RDM || s.skType == linux.SOCK_RAW +} + +// fetchReadView updates the readView field of the socket if it's currently +// empty. It assumes that the socket is locked. +func (s *SocketOperations) fetchReadView() *syserr.Error { + if len(s.readView) > 0 { + return nil + } + + s.readView = nil + s.sender = tcpip.FullAddress{} + + v, cms, err := s.Endpoint.Read(&s.sender) + if err != nil { + return syserr.TranslateNetstackError(err) + } + + s.readView = v + s.readCM = cms + + return nil +} + +// Release implements fs.FileOperations.Release. +func (s *SocketOperations) Release() { + s.Endpoint.Close() +} + +// Read implements fs.FileOperations.Read. +func (s *SocketOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { + if dst.NumBytes() == 0 { + return 0, nil + } + n, _, _, _, _, err := s.nonBlockingRead(ctx, dst, false, false, false) + if err == syserr.ErrWouldBlock { + return int64(n), syserror.ErrWouldBlock + } + if err != nil { + return 0, err.ToError() + } + return int64(n), nil +} + +// ioSequencePayload implements tcpip.Payload. It copies user memory bytes on demand +// based on the requested size. +type ioSequencePayload struct { + ctx context.Context + src usermem.IOSequence +} + +// Get implements tcpip.Payload. +func (i *ioSequencePayload) Get(size int) ([]byte, *tcpip.Error) { + if size > i.Size() { + size = i.Size() + } + v := buffer.NewView(size) + if _, err := i.src.CopyIn(i.ctx, v); err != nil { + return nil, tcpip.ErrBadAddress + } + return v, nil +} + +// Size implements tcpip.Payload. +func (i *ioSequencePayload) Size() int { + return int(i.src.NumBytes()) +} + +// Write implements fs.FileOperations.Write. +func (s *SocketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { + f := &ioSequencePayload{ctx: ctx, src: src} + n, resCh, err := s.Endpoint.Write(f, tcpip.WriteOptions{}) + if err == tcpip.ErrWouldBlock { + return 0, syserror.ErrWouldBlock + } + + if resCh != nil { + t := ctx.(*kernel.Task) + if err := t.Block(resCh); err != nil { + return 0, syserr.FromError(err).ToError() + } + + n, _, err = s.Endpoint.Write(f, tcpip.WriteOptions{}) + } + + if err != nil { + return 0, syserr.TranslateNetstackError(err).ToError() + } + + if int64(n) < src.NumBytes() { + return int64(n), syserror.ErrWouldBlock + } + + return int64(n), nil +} + +// Readiness returns a mask of ready events for socket s. +func (s *SocketOperations) Readiness(mask waiter.EventMask) waiter.EventMask { + r := s.Endpoint.Readiness(mask) + + // Check our cached value iff the caller asked for readability and the + // endpoint itself is currently not readable. + if (mask & ^r & waiter.EventIn) != 0 { + s.readMu.Lock() + if len(s.readView) > 0 { + r |= waiter.EventIn + } + s.readMu.Unlock() + } + + return r +} + +// Connect implements the linux syscall connect(2) for sockets backed by +// tpcip.Endpoint. +func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { + addr, err := GetAddress(s.family, sockaddr) + if err != nil { + return err + } + + // Always return right away in the non-blocking case. + if !blocking { + return syserr.TranslateNetstackError(s.Endpoint.Connect(addr)) + } + + // Register for notification when the endpoint becomes writable, then + // initiate the connection. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + + if err := s.Endpoint.Connect(addr); err != tcpip.ErrConnectStarted && err != tcpip.ErrAlreadyConnecting { + return syserr.TranslateNetstackError(err) + } + + // It's pending, so we have to wait for a notification, and fetch the + // result once the wait completes. + if err := t.Block(ch); err != nil { + return syserr.FromError(err) + } + + // Call Connect() again after blocking to find connect's result. + return syserr.TranslateNetstackError(s.Endpoint.Connect(addr)) +} + +// Bind implements the linux syscall bind(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { + addr, err := GetAddress(s.family, sockaddr) + if err != nil { + return err + } + + // Issue the bind request to the endpoint. + return syserr.TranslateNetstackError(s.Endpoint.Bind(addr)) +} + +// Listen implements the linux syscall listen(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error { + return syserr.TranslateNetstackError(s.Endpoint.Listen(backlog)) +} + +// blockingAccept implements a blocking version of accept(2), that is, if no +// connections are ready to be accept, it will block until one becomes ready. +func (s *SocketOperations) blockingAccept(t *kernel.Task) (tcpip.Endpoint, *waiter.Queue, *syserr.Error) { + // Register for notifications. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + // Try to accept the connection again; if it fails, then wait until we + // get a notification. + for { + if ep, wq, err := s.Endpoint.Accept(); err != tcpip.ErrWouldBlock { + return ep, wq, syserr.TranslateNetstackError(err) + } + + if err := t.Block(ch); err != nil { + return nil, nil, syserr.FromError(err) + } + } +} + +// Accept implements the linux syscall accept(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) { + // Issue the accept request to get the new endpoint. + ep, wq, terr := s.Endpoint.Accept() + if terr != nil { + if terr != tcpip.ErrWouldBlock || !blocking { + return 0, nil, 0, syserr.TranslateNetstackError(terr) + } + + var err *syserr.Error + ep, wq, err = s.blockingAccept(t) + if err != nil { + return 0, nil, 0, err + } + } + + ns, err := New(t, s.family, s.skType, wq, ep) + if err != nil { + return 0, nil, 0, err + } + defer ns.DecRef() + + if flags&linux.SOCK_NONBLOCK != 0 { + flags := ns.Flags() + flags.NonBlocking = true + ns.SetFlags(flags.Settable()) + } + + var addr interface{} + var addrLen uint32 + if peerRequested { + // Get address of the peer and write it to peer slice. + var err *syserr.Error + addr, addrLen, err = ns.FileOperations.(*SocketOperations).GetPeerName(t) + if err != nil { + return 0, nil, 0, err + } + } + + fdFlags := kernel.FDFlags{ + CloseOnExec: flags&linux.SOCK_CLOEXEC != 0, + } + fd, e := t.FDMap().NewFDFrom(0, ns, fdFlags, t.ThreadGroup().Limits()) + + t.Kernel().RecordSocket(ns, s.family) + + return fd, addr, addrLen, syserr.FromError(e) +} + +// ConvertShutdown converts Linux shutdown flags into tcpip shutdown flags. +func ConvertShutdown(how int) (tcpip.ShutdownFlags, *syserr.Error) { + var f tcpip.ShutdownFlags + switch how { + case linux.SHUT_RD: + f = tcpip.ShutdownRead + case linux.SHUT_WR: + f = tcpip.ShutdownWrite + case linux.SHUT_RDWR: + f = tcpip.ShutdownRead | tcpip.ShutdownWrite + default: + return 0, syserr.ErrInvalidArgument + } + return f, nil +} + +// Shutdown implements the linux syscall shutdown(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { + f, err := ConvertShutdown(how) + if err != nil { + return err + } + + // Issue shutdown request. + return syserr.TranslateNetstackError(s.Endpoint.Shutdown(f)) +} + +// GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name, outLen int) (interface{}, *syserr.Error) { + // TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is + // implemented specifically for epsocket.SocketOperations rather than + // commonEndpoint. commonEndpoint should be extended to support socket + // options where the implementation is not shared, as unix sockets need + // their own support for SO_TIMESTAMP. + if level == linux.SOL_SOCKET && name == linux.SO_TIMESTAMP { + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + val := int32(0) + s.readMu.Lock() + defer s.readMu.Unlock() + if s.sockOptTimestamp { + val = 1 + } + return val, nil + } + + return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outLen) +} + +// GetSockOpt can be used to implement the linux syscall getsockopt(2) for +// sockets backed by a commonEndpoint. +func GetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, skType transport.SockType, level, name, outLen int) (interface{}, *syserr.Error) { + switch level { + case linux.SOL_SOCKET: + return getSockOptSocket(t, s, ep, family, skType, name, outLen) + + case linux.SOL_TCP: + return getSockOptTCP(t, ep, name, outLen) + + case linux.SOL_IPV6: + return getSockOptIPv6(t, ep, name, outLen) + + case linux.SOL_IP: + return getSockOptIP(t, ep, name, outLen) + + case linux.SOL_UDP, + linux.SOL_ICMPV6, + linux.SOL_RAW, + linux.SOL_PACKET: + + t.Kernel().EmitUnimplementedEvent(t) + } + + return nil, syserr.ErrProtocolNotAvailable +} + +// getSockOptSocket implements GetSockOpt when level is SOL_SOCKET. +func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, skType transport.SockType, name, outLen int) (interface{}, *syserr.Error) { + // TODO(b/124056281): Stop rejecting short optLen values in getsockopt. + switch name { + case linux.SO_TYPE: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + return int32(skType), nil + + case linux.SO_ERROR: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + // Get the last error and convert it. + err := ep.GetSockOpt(tcpip.ErrorOption{}) + if err == nil { + return int32(0), nil + } + return int32(syserr.TranslateNetstackError(err).ToLinux().Number()), nil + + case linux.SO_PEERCRED: + if family != linux.AF_UNIX || outLen < syscall.SizeofUcred { + return nil, syserr.ErrInvalidArgument + } + + tcred := t.Credentials() + return syscall.Ucred{ + Pid: int32(t.ThreadGroup().ID()), + Uid: uint32(tcred.EffectiveKUID.In(tcred.UserNamespace).OrOverflow()), + Gid: uint32(tcred.EffectiveKGID.In(tcred.UserNamespace).OrOverflow()), + }, nil + + case linux.SO_PASSCRED: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.PasscredOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.SO_SNDBUF: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var size tcpip.SendBufferSizeOption + if err := ep.GetSockOpt(&size); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + if size > math.MaxInt32 { + size = math.MaxInt32 + } + + return int32(size), nil + + case linux.SO_RCVBUF: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var size tcpip.ReceiveBufferSizeOption + if err := ep.GetSockOpt(&size); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + if size > math.MaxInt32 { + size = math.MaxInt32 + } + + return int32(size), nil + + case linux.SO_REUSEADDR: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.ReuseAddressOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.SO_REUSEPORT: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.ReusePortOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.SO_BROADCAST: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.BroadcastOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.SO_KEEPALIVE: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.KeepaliveEnabledOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.SO_LINGER: + if outLen < linux.SizeOfLinger { + return nil, syserr.ErrInvalidArgument + } + return linux.Linger{}, nil + + case linux.SO_SNDTIMEO: + // TODO(igudger): Linux allows shorter lengths for partial results. + if outLen < linux.SizeOfTimeval { + return nil, syserr.ErrInvalidArgument + } + + return linux.NsecToTimeval(s.SendTimeout()), nil + + case linux.SO_RCVTIMEO: + // TODO(igudger): Linux allows shorter lengths for partial results. + if outLen < linux.SizeOfTimeval { + return nil, syserr.ErrInvalidArgument + } + + return linux.NsecToTimeval(s.RecvTimeout()), nil + + case linux.SO_OOBINLINE: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.OutOfBandInlineOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + default: + socket.GetSockOptEmitUnimplementedEvent(t, name) + } + return nil, syserr.ErrProtocolNotAvailable +} + +// getSockOptTCP implements GetSockOpt when level is SOL_TCP. +func getSockOptTCP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interface{}, *syserr.Error) { + switch name { + case linux.TCP_NODELAY: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.DelayOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + if v == 0 { + return int32(1), nil + } + return int32(0), nil + + case linux.TCP_CORK: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.CorkOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.TCP_QUICKACK: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.QuickAckOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.TCP_KEEPIDLE: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.KeepaliveIdleOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(time.Duration(v) / time.Second), nil + + case linux.TCP_KEEPINTVL: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.KeepaliveIntervalOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(time.Duration(v) / time.Second), nil + + case linux.TCP_INFO: + var v tcpip.TCPInfoOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + // TODO(b/64800844): Translate fields once they are added to + // tcpip.TCPInfoOption. + info := linux.TCPInfo{} + + // Linux truncates the output binary to outLen. + ib := binary.Marshal(nil, usermem.ByteOrder, &info) + if len(ib) > outLen { + ib = ib[:outLen] + } + + return ib, nil + + case linux.TCP_CC_INFO, + linux.TCP_NOTSENT_LOWAT, + linux.TCP_ZEROCOPY_RECEIVE: + + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEventTCP(t, name) + } + return nil, syserr.ErrProtocolNotAvailable +} + +// getSockOptIPv6 implements GetSockOpt when level is SOL_IPV6. +func getSockOptIPv6(t *kernel.Task, ep commonEndpoint, name, outLen int) (interface{}, *syserr.Error) { + switch name { + case linux.IPV6_V6ONLY: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.V6OnlyOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.IPV6_PATHMTU: + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEventIPv6(t, name) + } + return nil, syserr.ErrProtocolNotAvailable +} + +// getSockOptIP implements GetSockOpt when level is SOL_IP. +func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interface{}, *syserr.Error) { + switch name { + case linux.IP_MULTICAST_TTL: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.MulticastTTLOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + case linux.IP_MULTICAST_IF: + if outLen < len(linux.InetAddr{}) { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.MulticastInterfaceOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + a, _ := ConvertAddress(linux.AF_INET, tcpip.FullAddress{Addr: v.InterfaceAddr}) + + return a.(linux.SockAddrInet).Addr, nil + + case linux.IP_MULTICAST_LOOP: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.MulticastLoopOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + if v { + return int32(1), nil + } + return int32(0), nil + + default: + emitUnimplementedEventIP(t, name) + } + return nil, syserr.ErrProtocolNotAvailable +} + +// SetSockOpt implements the linux syscall setsockopt(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error { + // TODO(b/78348848): Unlike other socket options, SO_TIMESTAMP is + // implemented specifically for epsocket.SocketOperations rather than + // commonEndpoint. commonEndpoint should be extended to support socket + // options where the implementation is not shared, as unix sockets need + // their own support for SO_TIMESTAMP. + if level == linux.SOL_SOCKET && name == linux.SO_TIMESTAMP { + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + s.readMu.Lock() + defer s.readMu.Unlock() + s.sockOptTimestamp = usermem.ByteOrder.Uint32(optVal) != 0 + return nil + } + + return SetSockOpt(t, s, s.Endpoint, level, name, optVal) +} + +// SetSockOpt can be used to implement the linux syscall setsockopt(2) for +// sockets backed by a commonEndpoint. +func SetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, level int, name int, optVal []byte) *syserr.Error { + switch level { + case linux.SOL_SOCKET: + return setSockOptSocket(t, s, ep, name, optVal) + + case linux.SOL_TCP: + return setSockOptTCP(t, ep, name, optVal) + + case linux.SOL_IPV6: + return setSockOptIPv6(t, ep, name, optVal) + + case linux.SOL_IP: + return setSockOptIP(t, ep, name, optVal) + + case linux.SOL_UDP, + linux.SOL_ICMPV6, + linux.SOL_RAW, + linux.SOL_PACKET: + + t.Kernel().EmitUnimplementedEvent(t) + } + + // Default to the old behavior; hand off to network stack. + return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) +} + +// setSockOptSocket implements SetSockOpt when level is SOL_SOCKET. +func setSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int, optVal []byte) *syserr.Error { + switch name { + case linux.SO_SNDBUF: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.SendBufferSizeOption(v))) + + case linux.SO_RCVBUF: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.ReceiveBufferSizeOption(v))) + + case linux.SO_REUSEADDR: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.ReuseAddressOption(v))) + + case linux.SO_REUSEPORT: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.ReusePortOption(v))) + + case linux.SO_BROADCAST: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.BroadcastOption(v))) + + case linux.SO_PASSCRED: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.PasscredOption(v))) + + case linux.SO_KEEPALIVE: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.KeepaliveEnabledOption(v))) + + case linux.SO_SNDTIMEO: + if len(optVal) < linux.SizeOfTimeval { + return syserr.ErrInvalidArgument + } + + var v linux.Timeval + binary.Unmarshal(optVal[:linux.SizeOfTimeval], usermem.ByteOrder, &v) + if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) { + return syserr.ErrDomain + } + s.SetSendTimeout(v.ToNsecCapped()) + return nil + + case linux.SO_RCVTIMEO: + if len(optVal) < linux.SizeOfTimeval { + return syserr.ErrInvalidArgument + } + + var v linux.Timeval + binary.Unmarshal(optVal[:linux.SizeOfTimeval], usermem.ByteOrder, &v) + if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) { + return syserr.ErrDomain + } + s.SetRecvTimeout(v.ToNsecCapped()) + return nil + + case linux.SO_OOBINLINE: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + + if v == 0 { + socket.SetSockOptEmitUnimplementedEvent(t, name) + } + + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.OutOfBandInlineOption(v))) + + case linux.SO_LINGER: + if len(optVal) < linux.SizeOfLinger { + return syserr.ErrInvalidArgument + } + + var v linux.Linger + binary.Unmarshal(optVal[:linux.SizeOfLinger], usermem.ByteOrder, &v) + + if v != (linux.Linger{}) { + socket.SetSockOptEmitUnimplementedEvent(t, name) + } + + return nil + + default: + socket.SetSockOptEmitUnimplementedEvent(t, name) + } + + // Default to the old behavior; hand off to network stack. + return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) +} + +// setSockOptTCP implements SetSockOpt when level is SOL_TCP. +func setSockOptTCP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error { + switch name { + case linux.TCP_NODELAY: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + var o tcpip.DelayOption + if v == 0 { + o = 1 + } + return syserr.TranslateNetstackError(ep.SetSockOpt(o)) + + case linux.TCP_CORK: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.CorkOption(v))) + + case linux.TCP_QUICKACK: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.QuickAckOption(v))) + + case linux.TCP_KEEPIDLE: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + if v < 1 || v > linux.MAX_TCP_KEEPIDLE { + return syserr.ErrInvalidArgument + } + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.KeepaliveIdleOption(time.Second * time.Duration(v)))) + + case linux.TCP_KEEPINTVL: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + if v < 1 || v > linux.MAX_TCP_KEEPINTVL { + return syserr.ErrInvalidArgument + } + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.KeepaliveIntervalOption(time.Second * time.Duration(v)))) + + case linux.TCP_REPAIR_OPTIONS: + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEventTCP(t, name) + } + + // Default to the old behavior; hand off to network stack. + return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) +} + +// setSockOptIPv6 implements SetSockOpt when level is SOL_IPV6. +func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error { + switch name { + case linux.IPV6_V6ONLY: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := usermem.ByteOrder.Uint32(optVal) + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.V6OnlyOption(v))) + + case linux.IPV6_ADD_MEMBERSHIP, + linux.IPV6_DROP_MEMBERSHIP, + linux.IPV6_IPSEC_POLICY, + linux.IPV6_JOIN_ANYCAST, + linux.IPV6_LEAVE_ANYCAST, + linux.IPV6_PKTINFO, + linux.IPV6_ROUTER_ALERT, + linux.IPV6_XFRM_POLICY, + linux.MCAST_BLOCK_SOURCE, + linux.MCAST_JOIN_GROUP, + linux.MCAST_JOIN_SOURCE_GROUP, + linux.MCAST_LEAVE_GROUP, + linux.MCAST_LEAVE_SOURCE_GROUP, + linux.MCAST_UNBLOCK_SOURCE: + + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEventIPv6(t, name) + } + + // Default to the old behavior; hand off to network stack. + return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) +} + +var ( + inetMulticastRequestSize = int(binary.Size(linux.InetMulticastRequest{})) + inetMulticastRequestWithNICSize = int(binary.Size(linux.InetMulticastRequestWithNIC{})) +) + +// copyInMulticastRequest copies in a variable-size multicast request. The +// kernel determines which structure was passed by its length. IP_MULTICAST_IF +// supports ip_mreqn, ip_mreq and in_addr, while IP_ADD_MEMBERSHIP and +// IP_DROP_MEMBERSHIP only support ip_mreqn and ip_mreq. To handle this, +// allowAddr controls whether in_addr is accepted or rejected. +func copyInMulticastRequest(optVal []byte, allowAddr bool) (linux.InetMulticastRequestWithNIC, *syserr.Error) { + if len(optVal) < len(linux.InetAddr{}) { + return linux.InetMulticastRequestWithNIC{}, syserr.ErrInvalidArgument + } + + if len(optVal) < inetMulticastRequestSize { + if !allowAddr { + return linux.InetMulticastRequestWithNIC{}, syserr.ErrInvalidArgument + } + + var req linux.InetMulticastRequestWithNIC + copy(req.InterfaceAddr[:], optVal) + return req, nil + } + + if len(optVal) >= inetMulticastRequestWithNICSize { + var req linux.InetMulticastRequestWithNIC + binary.Unmarshal(optVal[:inetMulticastRequestWithNICSize], usermem.ByteOrder, &req) + return req, nil + } + + var req linux.InetMulticastRequestWithNIC + binary.Unmarshal(optVal[:inetMulticastRequestSize], usermem.ByteOrder, &req.InetMulticastRequest) + return req, nil +} + +// parseIntOrChar copies either a 32-bit int or an 8-bit uint out of buf. +// +// net/ipv4/ip_sockglue.c:do_ip_setsockopt does this for its socket options. +func parseIntOrChar(buf []byte) (int32, *syserr.Error) { + if len(buf) == 0 { + return 0, syserr.ErrInvalidArgument + } + + if len(buf) >= sizeOfInt32 { + return int32(usermem.ByteOrder.Uint32(buf)), nil + } + + return int32(buf[0]), nil +} + +// setSockOptIP implements SetSockOpt when level is SOL_IP. +func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error { + switch name { + case linux.IP_MULTICAST_TTL: + v, err := parseIntOrChar(optVal) + if err != nil { + return err + } + + if v == -1 { + // Linux translates -1 to 1. + v = 1 + } + if v < 0 || v > 255 { + return syserr.ErrInvalidArgument + } + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastTTLOption(v))) + + case linux.IP_ADD_MEMBERSHIP: + req, err := copyInMulticastRequest(optVal, false /* allowAddr */) + if err != nil { + return err + } + + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.AddMembershipOption{ + NIC: tcpip.NICID(req.InterfaceIndex), + // TODO(igudger): Change AddMembership to use the standard + // any address representation. + InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]), + MulticastAddr: tcpip.Address(req.MulticastAddr[:]), + })) + + case linux.IP_DROP_MEMBERSHIP: + req, err := copyInMulticastRequest(optVal, false /* allowAddr */) + if err != nil { + return err + } + + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.RemoveMembershipOption{ + NIC: tcpip.NICID(req.InterfaceIndex), + // TODO(igudger): Change DropMembership to use the standard + // any address representation. + InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]), + MulticastAddr: tcpip.Address(req.MulticastAddr[:]), + })) + + case linux.IP_MULTICAST_IF: + req, err := copyInMulticastRequest(optVal, true /* allowAddr */) + if err != nil { + return err + } + + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastInterfaceOption{ + NIC: tcpip.NICID(req.InterfaceIndex), + InterfaceAddr: bytesToIPAddress(req.InterfaceAddr[:]), + })) + + case linux.IP_MULTICAST_LOOP: + v, err := parseIntOrChar(optVal) + if err != nil { + return err + } + + return syserr.TranslateNetstackError(ep.SetSockOpt( + tcpip.MulticastLoopOption(v != 0), + )) + + case linux.MCAST_JOIN_GROUP: + // FIXME(b/124219304): Implement MCAST_JOIN_GROUP. + t.Kernel().EmitUnimplementedEvent(t) + return syserr.ErrInvalidArgument + + case linux.IP_ADD_SOURCE_MEMBERSHIP, + linux.IP_BIND_ADDRESS_NO_PORT, + linux.IP_BLOCK_SOURCE, + linux.IP_CHECKSUM, + linux.IP_DROP_SOURCE_MEMBERSHIP, + linux.IP_FREEBIND, + linux.IP_HDRINCL, + linux.IP_IPSEC_POLICY, + linux.IP_MINTTL, + linux.IP_MSFILTER, + linux.IP_MTU_DISCOVER, + linux.IP_MULTICAST_ALL, + linux.IP_NODEFRAG, + linux.IP_OPTIONS, + linux.IP_PASSSEC, + linux.IP_PKTINFO, + linux.IP_RECVERR, + linux.IP_RECVFRAGSIZE, + linux.IP_RECVOPTS, + linux.IP_RECVORIGDSTADDR, + linux.IP_RECVTOS, + linux.IP_RECVTTL, + linux.IP_RETOPTS, + linux.IP_TOS, + linux.IP_TRANSPARENT, + linux.IP_TTL, + linux.IP_UNBLOCK_SOURCE, + linux.IP_UNICAST_IF, + linux.IP_XFRM_POLICY, + linux.MCAST_BLOCK_SOURCE, + linux.MCAST_JOIN_SOURCE_GROUP, + linux.MCAST_LEAVE_GROUP, + linux.MCAST_LEAVE_SOURCE_GROUP, + linux.MCAST_MSFILTER, + linux.MCAST_UNBLOCK_SOURCE: + + t.Kernel().EmitUnimplementedEvent(t) + } + + // Default to the old behavior; hand off to network stack. + return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) +} + +// emitUnimplementedEventTCP emits unimplemented event if name is valid. This +// function contains names that are common between Get and SetSockOpt when +// level is SOL_TCP. +func emitUnimplementedEventTCP(t *kernel.Task, name int) { + switch name { + case linux.TCP_CONGESTION, + linux.TCP_CORK, + linux.TCP_DEFER_ACCEPT, + linux.TCP_FASTOPEN, + linux.TCP_FASTOPEN_CONNECT, + linux.TCP_FASTOPEN_KEY, + linux.TCP_FASTOPEN_NO_COOKIE, + linux.TCP_INQ, + linux.TCP_KEEPCNT, + linux.TCP_KEEPIDLE, + linux.TCP_KEEPINTVL, + linux.TCP_LINGER2, + linux.TCP_MAXSEG, + linux.TCP_QUEUE_SEQ, + linux.TCP_QUICKACK, + linux.TCP_REPAIR, + linux.TCP_REPAIR_QUEUE, + linux.TCP_REPAIR_WINDOW, + linux.TCP_SAVED_SYN, + linux.TCP_SAVE_SYN, + linux.TCP_SYNCNT, + linux.TCP_THIN_DUPACK, + linux.TCP_THIN_LINEAR_TIMEOUTS, + linux.TCP_TIMESTAMP, + linux.TCP_ULP, + linux.TCP_USER_TIMEOUT, + linux.TCP_WINDOW_CLAMP: + + t.Kernel().EmitUnimplementedEvent(t) + } +} + +// emitUnimplementedEventIPv6 emits unimplemented event if name is valid. It +// contains names that are common between Get and SetSockOpt when level is +// SOL_IPV6. +func emitUnimplementedEventIPv6(t *kernel.Task, name int) { + switch name { + case linux.IPV6_2292DSTOPTS, + linux.IPV6_2292HOPLIMIT, + linux.IPV6_2292HOPOPTS, + linux.IPV6_2292PKTINFO, + linux.IPV6_2292PKTOPTIONS, + linux.IPV6_2292RTHDR, + linux.IPV6_ADDR_PREFERENCES, + linux.IPV6_AUTOFLOWLABEL, + linux.IPV6_DONTFRAG, + linux.IPV6_DSTOPTS, + linux.IPV6_FLOWINFO, + linux.IPV6_FLOWINFO_SEND, + linux.IPV6_FLOWLABEL_MGR, + linux.IPV6_FREEBIND, + linux.IPV6_HOPOPTS, + linux.IPV6_MINHOPCOUNT, + linux.IPV6_MTU, + linux.IPV6_MTU_DISCOVER, + linux.IPV6_MULTICAST_ALL, + linux.IPV6_MULTICAST_HOPS, + linux.IPV6_MULTICAST_IF, + linux.IPV6_MULTICAST_LOOP, + linux.IPV6_RECVDSTOPTS, + linux.IPV6_RECVERR, + linux.IPV6_RECVFRAGSIZE, + linux.IPV6_RECVHOPLIMIT, + linux.IPV6_RECVHOPOPTS, + linux.IPV6_RECVORIGDSTADDR, + linux.IPV6_RECVPATHMTU, + linux.IPV6_RECVPKTINFO, + linux.IPV6_RECVRTHDR, + linux.IPV6_RECVTCLASS, + linux.IPV6_RTHDR, + linux.IPV6_RTHDRDSTOPTS, + linux.IPV6_TCLASS, + linux.IPV6_TRANSPARENT, + linux.IPV6_UNICAST_HOPS, + linux.IPV6_UNICAST_IF, + linux.MCAST_MSFILTER, + linux.IPV6_ADDRFORM: + + t.Kernel().EmitUnimplementedEvent(t) + } +} + +// emitUnimplementedEventIP emits unimplemented event if name is valid. It +// contains names that are common between Get and SetSockOpt when level is +// SOL_IP. +func emitUnimplementedEventIP(t *kernel.Task, name int) { + switch name { + case linux.IP_TOS, + linux.IP_TTL, + linux.IP_HDRINCL, + linux.IP_OPTIONS, + linux.IP_ROUTER_ALERT, + linux.IP_RECVOPTS, + linux.IP_RETOPTS, + linux.IP_PKTINFO, + linux.IP_PKTOPTIONS, + linux.IP_MTU_DISCOVER, + linux.IP_RECVERR, + linux.IP_RECVTTL, + linux.IP_RECVTOS, + linux.IP_MTU, + linux.IP_FREEBIND, + linux.IP_IPSEC_POLICY, + linux.IP_XFRM_POLICY, + linux.IP_PASSSEC, + linux.IP_TRANSPARENT, + linux.IP_ORIGDSTADDR, + linux.IP_MINTTL, + linux.IP_NODEFRAG, + linux.IP_CHECKSUM, + linux.IP_BIND_ADDRESS_NO_PORT, + linux.IP_RECVFRAGSIZE, + linux.IP_MULTICAST_IF, + linux.IP_MULTICAST_TTL, + linux.IP_MULTICAST_LOOP, + linux.IP_ADD_MEMBERSHIP, + linux.IP_DROP_MEMBERSHIP, + linux.IP_UNBLOCK_SOURCE, + linux.IP_BLOCK_SOURCE, + linux.IP_ADD_SOURCE_MEMBERSHIP, + linux.IP_DROP_SOURCE_MEMBERSHIP, + linux.IP_MSFILTER, + linux.MCAST_JOIN_GROUP, + linux.MCAST_BLOCK_SOURCE, + linux.MCAST_UNBLOCK_SOURCE, + linux.MCAST_LEAVE_GROUP, + linux.MCAST_JOIN_SOURCE_GROUP, + linux.MCAST_LEAVE_SOURCE_GROUP, + linux.MCAST_MSFILTER, + linux.IP_MULTICAST_ALL, + linux.IP_UNICAST_IF: + + t.Kernel().EmitUnimplementedEvent(t) + } +} + +// isLinkLocal determines if the given IPv6 address is link-local. This is the +// case when it has the fe80::/10 prefix. This check is used to determine when +// the NICID is relevant for a given IPv6 address. +func isLinkLocal(addr tcpip.Address) bool { + return len(addr) >= 2 && addr[0] == 0xfe && addr[1]&0xc0 == 0x80 +} + +// ConvertAddress converts the given address to a native format. +func ConvertAddress(family int, addr tcpip.FullAddress) (interface{}, uint32) { + switch family { + case linux.AF_UNIX: + var out linux.SockAddrUnix + out.Family = linux.AF_UNIX + l := len([]byte(addr.Addr)) + for i := 0; i < l; i++ { + out.Path[i] = int8(addr.Addr[i]) + } + + // Linux returns the used length of the address struct (including the + // null terminator) for filesystem paths. The Family field is 2 bytes. + // It is sometimes allowed to exclude the null terminator if the + // address length is the max. Abstract and empty paths always return + // the full exact length. + if l == 0 || out.Path[0] == 0 || l == len(out.Path) { + return out, uint32(2 + l) + } + return out, uint32(3 + l) + case linux.AF_INET: + var out linux.SockAddrInet + copy(out.Addr[:], addr.Addr) + out.Family = linux.AF_INET + out.Port = htons(addr.Port) + return out, uint32(binary.Size(out)) + case linux.AF_INET6: + var out linux.SockAddrInet6 + if len(addr.Addr) == 4 { + // Copy address is v4-mapped format. + copy(out.Addr[12:], addr.Addr) + out.Addr[10] = 0xff + out.Addr[11] = 0xff + } else { + copy(out.Addr[:], addr.Addr) + } + out.Family = linux.AF_INET6 + out.Port = htons(addr.Port) + if isLinkLocal(addr.Addr) { + out.Scope_id = uint32(addr.NIC) + } + return out, uint32(binary.Size(out)) + default: + return nil, 0 + } +} + +// GetSockName implements the linux syscall getsockname(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr, err := s.Endpoint.GetLocalAddress() + if err != nil { + return nil, 0, syserr.TranslateNetstackError(err) + } + + a, l := ConvertAddress(s.family, addr) + return a, l, nil +} + +// GetPeerName implements the linux syscall getpeername(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr, err := s.Endpoint.GetRemoteAddress() + if err != nil { + return nil, 0, syserr.TranslateNetstackError(err) + } + + a, l := ConvertAddress(s.family, addr) + return a, l, nil +} + +// coalescingRead is the fast path for non-blocking, non-peek, stream-based +// case. It coalesces as many packets as possible before returning to the +// caller. +// +// Precondition: s.readMu must be locked. +func (s *SocketOperations) coalescingRead(ctx context.Context, dst usermem.IOSequence, discard bool) (int, *syserr.Error) { + var err *syserr.Error + var copied int + + // Copy as many views as possible into the user-provided buffer. + for dst.NumBytes() != 0 { + err = s.fetchReadView() + if err != nil { + break + } + + var n int + var e error + if discard { + n = len(s.readView) + if int64(n) > dst.NumBytes() { + n = int(dst.NumBytes()) + } + } else { + n, e = dst.CopyOut(ctx, s.readView) + // Set the control message, even if 0 bytes were read. + if e == nil { + s.updateTimestamp() + } + } + copied += n + s.readView.TrimFront(n) + dst = dst.DropFirst(n) + if e != nil { + err = syserr.FromError(e) + break + } + } + + // If we managed to copy something, we must deliver it. + if copied > 0 { + return copied, nil + } + + return 0, err +} + +// nonBlockingRead issues a non-blocking read. +// +// TODO(b/78348848): Support timestamps for stream sockets. +func (s *SocketOperations) nonBlockingRead(ctx context.Context, dst usermem.IOSequence, peek, trunc, senderRequested bool) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { + isPacket := s.isPacketBased() + + // Fast path for regular reads from stream (e.g., TCP) endpoints. Note + // that senderRequested is ignored for stream sockets. + if !peek && !isPacket { + // TCP sockets discard the data if MSG_TRUNC is set. + // + // This behavior is documented in man 7 tcp: + // Since version 2.4, Linux supports the use of MSG_TRUNC in the flags + // argument of recv(2) (and recvmsg(2)). This flag causes the received + // bytes of data to be discarded, rather than passed back in a + // caller-supplied buffer. + s.readMu.Lock() + n, err := s.coalescingRead(ctx, dst, trunc) + s.readMu.Unlock() + return n, 0, nil, 0, socket.ControlMessages{}, err + } + + s.readMu.Lock() + defer s.readMu.Unlock() + + if err := s.fetchReadView(); err != nil { + return 0, 0, nil, 0, socket.ControlMessages{}, err + } + + if !isPacket && peek && trunc { + // MSG_TRUNC with MSG_PEEK on a TCP socket returns the + // amount that could be read. + var rql tcpip.ReceiveQueueSizeOption + if err := s.Endpoint.GetSockOpt(&rql); err != nil { + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.TranslateNetstackError(err) + } + available := len(s.readView) + int(rql) + bufLen := int(dst.NumBytes()) + if available < bufLen { + return available, 0, nil, 0, socket.ControlMessages{}, nil + } + return bufLen, 0, nil, 0, socket.ControlMessages{}, nil + } + + n, err := dst.CopyOut(ctx, s.readView) + // Set the control message, even if 0 bytes were read. + if err == nil { + s.updateTimestamp() + } + var addr interface{} + var addrLen uint32 + if isPacket && senderRequested { + addr, addrLen = ConvertAddress(s.family, s.sender) + } + + if peek { + if l := len(s.readView); trunc && l > n { + // isPacket must be true. + return l, linux.MSG_TRUNC, addr, addrLen, s.controlMessages(), syserr.FromError(err) + } + + if isPacket || err != nil { + return n, 0, addr, addrLen, s.controlMessages(), syserr.FromError(err) + } + + // We need to peek beyond the first message. + dst = dst.DropFirst(n) + num, err := dst.CopyOutFrom(ctx, safemem.FromVecReaderFunc{func(dsts [][]byte) (int64, error) { + n, _, err := s.Endpoint.Peek(dsts) + // TODO(b/78348848): Handle peek timestamp. + if err != nil { + return int64(n), syserr.TranslateNetstackError(err).ToError() + } + return int64(n), nil + }}) + n += int(num) + if err == syserror.ErrWouldBlock && n > 0 { + // We got some data, so no need to return an error. + err = nil + } + return n, 0, nil, 0, s.controlMessages(), syserr.FromError(err) + } + + var msgLen int + if isPacket { + msgLen = len(s.readView) + s.readView = nil + } else { + msgLen = int(n) + s.readView.TrimFront(int(n)) + } + + var flags int + if msgLen > int(n) { + flags |= linux.MSG_TRUNC + } + + if trunc { + n = msgLen + } + + return n, flags, addr, addrLen, s.controlMessages(), syserr.FromError(err) +} + +func (s *SocketOperations) controlMessages() socket.ControlMessages { + return socket.ControlMessages{IP: tcpip.ControlMessages{HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp, Timestamp: s.readCM.Timestamp}} +} + +// updateTimestamp sets the timestamp for SIOCGSTAMP. It should be called after +// successfully writing packet data out to userspace. +// +// Precondition: s.readMu must be locked. +func (s *SocketOperations) updateTimestamp() { + // Save the SIOCGSTAMP timestamp only if SO_TIMESTAMP is disabled. + if !s.sockOptTimestamp { + s.timestampValid = true + s.timestampNS = s.readCM.Timestamp + } +} + +// RecvMsg implements the linux syscall recvmsg(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (n int, msgFlags int, senderAddr interface{}, senderAddrLen uint32, controlMessages socket.ControlMessages, err *syserr.Error) { + trunc := flags&linux.MSG_TRUNC != 0 + peek := flags&linux.MSG_PEEK != 0 + dontWait := flags&linux.MSG_DONTWAIT != 0 + waitAll := flags&linux.MSG_WAITALL != 0 + if senderRequested && !s.isPacketBased() { + // Stream sockets ignore the sender address. + senderRequested = false + } + n, msgFlags, senderAddr, senderAddrLen, controlMessages, err = s.nonBlockingRead(t, dst, peek, trunc, senderRequested) + + if s.isPacketBased() && err == syserr.ErrClosedForReceive && flags&linux.MSG_DONTWAIT != 0 { + // In this situation we should return EAGAIN. + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain + } + + if err != nil && (err != syserr.ErrWouldBlock || dontWait) { + // Read failed and we should not retry. + return 0, 0, nil, 0, socket.ControlMessages{}, err + } + + if err == nil && (dontWait || !waitAll || s.isPacketBased() || int64(n) >= dst.NumBytes()) { + // We got all the data we need. + return + } + + // Don't overwrite any data we received. + dst = dst.DropFirst(n) + + // We'll have to block. Register for notifications and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + for { + var rn int + rn, msgFlags, senderAddr, senderAddrLen, controlMessages, err = s.nonBlockingRead(t, dst, peek, trunc, senderRequested) + n += rn + if err != nil && err != syserr.ErrWouldBlock { + // Always stop on errors other than would block as we generally + // won't be able to get any more data. Eat the error if we got + // any data. + if n > 0 { + err = nil + } + return + } + if err == nil && (s.isPacketBased() || !waitAll || int64(rn) >= dst.NumBytes()) { + // We got all the data we need. + return + } + dst = dst.DropFirst(rn) + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if n > 0 { + return n, msgFlags, senderAddr, senderAddrLen, controlMessages, nil + } + if err == syserror.ETIMEDOUT { + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain + } + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err) + } + } +} + +// SendMsg implements the linux syscall sendmsg(2) for sockets backed by +// tcpip.Endpoint. +func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) { + // Reject Unix control messages. + if !controlMessages.Unix.Empty() { + return 0, syserr.ErrInvalidArgument + } + + var addr *tcpip.FullAddress + if len(to) > 0 { + addrBuf, err := GetAddress(s.family, to) + if err != nil { + return 0, err + } + + addr = &addrBuf + } + + v := buffer.NewView(int(src.NumBytes())) + + // Copy all the data into the buffer. + if _, err := src.CopyIn(t, v); err != nil { + return 0, syserr.FromError(err) + } + + opts := tcpip.WriteOptions{ + To: addr, + More: flags&linux.MSG_MORE != 0, + EndOfRecord: flags&linux.MSG_EOR != 0, + } + + n, resCh, err := s.Endpoint.Write(tcpip.SlicePayload(v), opts) + if resCh != nil { + if err := t.Block(resCh); err != nil { + return 0, syserr.FromError(err) + } + n, _, err = s.Endpoint.Write(tcpip.SlicePayload(v), opts) + } + dontWait := flags&linux.MSG_DONTWAIT != 0 + if err == nil && (n >= uintptr(len(v)) || dontWait) { + // Complete write. + return int(n), nil + } + if err != nil && (err != tcpip.ErrWouldBlock || dontWait) { + return int(n), syserr.TranslateNetstackError(err) + } + + // We'll have to block. Register for notification and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + + v.TrimFront(int(n)) + total := n + for { + n, _, err = s.Endpoint.Write(tcpip.SlicePayload(v), opts) + v.TrimFront(int(n)) + total += n + + if err != nil && err != tcpip.ErrWouldBlock && total == 0 { + return 0, syserr.TranslateNetstackError(err) + } + + if err == nil && len(v) == 0 || err != nil && err != tcpip.ErrWouldBlock { + return int(total), nil + } + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + return int(total), syserr.ErrTryAgain + } + // handleIOError will consume errors from t.Block if needed. + return int(total), syserr.FromError(err) + } + } +} + +// Ioctl implements fs.FileOperations.Ioctl. +func (s *SocketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + // SIOCGSTAMP is implemented by epsocket rather than all commonEndpoint + // sockets. + // TODO(b/78348848): Add a commonEndpoint method to support SIOCGSTAMP. + if int(args[1].Int()) == syscall.SIOCGSTAMP { + s.readMu.Lock() + defer s.readMu.Unlock() + if !s.timestampValid { + return 0, syserror.ENOENT + } + + tv := linux.NsecToTimeval(s.timestampNS) + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), &tv, usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err + } + + return Ioctl(ctx, s.Endpoint, io, args) +} + +// Ioctl performs a socket ioctl. +func Ioctl(ctx context.Context, ep commonEndpoint, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + switch arg := int(args[1].Int()); arg { + case syscall.SIOCGIFFLAGS, + syscall.SIOCGIFADDR, + syscall.SIOCGIFBRDADDR, + syscall.SIOCGIFDSTADDR, + syscall.SIOCGIFHWADDR, + syscall.SIOCGIFINDEX, + syscall.SIOCGIFMAP, + syscall.SIOCGIFMETRIC, + syscall.SIOCGIFMTU, + syscall.SIOCGIFNAME, + syscall.SIOCGIFNETMASK, + syscall.SIOCGIFTXQLEN: + + var ifr linux.IFReq + if _, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &ifr, usermem.IOOpts{ + AddressSpaceActive: true, + }); err != nil { + return 0, err + } + if err := interfaceIoctl(ctx, io, arg, &ifr); err != nil { + return 0, err.ToError() + } + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), &ifr, usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err + + case syscall.SIOCGIFCONF: + // Return a list of interface addresses or the buffer size + // necessary to hold the list. + var ifc linux.IFConf + if _, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &ifc, usermem.IOOpts{ + AddressSpaceActive: true, + }); err != nil { + return 0, err + } + + if err := ifconfIoctl(ctx, io, &ifc); err != nil { + return 0, err + } + + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), ifc, usermem.IOOpts{ + AddressSpaceActive: true, + }) + + return 0, err + + case linux.TIOCINQ: + var v tcpip.ReceiveQueueSizeOption + if err := ep.GetSockOpt(&v); err != nil { + return 0, syserr.TranslateNetstackError(err).ToError() + } + + if v > math.MaxInt32 { + v = math.MaxInt32 + } + // Copy result to user-space. + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), int32(v), usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err + + case linux.TIOCOUTQ: + var v tcpip.SendQueueSizeOption + if err := ep.GetSockOpt(&v); err != nil { + return 0, syserr.TranslateNetstackError(err).ToError() + } + + if v > math.MaxInt32 { + v = math.MaxInt32 + } + + // Copy result to user-space. + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), int32(v), usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err + + case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG: + unimpl.EmitUnimplementedEvent(ctx) + } + + return 0, syserror.ENOTTY +} + +// interfaceIoctl implements interface requests. +func interfaceIoctl(ctx context.Context, io usermem.IO, arg int, ifr *linux.IFReq) *syserr.Error { + var ( + iface inet.Interface + index int32 + found bool + ) + + // Find the relevant device. + stack := inet.StackFromContext(ctx) + if stack == nil { + return syserr.ErrNoDevice + } + + // SIOCGIFNAME uses ifr.ifr_ifindex rather than ifr.ifr_name to + // identify a device. + if arg == syscall.SIOCGIFNAME { + // Gets the name of the interface given the interface index + // stored in ifr_ifindex. + index = int32(usermem.ByteOrder.Uint32(ifr.Data[:4])) + if iface, ok := stack.Interfaces()[index]; ok { + ifr.SetName(iface.Name) + return nil + } + return syserr.ErrNoDevice + } + + // Find the relevant device. + for index, iface = range stack.Interfaces() { + if iface.Name == ifr.Name() { + found = true + break + } + } + if !found { + return syserr.ErrNoDevice + } + + switch arg { + case syscall.SIOCGIFINDEX: + // Copy out the index to the data. + usermem.ByteOrder.PutUint32(ifr.Data[:], uint32(index)) + + case syscall.SIOCGIFHWADDR: + // Copy the hardware address out. + ifr.Data[0] = 6 // IEEE802.2 arp type. + ifr.Data[1] = 0 + n := copy(ifr.Data[2:], iface.Addr) + for i := 2 + n; i < len(ifr.Data); i++ { + ifr.Data[i] = 0 // Clear padding. + } + usermem.ByteOrder.PutUint16(ifr.Data[:2], uint16(n)) + + case syscall.SIOCGIFFLAGS: + f, err := interfaceStatusFlags(stack, iface.Name) + if err != nil { + return err + } + // Drop the flags that don't fit in the size that we need to return. This + // matches Linux behavior. + usermem.ByteOrder.PutUint16(ifr.Data[:2], uint16(f)) + + case syscall.SIOCGIFADDR: + // Copy the IPv4 address out. + for _, addr := range stack.InterfaceAddrs()[index] { + // This ioctl is only compatible with AF_INET addresses. + if addr.Family != linux.AF_INET { + continue + } + copy(ifr.Data[4:8], addr.Addr) + break + } + + case syscall.SIOCGIFMETRIC: + // Gets the metric of the device. As per netdevice(7), this + // always just sets ifr_metric to 0. + usermem.ByteOrder.PutUint32(ifr.Data[:4], 0) + + case syscall.SIOCGIFMTU: + // Gets the MTU of the device. + usermem.ByteOrder.PutUint32(ifr.Data[:4], iface.MTU) + + case syscall.SIOCGIFMAP: + // Gets the hardware parameters of the device. + // TODO(b/71872867): Implement. + + case syscall.SIOCGIFTXQLEN: + // Gets the transmit queue length of the device. + // TODO(b/71872867): Implement. + + case syscall.SIOCGIFDSTADDR: + // Gets the destination address of a point-to-point device. + // TODO(b/71872867): Implement. + + case syscall.SIOCGIFBRDADDR: + // Gets the broadcast address of a device. + // TODO(b/71872867): Implement. + + case syscall.SIOCGIFNETMASK: + // Gets the network mask of a device. + for _, addr := range stack.InterfaceAddrs()[index] { + // This ioctl is only compatible with AF_INET addresses. + if addr.Family != linux.AF_INET { + continue + } + // Populate ifr.ifr_netmask (type sockaddr). + usermem.ByteOrder.PutUint16(ifr.Data[0:2], uint16(linux.AF_INET)) + usermem.ByteOrder.PutUint16(ifr.Data[2:4], 0) + var mask uint32 = 0xffffffff << (32 - addr.PrefixLen) + // Netmask is expected to be returned as a big endian + // value. + binary.BigEndian.PutUint32(ifr.Data[4:8], mask) + break + } + + default: + // Not a valid call. + return syserr.ErrInvalidArgument + } + + return nil +} + +// ifconfIoctl populates a struct ifconf for the SIOCGIFCONF ioctl. +func ifconfIoctl(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error { + // If Ptr is NULL, return the necessary buffer size via Len. + // Otherwise, write up to Len bytes starting at Ptr containing ifreq + // structs. + stack := inet.StackFromContext(ctx) + if stack == nil { + return syserr.ErrNoDevice.ToError() + } + + if ifc.Ptr == 0 { + ifc.Len = int32(len(stack.Interfaces())) * int32(linux.SizeOfIFReq) + return nil + } + + max := ifc.Len + ifc.Len = 0 + for key, ifaceAddrs := range stack.InterfaceAddrs() { + iface := stack.Interfaces()[key] + for _, ifaceAddr := range ifaceAddrs { + // Don't write past the end of the buffer. + if ifc.Len+int32(linux.SizeOfIFReq) > max { + break + } + if ifaceAddr.Family != linux.AF_INET { + continue + } + + // Populate ifr.ifr_addr. + ifr := linux.IFReq{} + ifr.SetName(iface.Name) + usermem.ByteOrder.PutUint16(ifr.Data[0:2], uint16(ifaceAddr.Family)) + usermem.ByteOrder.PutUint16(ifr.Data[2:4], 0) + copy(ifr.Data[4:8], ifaceAddr.Addr[:4]) + + // Copy the ifr to userspace. + dst := uintptr(ifc.Ptr) + uintptr(ifc.Len) + ifc.Len += int32(linux.SizeOfIFReq) + if _, err := usermem.CopyObjectOut(ctx, io, usermem.Addr(dst), ifr, usermem.IOOpts{ + AddressSpaceActive: true, + }); err != nil { + return err + } + } + } + return nil +} + +// interfaceStatusFlags returns status flags for an interface in the stack. +// Flag values and meanings are described in greater detail in netdevice(7) in +// the SIOCGIFFLAGS section. +func interfaceStatusFlags(stack inet.Stack, name string) (uint32, *syserr.Error) { + // epsocket should only ever be passed an epsocket.Stack. + epstack, ok := stack.(*Stack) + if !ok { + return 0, errStackType + } + + // Find the NIC corresponding to this interface. + for _, info := range epstack.Stack.NICInfo() { + if info.Name == name { + return nicStateFlagsToLinux(info.Flags), nil + } + } + return 0, syserr.ErrNoDevice +} + +func nicStateFlagsToLinux(f stack.NICStateFlags) uint32 { + var rv uint32 + if f.Up { + rv |= linux.IFF_UP | linux.IFF_LOWER_UP + } + if f.Running { + rv |= linux.IFF_RUNNING + } + if f.Promiscuous { + rv |= linux.IFF_PROMISC + } + if f.Loopback { + rv |= linux.IFF_LOOPBACK + } + return rv +} diff --git a/pkg/sentry/socket/epsocket/epsocket_state_autogen.go b/pkg/sentry/socket/epsocket/epsocket_state_autogen.go new file mode 100755 index 000000000..4b407b796 --- /dev/null +++ b/pkg/sentry/socket/epsocket/epsocket_state_autogen.go @@ -0,0 +1,52 @@ +// automatically generated by stateify. + +package epsocket + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *SocketOperations) beforeSave() {} +func (x *SocketOperations) save(m state.Map) { + x.beforeSave() + m.Save("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Save("Queue", &x.Queue) + m.Save("family", &x.family) + m.Save("Endpoint", &x.Endpoint) + m.Save("skType", &x.skType) + m.Save("readView", &x.readView) + m.Save("readCM", &x.readCM) + m.Save("sender", &x.sender) + m.Save("sockOptTimestamp", &x.sockOptTimestamp) + m.Save("timestampValid", &x.timestampValid) + m.Save("timestampNS", &x.timestampNS) +} + +func (x *SocketOperations) afterLoad() {} +func (x *SocketOperations) load(m state.Map) { + m.Load("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Load("Queue", &x.Queue) + m.Load("family", &x.family) + m.Load("Endpoint", &x.Endpoint) + m.Load("skType", &x.skType) + m.Load("readView", &x.readView) + m.Load("readCM", &x.readCM) + m.Load("sender", &x.sender) + m.Load("sockOptTimestamp", &x.sockOptTimestamp) + m.Load("timestampValid", &x.timestampValid) + m.Load("timestampNS", &x.timestampNS) +} + +func (x *Stack) beforeSave() {} +func (x *Stack) save(m state.Map) { + x.beforeSave() +} + +func (x *Stack) load(m state.Map) { + m.AfterLoad(x.afterLoad) +} + +func init() { + state.Register("epsocket.SocketOperations", (*SocketOperations)(nil), state.Fns{Save: (*SocketOperations).save, Load: (*SocketOperations).load}) + state.Register("epsocket.Stack", (*Stack)(nil), state.Fns{Save: (*Stack).save, Load: (*Stack).load}) +} diff --git a/pkg/sentry/socket/epsocket/provider.go b/pkg/sentry/socket/epsocket/provider.go new file mode 100644 index 000000000..ec930d8d5 --- /dev/null +++ b/pkg/sentry/socket/epsocket/provider.go @@ -0,0 +1,140 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package epsocket + +import ( + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/tcpip/header" + "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4" + "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6" + "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp" + "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/udp" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// provider is an inet socket provider. +type provider struct { + family int + netProto tcpip.NetworkProtocolNumber +} + +// getTransportProtocol figures out transport protocol. Currently only TCP, +// UDP, and ICMP are supported. +func getTransportProtocol(ctx context.Context, stype transport.SockType, protocol int) (tcpip.TransportProtocolNumber, *syserr.Error) { + switch stype { + case linux.SOCK_STREAM: + if protocol != 0 && protocol != syscall.IPPROTO_TCP { + return 0, syserr.ErrInvalidArgument + } + return tcp.ProtocolNumber, nil + + case linux.SOCK_DGRAM: + switch protocol { + case 0, syscall.IPPROTO_UDP: + return udp.ProtocolNumber, nil + case syscall.IPPROTO_ICMP: + return header.ICMPv4ProtocolNumber, nil + case syscall.IPPROTO_ICMPV6: + return header.ICMPv6ProtocolNumber, nil + } + + case linux.SOCK_RAW: + // Raw sockets require CAP_NET_RAW. + creds := auth.CredentialsFromContext(ctx) + if !creds.HasCapability(linux.CAP_NET_RAW) { + return 0, syserr.ErrPermissionDenied + } + + switch protocol { + case syscall.IPPROTO_ICMP: + return header.ICMPv4ProtocolNumber, nil + case syscall.IPPROTO_UDP: + return header.UDPProtocolNumber, nil + case syscall.IPPROTO_TCP: + return header.TCPProtocolNumber, nil + } + } + return 0, syserr.ErrProtocolNotSupported +} + +// Socket creates a new socket object for the AF_INET or AF_INET6 family. +func (p *provider) Socket(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *syserr.Error) { + // Fail right away if we don't have a stack. + stack := t.NetworkContext() + if stack == nil { + // Don't propagate an error here. Instead, allow the socket + // code to continue searching for another provider. + return nil, nil + } + eps, ok := stack.(*Stack) + if !ok { + return nil, nil + } + + // Figure out the transport protocol. + transProto, err := getTransportProtocol(t, stype, protocol) + if err != nil { + return nil, err + } + + // Create the endpoint. + var ep tcpip.Endpoint + var e *tcpip.Error + wq := &waiter.Queue{} + if stype == linux.SOCK_RAW { + ep, e = eps.Stack.NewRawEndpoint(transProto, p.netProto, wq) + } else { + ep, e = eps.Stack.NewEndpoint(transProto, p.netProto, wq) + } + if e != nil { + return nil, syserr.TranslateNetstackError(e) + } + + return New(t, p.family, stype, wq, ep) +} + +// Pair just returns nil sockets (not supported). +func (*provider) Pair(*kernel.Task, transport.SockType, int) (*fs.File, *fs.File, *syserr.Error) { + return nil, nil, nil +} + +// init registers socket providers for AF_INET and AF_INET6. +func init() { + // Providers backed by netstack. + p := []provider{ + { + family: linux.AF_INET, + netProto: ipv4.ProtocolNumber, + }, + { + family: linux.AF_INET6, + netProto: ipv6.ProtocolNumber, + }, + } + + for i := range p { + socket.RegisterProvider(p[i].family, &p[i]) + } +} diff --git a/pkg/sentry/socket/epsocket/save_restore.go b/pkg/sentry/socket/epsocket/save_restore.go new file mode 100644 index 000000000..feaafb7cc --- /dev/null +++ b/pkg/sentry/socket/epsocket/save_restore.go @@ -0,0 +1,27 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package epsocket + +import ( + "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" +) + +// afterLoad is invoked by stateify. +func (s *Stack) afterLoad() { + s.Stack = stack.StackFromEnv // FIXME(b/36201077) + if s.Stack == nil { + panic("can't restore without netstack/tcpip/stack.Stack") + } +} diff --git a/pkg/sentry/socket/epsocket/stack.go b/pkg/sentry/socket/epsocket/stack.go new file mode 100644 index 000000000..edefa225b --- /dev/null +++ b/pkg/sentry/socket/epsocket/stack.go @@ -0,0 +1,140 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package epsocket + +import ( + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/inet" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4" + "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6" + "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" + "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp" +) + +// Stack implements inet.Stack for netstack/tcpip/stack.Stack. +// +// +stateify savable +type Stack struct { + Stack *stack.Stack `state:"manual"` +} + +// SupportsIPv6 implements Stack.SupportsIPv6. +func (s *Stack) SupportsIPv6() bool { + return s.Stack.CheckNetworkProtocol(ipv6.ProtocolNumber) +} + +// Interfaces implements inet.Stack.Interfaces. +func (s *Stack) Interfaces() map[int32]inet.Interface { + is := make(map[int32]inet.Interface) + for id, ni := range s.Stack.NICInfo() { + var devType uint16 + if ni.Flags.Loopback { + devType = linux.ARPHRD_LOOPBACK + } + is[int32(id)] = inet.Interface{ + Name: ni.Name, + Addr: []byte(ni.LinkAddress), + Flags: uint32(nicStateFlagsToLinux(ni.Flags)), + DeviceType: devType, + MTU: ni.MTU, + } + } + return is +} + +// InterfaceAddrs implements inet.Stack.InterfaceAddrs. +func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { + nicAddrs := make(map[int32][]inet.InterfaceAddr) + for id, ni := range s.Stack.NICInfo() { + var addrs []inet.InterfaceAddr + for _, a := range ni.ProtocolAddresses { + var family uint8 + switch a.Protocol { + case ipv4.ProtocolNumber: + family = linux.AF_INET + case ipv6.ProtocolNumber: + family = linux.AF_INET6 + default: + log.Warningf("Unknown network protocol in %+v", a) + continue + } + + addrs = append(addrs, inet.InterfaceAddr{ + Family: family, + PrefixLen: uint8(len(a.Address) * 8), + Addr: []byte(a.Address), + // TODO(b/68878065): Other fields. + }) + } + nicAddrs[int32(id)] = addrs + } + return nicAddrs +} + +// TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize. +func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) { + var rs tcp.ReceiveBufferSizeOption + err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &rs) + return inet.TCPBufferSize{ + Min: rs.Min, + Default: rs.Default, + Max: rs.Max, + }, syserr.TranslateNetstackError(err).ToError() +} + +// SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize. +func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error { + rs := tcp.ReceiveBufferSizeOption{ + Min: size.Min, + Default: size.Default, + Max: size.Max, + } + return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, rs)).ToError() +} + +// TCPSendBufferSize implements inet.Stack.TCPSendBufferSize. +func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) { + var ss tcp.SendBufferSizeOption + err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &ss) + return inet.TCPBufferSize{ + Min: ss.Min, + Default: ss.Default, + Max: ss.Max, + }, syserr.TranslateNetstackError(err).ToError() +} + +// SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize. +func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error { + ss := tcp.SendBufferSizeOption{ + Min: size.Min, + Default: size.Default, + Max: size.Max, + } + return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, ss)).ToError() +} + +// TCPSACKEnabled implements inet.Stack.TCPSACKEnabled. +func (s *Stack) TCPSACKEnabled() (bool, error) { + var sack tcp.SACKEnabled + err := s.Stack.TransportProtocolOption(tcp.ProtocolNumber, &sack) + return bool(sack), syserr.TranslateNetstackError(err).ToError() +} + +// SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled. +func (s *Stack) SetTCPSACKEnabled(enabled bool) error { + return syserr.TranslateNetstackError(s.Stack.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.SACKEnabled(enabled))).ToError() +} diff --git a/pkg/sentry/socket/hostinet/device.go b/pkg/sentry/socket/hostinet/device.go new file mode 100644 index 000000000..4267e3691 --- /dev/null +++ b/pkg/sentry/socket/hostinet/device.go @@ -0,0 +1,19 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostinet + +import "gvisor.googlesource.com/gvisor/pkg/sentry/device" + +var socketDevice = device.NewAnonDevice() diff --git a/pkg/sentry/socket/hostinet/hostinet.go b/pkg/sentry/socket/hostinet/hostinet.go new file mode 100644 index 000000000..0d6f51d2b --- /dev/null +++ b/pkg/sentry/socket/hostinet/hostinet.go @@ -0,0 +1,17 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hostinet implements AF_INET and AF_INET6 sockets using the host's +// network stack. +package hostinet diff --git a/pkg/sentry/socket/hostinet/hostinet_state_autogen.go b/pkg/sentry/socket/hostinet/hostinet_state_autogen.go new file mode 100755 index 000000000..0a5c7cdf3 --- /dev/null +++ b/pkg/sentry/socket/hostinet/hostinet_state_autogen.go @@ -0,0 +1,4 @@ +// automatically generated by stateify. + +package hostinet + diff --git a/pkg/sentry/socket/hostinet/save_restore.go b/pkg/sentry/socket/hostinet/save_restore.go new file mode 100644 index 000000000..1dec33897 --- /dev/null +++ b/pkg/sentry/socket/hostinet/save_restore.go @@ -0,0 +1,20 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostinet + +// beforeSave is invoked by stateify. +func (*socketOperations) beforeSave() { + panic("host.socketOperations is not savable") +} diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go new file mode 100644 index 000000000..41f9693bb --- /dev/null +++ b/pkg/sentry/socket/hostinet/socket.go @@ -0,0 +1,578 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostinet + +import ( + "fmt" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/fdnotifier" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/safemem" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +const ( + sizeofInt32 = 4 + + // sizeofSockaddr is the size in bytes of the largest sockaddr type + // supported by this package. + sizeofSockaddr = syscall.SizeofSockaddrInet6 // sizeof(sockaddr_in6) > sizeof(sockaddr_in) +) + +// socketOperations implements fs.FileOperations and socket.Socket for a socket +// implemented using a host socket. +type socketOperations struct { + fsutil.FilePipeSeek `state:"nosave"` + fsutil.FileNotDirReaddir `state:"nosave"` + fsutil.FileNoFsync `state:"nosave"` + fsutil.FileNoMMap `state:"nosave"` + fsutil.FileNoSplice `state:"nosave"` + fsutil.FileNoopFlush `state:"nosave"` + fsutil.FileUseInodeUnstableAttr `state:"nosave"` + socket.SendReceiveTimeout + + family int // Read-only. + fd int // must be O_NONBLOCK + queue waiter.Queue +} + +var _ = socket.Socket(&socketOperations{}) + +func newSocketFile(ctx context.Context, family int, fd int, nonblock bool) (*fs.File, *syserr.Error) { + s := &socketOperations{family: family, fd: fd} + if err := fdnotifier.AddFD(int32(fd), &s.queue); err != nil { + return nil, syserr.FromError(err) + } + dirent := socket.NewDirent(ctx, socketDevice) + defer dirent.DecRef() + return fs.NewFile(ctx, dirent, fs.FileFlags{NonBlocking: nonblock, Read: true, Write: true}, s), nil +} + +// Release implements fs.FileOperations.Release. +func (s *socketOperations) Release() { + fdnotifier.RemoveFD(int32(s.fd)) + syscall.Close(s.fd) +} + +// Readiness implements waiter.Waitable.Readiness. +func (s *socketOperations) Readiness(mask waiter.EventMask) waiter.EventMask { + return fdnotifier.NonBlockingPoll(int32(s.fd), mask) +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (s *socketOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { + s.queue.EventRegister(e, mask) + fdnotifier.UpdateFD(int32(s.fd)) +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (s *socketOperations) EventUnregister(e *waiter.Entry) { + s.queue.EventUnregister(e) + fdnotifier.UpdateFD(int32(s.fd)) +} + +// Read implements fs.FileOperations.Read. +func (s *socketOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { + n, err := dst.CopyOutFrom(ctx, safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) { + // Refuse to do anything if any part of dst.Addrs was unusable. + if uint64(dst.NumBytes()) != dsts.NumBytes() { + return 0, nil + } + if dsts.IsEmpty() { + return 0, nil + } + if dsts.NumBlocks() == 1 { + // Skip allocating []syscall.Iovec. + n, err := syscall.Read(s.fd, dsts.Head().ToSlice()) + if err != nil { + return 0, translateIOSyscallError(err) + } + return uint64(n), nil + } + return readv(s.fd, iovecsFromBlockSeq(dsts)) + })) + return int64(n), err +} + +// Write implements fs.FileOperations.Write. +func (s *socketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { + n, err := src.CopyInTo(ctx, safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) { + // Refuse to do anything if any part of src.Addrs was unusable. + if uint64(src.NumBytes()) != srcs.NumBytes() { + return 0, nil + } + if srcs.IsEmpty() { + return 0, nil + } + if srcs.NumBlocks() == 1 { + // Skip allocating []syscall.Iovec. + n, err := syscall.Write(s.fd, srcs.Head().ToSlice()) + if err != nil { + return 0, translateIOSyscallError(err) + } + return uint64(n), nil + } + return writev(s.fd, iovecsFromBlockSeq(srcs)) + })) + return int64(n), err +} + +// Connect implements socket.Socket.Connect. +func (s *socketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { + if len(sockaddr) > sizeofSockaddr { + sockaddr = sockaddr[:sizeofSockaddr] + } + + _, _, errno := syscall.Syscall(syscall.SYS_CONNECT, uintptr(s.fd), uintptr(firstBytePtr(sockaddr)), uintptr(len(sockaddr))) + + if errno == 0 { + return nil + } + if errno != syscall.EINPROGRESS || !blocking { + return syserr.FromError(translateIOSyscallError(errno)) + } + + // "EINPROGRESS: The socket is nonblocking and the connection cannot be + // completed immediately. It is possible to select(2) or poll(2) for + // completion by selecting the socket for writing. After select(2) + // indicates writability, use getsockopt(2) to read the SO_ERROR option at + // level SOL-SOCKET to determine whether connect() completed successfully + // (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error + // codes listed here, explaining the reason for the failure)." - connect(2) + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + if s.Readiness(waiter.EventOut)&waiter.EventOut == 0 { + if err := t.Block(ch); err != nil { + return syserr.FromError(err) + } + } + val, err := syscall.GetsockoptInt(s.fd, syscall.SOL_SOCKET, syscall.SO_ERROR) + if err != nil { + return syserr.FromError(err) + } + if val != 0 { + return syserr.FromError(syscall.Errno(uintptr(val))) + } + return nil +} + +// Accept implements socket.Socket.Accept. +func (s *socketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) { + var peerAddr []byte + var peerAddrlen uint32 + var peerAddrPtr *byte + var peerAddrlenPtr *uint32 + if peerRequested { + peerAddr = make([]byte, sizeofSockaddr) + peerAddrlen = uint32(len(peerAddr)) + peerAddrPtr = &peerAddr[0] + peerAddrlenPtr = &peerAddrlen + } + + // Conservatively ignore all flags specified by the application and add + // SOCK_NONBLOCK since socketOperations requires it. + fd, syscallErr := accept4(s.fd, peerAddrPtr, peerAddrlenPtr, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) + if blocking { + var ch chan struct{} + for syscallErr == syserror.ErrWouldBlock { + if ch != nil { + if syscallErr = t.Block(ch); syscallErr != nil { + break + } + } else { + var e waiter.Entry + e, ch = waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + } + fd, syscallErr = accept4(s.fd, peerAddrPtr, peerAddrlenPtr, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) + } + } + + if peerRequested { + peerAddr = peerAddr[:peerAddrlen] + } + if syscallErr != nil { + return 0, peerAddr, peerAddrlen, syserr.FromError(syscallErr) + } + + f, err := newSocketFile(t, s.family, fd, flags&syscall.SOCK_NONBLOCK != 0) + if err != nil { + syscall.Close(fd) + return 0, nil, 0, err + } + defer f.DecRef() + + fdFlags := kernel.FDFlags{ + CloseOnExec: flags&syscall.SOCK_CLOEXEC != 0, + } + kfd, kerr := t.FDMap().NewFDFrom(0, f, fdFlags, t.ThreadGroup().Limits()) + t.Kernel().RecordSocket(f, s.family) + return kfd, peerAddr, peerAddrlen, syserr.FromError(kerr) +} + +// Bind implements socket.Socket.Bind. +func (s *socketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { + if len(sockaddr) > sizeofSockaddr { + sockaddr = sockaddr[:sizeofSockaddr] + } + + _, _, errno := syscall.Syscall(syscall.SYS_BIND, uintptr(s.fd), uintptr(firstBytePtr(sockaddr)), uintptr(len(sockaddr))) + if errno != 0 { + return syserr.FromError(errno) + } + return nil +} + +// Listen implements socket.Socket.Listen. +func (s *socketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error { + return syserr.FromError(syscall.Listen(s.fd, backlog)) +} + +// Shutdown implements socket.Socket.Shutdown. +func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { + switch how { + case syscall.SHUT_RD, syscall.SHUT_WR, syscall.SHUT_RDWR: + return syserr.FromError(syscall.Shutdown(s.fd, how)) + default: + return syserr.ErrInvalidArgument + } +} + +// GetSockOpt implements socket.Socket.GetSockOpt. +func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) { + if outLen < 0 { + return nil, syserr.ErrInvalidArgument + } + + // Whitelist options and constrain option length. + var optlen int + switch level { + case syscall.SOL_IPV6: + switch name { + case syscall.IPV6_V6ONLY: + optlen = sizeofInt32 + } + case syscall.SOL_SOCKET: + switch name { + case syscall.SO_ERROR, syscall.SO_KEEPALIVE, syscall.SO_SNDBUF, syscall.SO_RCVBUF, syscall.SO_REUSEADDR, syscall.SO_TYPE: + optlen = sizeofInt32 + case syscall.SO_LINGER: + optlen = syscall.SizeofLinger + } + case syscall.SOL_TCP: + switch name { + case syscall.TCP_NODELAY: + optlen = sizeofInt32 + case syscall.TCP_INFO: + optlen = int(linux.SizeOfTCPInfo) + } + } + if optlen == 0 { + return nil, syserr.ErrProtocolNotAvailable // ENOPROTOOPT + } + if outLen < optlen { + return nil, syserr.ErrInvalidArgument + } + + opt, err := getsockopt(s.fd, level, name, optlen) + if err != nil { + return nil, syserr.FromError(err) + } + return opt, nil +} + +// SetSockOpt implements socket.Socket.SetSockOpt. +func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error { + // Whitelist options and constrain option length. + var optlen int + switch level { + case syscall.SOL_IPV6: + switch name { + case syscall.IPV6_V6ONLY: + optlen = sizeofInt32 + } + case syscall.SOL_SOCKET: + switch name { + case syscall.SO_SNDBUF, syscall.SO_RCVBUF, syscall.SO_REUSEADDR: + optlen = sizeofInt32 + } + case syscall.SOL_TCP: + switch name { + case syscall.TCP_NODELAY: + optlen = sizeofInt32 + } + } + if optlen == 0 { + // Pretend to accept socket options we don't understand. This seems + // dangerous, but it's what netstack does... + return nil + } + if len(opt) < optlen { + return syserr.ErrInvalidArgument + } + opt = opt[:optlen] + + _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(s.fd), uintptr(level), uintptr(name), uintptr(firstBytePtr(opt)), uintptr(len(opt)), 0) + if errno != 0 { + return syserr.FromError(errno) + } + return nil +} + +// RecvMsg implements socket.Socket.RecvMsg. +func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { + // Whitelist flags. + // + // FIXME(jamieliu): We can't support MSG_ERRQUEUE because it uses ancillary + // messages that netstack/tcpip/transport/unix doesn't understand. Kill the + // Socket interface's dependence on netstack. + if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_PEEK|syscall.MSG_TRUNC) != 0 { + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrInvalidArgument + } + + var senderAddr []byte + if senderRequested { + senderAddr = make([]byte, sizeofSockaddr) + } + + var msgFlags int + + recvmsgToBlocks := safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) { + // Refuse to do anything if any part of dst.Addrs was unusable. + if uint64(dst.NumBytes()) != dsts.NumBytes() { + return 0, nil + } + if dsts.IsEmpty() { + return 0, nil + } + + // We always do a non-blocking recv*(). + sysflags := flags | syscall.MSG_DONTWAIT + + if dsts.NumBlocks() == 1 { + // Skip allocating []syscall.Iovec. + return recvfrom(s.fd, dsts.Head().ToSlice(), sysflags, &senderAddr) + } + + iovs := iovecsFromBlockSeq(dsts) + msg := syscall.Msghdr{ + Iov: &iovs[0], + Iovlen: uint64(len(iovs)), + } + if len(senderAddr) != 0 { + msg.Name = &senderAddr[0] + msg.Namelen = uint32(len(senderAddr)) + } + n, err := recvmsg(s.fd, &msg, sysflags) + if err != nil { + return 0, err + } + senderAddr = senderAddr[:msg.Namelen] + msgFlags = int(msg.Flags) + return n, nil + }) + + var ch chan struct{} + n, err := dst.CopyOutFrom(t, recvmsgToBlocks) + if flags&syscall.MSG_DONTWAIT == 0 { + for err == syserror.ErrWouldBlock { + // We only expect blocking to come from the actual syscall, in which + // case it can't have returned any data. + if n != 0 { + panic(fmt.Sprintf("CopyOutFrom: got (%d, %v), wanted (0, %v)", n, err, err)) + } + if ch != nil { + if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + break + } + } else { + var e waiter.Entry + e, ch = waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + } + n, err = dst.CopyOutFrom(t, recvmsgToBlocks) + } + } + + // We don't allow control messages. + msgFlags &^= linux.MSG_CTRUNC + + return int(n), msgFlags, senderAddr, uint32(len(senderAddr)), socket.ControlMessages{}, syserr.FromError(err) +} + +// SendMsg implements socket.Socket.SendMsg. +func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) { + // Whitelist flags. + if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_EOR|syscall.MSG_FASTOPEN|syscall.MSG_MORE|syscall.MSG_NOSIGNAL) != 0 { + return 0, syserr.ErrInvalidArgument + } + + sendmsgFromBlocks := safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) { + // Refuse to do anything if any part of src.Addrs was unusable. + if uint64(src.NumBytes()) != srcs.NumBytes() { + return 0, nil + } + if srcs.IsEmpty() { + return 0, nil + } + + // We always do a non-blocking send*(). + sysflags := flags | syscall.MSG_DONTWAIT + + if srcs.NumBlocks() == 1 { + // Skip allocating []syscall.Iovec. + src := srcs.Head() + n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s.fd), src.Addr(), uintptr(src.Len()), uintptr(sysflags), uintptr(firstBytePtr(to)), uintptr(len(to))) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return uint64(n), nil + } + + iovs := iovecsFromBlockSeq(srcs) + msg := syscall.Msghdr{ + Iov: &iovs[0], + Iovlen: uint64(len(iovs)), + } + if len(to) != 0 { + msg.Name = &to[0] + msg.Namelen = uint32(len(to)) + } + return sendmsg(s.fd, &msg, sysflags) + }) + + var ch chan struct{} + n, err := src.CopyInTo(t, sendmsgFromBlocks) + if flags&syscall.MSG_DONTWAIT == 0 { + for err == syserror.ErrWouldBlock { + // We only expect blocking to come from the actual syscall, in which + // case it can't have returned any data. + if n != 0 { + panic(fmt.Sprintf("CopyInTo: got (%d, %v), wanted (0, %v)", n, err, err)) + } + if ch != nil { + if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + err = syserror.ErrWouldBlock + } + break + } + } else { + var e waiter.Entry + e, ch = waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + } + n, err = src.CopyInTo(t, sendmsgFromBlocks) + } + } + + return int(n), syserr.FromError(err) +} + +func iovecsFromBlockSeq(bs safemem.BlockSeq) []syscall.Iovec { + iovs := make([]syscall.Iovec, 0, bs.NumBlocks()) + for ; !bs.IsEmpty(); bs = bs.Tail() { + b := bs.Head() + iovs = append(iovs, syscall.Iovec{ + Base: &b.ToSlice()[0], + Len: uint64(b.Len()), + }) + // We don't need to care about b.NeedSafecopy(), because the host + // kernel will handle such address ranges just fine (by returning + // EFAULT). + } + return iovs +} + +func translateIOSyscallError(err error) error { + if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK { + return syserror.ErrWouldBlock + } + return err +} + +type socketProvider struct { + family int +} + +// Socket implements socket.Provider.Socket. +func (p *socketProvider) Socket(t *kernel.Task, stypeflags transport.SockType, protocol int) (*fs.File, *syserr.Error) { + // Check that we are using the host network stack. + stack := t.NetworkContext() + if stack == nil { + return nil, nil + } + if _, ok := stack.(*Stack); !ok { + return nil, nil + } + + // Only accept TCP and UDP. + stype := int(stypeflags) & linux.SOCK_TYPE_MASK + switch stype { + case syscall.SOCK_STREAM: + switch protocol { + case 0, syscall.IPPROTO_TCP: + // ok + default: + return nil, nil + } + case syscall.SOCK_DGRAM: + switch protocol { + case 0, syscall.IPPROTO_UDP: + // ok + default: + return nil, nil + } + default: + return nil, nil + } + + // Conservatively ignore all flags specified by the application and add + // SOCK_NONBLOCK since socketOperations requires it. Pass a protocol of 0 + // to simplify the syscall filters, since 0 and IPPROTO_* are equivalent. + fd, err := syscall.Socket(p.family, stype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return nil, syserr.FromError(err) + } + return newSocketFile(t, p.family, fd, stypeflags&syscall.SOCK_NONBLOCK != 0) +} + +// Pair implements socket.Provider.Pair. +func (p *socketProvider) Pair(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) { + // Not supported by AF_INET/AF_INET6. + return nil, nil, nil +} + +func init() { + for _, family := range []int{syscall.AF_INET, syscall.AF_INET6} { + socket.RegisterProvider(family, &socketProvider{family}) + } +} diff --git a/pkg/sentry/socket/hostinet/socket_unsafe.go b/pkg/sentry/socket/hostinet/socket_unsafe.go new file mode 100644 index 000000000..eed0c7837 --- /dev/null +++ b/pkg/sentry/socket/hostinet/socket_unsafe.go @@ -0,0 +1,138 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostinet + +import ( + "syscall" + "unsafe" + + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +func firstBytePtr(bs []byte) unsafe.Pointer { + if bs == nil { + return nil + } + return unsafe.Pointer(&bs[0]) +} + +// Preconditions: len(dsts) != 0. +func readv(fd int, dsts []syscall.Iovec) (uint64, error) { + n, _, errno := syscall.Syscall(syscall.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&dsts[0])), uintptr(len(dsts))) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return uint64(n), nil +} + +// Preconditions: len(srcs) != 0. +func writev(fd int, srcs []syscall.Iovec) (uint64, error) { + n, _, errno := syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&srcs[0])), uintptr(len(srcs))) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return uint64(n), nil +} + +// Ioctl implements fs.FileOperations.Ioctl. +func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + switch cmd := uintptr(args[1].Int()); cmd { + case syscall.TIOCINQ, syscall.TIOCOUTQ: + var val int32 + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s.fd), cmd, uintptr(unsafe.Pointer(&val))); errno != 0 { + return 0, translateIOSyscallError(errno) + } + var buf [4]byte + usermem.ByteOrder.PutUint32(buf[:], uint32(val)) + _, err := io.CopyOut(ctx, args[2].Pointer(), buf[:], usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err + + default: + return 0, syserror.ENOTTY + } +} + +func accept4(fd int, addr *byte, addrlen *uint32, flags int) (int, error) { + afd, _, errno := syscall.Syscall6(syscall.SYS_ACCEPT4, uintptr(fd), uintptr(unsafe.Pointer(addr)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return int(afd), nil +} + +func getsockopt(fd int, level, name int, optlen int) ([]byte, error) { + opt := make([]byte, optlen) + optlen32 := int32(len(opt)) + _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(firstBytePtr(opt)), uintptr(unsafe.Pointer(&optlen32)), 0) + if errno != 0 { + return nil, errno + } + return opt[:optlen32], nil +} + +// GetSockName implements socket.Socket.GetSockName. +func (s *socketOperations) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr := make([]byte, sizeofSockaddr) + addrlen := uint32(len(addr)) + _, _, errno := syscall.Syscall(syscall.SYS_GETSOCKNAME, uintptr(s.fd), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&addrlen))) + if errno != 0 { + return nil, 0, syserr.FromError(errno) + } + return addr[:addrlen], addrlen, nil +} + +// GetPeerName implements socket.Socket.GetPeerName. +func (s *socketOperations) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr := make([]byte, sizeofSockaddr) + addrlen := uint32(len(addr)) + _, _, errno := syscall.Syscall(syscall.SYS_GETPEERNAME, uintptr(s.fd), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&addrlen))) + if errno != 0 { + return nil, 0, syserr.FromError(errno) + } + return addr[:addrlen], addrlen, nil +} + +func recvfrom(fd int, dst []byte, flags int, from *[]byte) (uint64, error) { + fromLen := uint32(len(*from)) + n, _, errno := syscall.Syscall6(syscall.SYS_RECVFROM, uintptr(fd), uintptr(firstBytePtr(dst)), uintptr(len(dst)), uintptr(flags), uintptr(firstBytePtr(*from)), uintptr(unsafe.Pointer(&fromLen))) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + *from = (*from)[:fromLen] + return uint64(n), nil +} + +func recvmsg(fd int, msg *syscall.Msghdr, flags int) (uint64, error) { + n, _, errno := syscall.Syscall(syscall.SYS_RECVMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return uint64(n), nil +} + +func sendmsg(fd int, msg *syscall.Msghdr, flags int) (uint64, error) { + n, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + if errno != 0 { + return 0, translateIOSyscallError(errno) + } + return uint64(n), nil +} diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go new file mode 100644 index 000000000..9c45991ba --- /dev/null +++ b/pkg/sentry/socket/hostinet/stack.go @@ -0,0 +1,246 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostinet + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/inet" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +var defaultRecvBufSize = inet.TCPBufferSize{ + Min: 4096, + Default: 87380, + Max: 6291456, +} + +var defaultSendBufSize = inet.TCPBufferSize{ + Min: 4096, + Default: 16384, + Max: 4194304, +} + +// Stack implements inet.Stack for host sockets. +type Stack struct { + // Stack is immutable. + interfaces map[int32]inet.Interface + interfaceAddrs map[int32][]inet.InterfaceAddr + supportsIPv6 bool + tcpRecvBufSize inet.TCPBufferSize + tcpSendBufSize inet.TCPBufferSize + tcpSACKEnabled bool +} + +// NewStack returns an empty Stack containing no configuration. +func NewStack() *Stack { + return &Stack{ + interfaces: make(map[int32]inet.Interface), + interfaceAddrs: make(map[int32][]inet.InterfaceAddr), + } +} + +// Configure sets up the stack using the current state of the host network. +func (s *Stack) Configure() error { + if err := addHostInterfaces(s); err != nil { + return err + } + + if _, err := os.Stat("/proc/net/if_inet6"); err == nil { + s.supportsIPv6 = true + } + + s.tcpRecvBufSize = defaultRecvBufSize + if tcpRMem, err := readTCPBufferSizeFile("/proc/sys/net/ipv4/tcp_rmem"); err == nil { + s.tcpRecvBufSize = tcpRMem + } else { + log.Warningf("Failed to read TCP receive buffer size, using default values") + } + + s.tcpSendBufSize = defaultSendBufSize + if tcpWMem, err := readTCPBufferSizeFile("/proc/sys/net/ipv4/tcp_wmem"); err == nil { + s.tcpSendBufSize = tcpWMem + } else { + log.Warningf("Failed to read TCP send buffer size, using default values") + } + + // SACK is important for performance and even compatibility, assume it's + // enabled if we can't find the actual value. + s.tcpSACKEnabled = true + if sack, err := ioutil.ReadFile("/proc/sys/net/ipv4/tcp_sack"); err == nil { + s.tcpSACKEnabled = strings.TrimSpace(string(sack)) != "0" + } else { + log.Warningf("Failed to read if TCP SACK if enabled, setting to true") + } + + return nil +} + +// ExtractHostInterfaces will populate an interface map and +// interfaceAddrs map with the results of the equivalent +// netlink messages. +func ExtractHostInterfaces(links []syscall.NetlinkMessage, addrs []syscall.NetlinkMessage, interfaces map[int32]inet.Interface, interfaceAddrs map[int32][]inet.InterfaceAddr) error { + for _, link := range links { + if link.Header.Type != syscall.RTM_NEWLINK { + continue + } + if len(link.Data) < syscall.SizeofIfInfomsg { + return fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid data length (%d bytes, expected at least %d bytes)", len(link.Data), syscall.SizeofIfInfomsg) + } + var ifinfo syscall.IfInfomsg + binary.Unmarshal(link.Data[:syscall.SizeofIfInfomsg], usermem.ByteOrder, &ifinfo) + inetIF := inet.Interface{ + DeviceType: ifinfo.Type, + Flags: ifinfo.Flags, + } + // Not clearly documented: syscall.ParseNetlinkRouteAttr will check the + // syscall.NetlinkMessage.Header.Type and skip the struct ifinfomsg + // accordingly. + attrs, err := syscall.ParseNetlinkRouteAttr(&link) + if err != nil { + return fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid rtattrs: %v", err) + } + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.IFLA_ADDRESS: + inetIF.Addr = attr.Value + case syscall.IFLA_IFNAME: + inetIF.Name = string(attr.Value[:len(attr.Value)-1]) + } + } + interfaces[ifinfo.Index] = inetIF + } + + for _, addr := range addrs { + if addr.Header.Type != syscall.RTM_NEWADDR { + continue + } + if len(addr.Data) < syscall.SizeofIfAddrmsg { + return fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid data length (%d bytes, expected at least %d bytes)", len(addr.Data), syscall.SizeofIfAddrmsg) + } + var ifaddr syscall.IfAddrmsg + binary.Unmarshal(addr.Data[:syscall.SizeofIfAddrmsg], usermem.ByteOrder, &ifaddr) + inetAddr := inet.InterfaceAddr{ + Family: ifaddr.Family, + PrefixLen: ifaddr.Prefixlen, + Flags: ifaddr.Flags, + } + attrs, err := syscall.ParseNetlinkRouteAttr(&addr) + if err != nil { + return fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid rtattrs: %v", err) + } + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.IFA_ADDRESS: + inetAddr.Addr = attr.Value + } + } + interfaceAddrs[int32(ifaddr.Index)] = append(interfaceAddrs[int32(ifaddr.Index)], inetAddr) + } + + return nil +} + +func addHostInterfaces(s *Stack) error { + links, err := doNetlinkRouteRequest(syscall.RTM_GETLINK) + if err != nil { + return fmt.Errorf("RTM_GETLINK failed: %v", err) + } + + addrs, err := doNetlinkRouteRequest(syscall.RTM_GETADDR) + if err != nil { + return fmt.Errorf("RTM_GETADDR failed: %v", err) + } + + return ExtractHostInterfaces(links, addrs, s.interfaces, s.interfaceAddrs) +} + +func doNetlinkRouteRequest(req int) ([]syscall.NetlinkMessage, error) { + data, err := syscall.NetlinkRIB(req, syscall.AF_UNSPEC) + if err != nil { + return nil, err + } + return syscall.ParseNetlinkMessage(data) +} + +func readTCPBufferSizeFile(filename string) (inet.TCPBufferSize, error) { + contents, err := ioutil.ReadFile(filename) + if err != nil { + return inet.TCPBufferSize{}, fmt.Errorf("failed to read %s: %v", filename, err) + } + ioseq := usermem.BytesIOSequence(contents) + fields := make([]int32, 3) + if n, err := usermem.CopyInt32StringsInVec(context.Background(), ioseq.IO, ioseq.Addrs, fields, ioseq.Opts); n != ioseq.NumBytes() || err != nil { + return inet.TCPBufferSize{}, fmt.Errorf("failed to parse %s (%q): got %v after %d/%d bytes", filename, contents, err, n, ioseq.NumBytes()) + } + return inet.TCPBufferSize{ + Min: int(fields[0]), + Default: int(fields[1]), + Max: int(fields[2]), + }, nil +} + +// Interfaces implements inet.Stack.Interfaces. +func (s *Stack) Interfaces() map[int32]inet.Interface { + return s.interfaces +} + +// InterfaceAddrs implements inet.Stack.InterfaceAddrs. +func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { + return s.interfaceAddrs +} + +// SupportsIPv6 implements inet.Stack.SupportsIPv6. +func (s *Stack) SupportsIPv6() bool { + return s.supportsIPv6 +} + +// TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize. +func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) { + return s.tcpRecvBufSize, nil +} + +// SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize. +func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error { + return syserror.EACCES +} + +// TCPSendBufferSize implements inet.Stack.TCPSendBufferSize. +func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) { + return s.tcpSendBufSize, nil +} + +// SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize. +func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error { + return syserror.EACCES +} + +// TCPSACKEnabled implements inet.Stack.TCPSACKEnabled. +func (s *Stack) TCPSACKEnabled() (bool, error) { + return s.tcpSACKEnabled, nil +} + +// SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled. +func (s *Stack) SetTCPSACKEnabled(enabled bool) error { + return syserror.EACCES +} diff --git a/pkg/sentry/socket/netlink/message.go b/pkg/sentry/socket/netlink/message.go new file mode 100644 index 000000000..5bd3b49ce --- /dev/null +++ b/pkg/sentry/socket/netlink/message.go @@ -0,0 +1,159 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package netlink + +import ( + "fmt" + "math" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +// alignUp rounds a length up to an alignment. +// +// Preconditions: align is a power of two. +func alignUp(length int, align uint) int { + return (length + int(align) - 1) &^ (int(align) - 1) +} + +// Message contains a complete serialized netlink message. +type Message struct { + buf []byte +} + +// NewMessage creates a new Message containing the passed header. +// +// The header length will be updated by Finalize. +func NewMessage(hdr linux.NetlinkMessageHeader) *Message { + return &Message{ + buf: binary.Marshal(nil, usermem.ByteOrder, hdr), + } +} + +// Finalize returns the []byte containing the entire message, with the total +// length set in the message header. The Message must not be modified after +// calling Finalize. +func (m *Message) Finalize() []byte { + // Update length, which is the first 4 bytes of the header. + usermem.ByteOrder.PutUint32(m.buf, uint32(len(m.buf))) + + // Align the message. Note that the message length in the header (set + // above) is the useful length of the message, not the total aligned + // length. See net/netlink/af_netlink.c:__nlmsg_put. + aligned := alignUp(len(m.buf), linux.NLMSG_ALIGNTO) + m.putZeros(aligned - len(m.buf)) + return m.buf +} + +// putZeros adds n zeros to the message. +func (m *Message) putZeros(n int) { + for n > 0 { + m.buf = append(m.buf, 0) + n-- + } +} + +// Put serializes v into the message. +func (m *Message) Put(v interface{}) { + m.buf = binary.Marshal(m.buf, usermem.ByteOrder, v) +} + +// PutAttr adds v to the message as a netlink attribute. +// +// Preconditions: The serialized attribute (linux.NetlinkAttrHeaderSize + +// binary.Size(v) fits in math.MaxUint16 bytes. +func (m *Message) PutAttr(atype uint16, v interface{}) { + l := linux.NetlinkAttrHeaderSize + int(binary.Size(v)) + if l > math.MaxUint16 { + panic(fmt.Sprintf("attribute too large: %d", l)) + } + + m.Put(linux.NetlinkAttrHeader{ + Type: atype, + Length: uint16(l), + }) + m.Put(v) + + // Align the attribute. + aligned := alignUp(l, linux.NLA_ALIGNTO) + m.putZeros(aligned - l) +} + +// PutAttrString adds s to the message as a netlink attribute. +func (m *Message) PutAttrString(atype uint16, s string) { + l := linux.NetlinkAttrHeaderSize + len(s) + 1 + m.Put(linux.NetlinkAttrHeader{ + Type: atype, + Length: uint16(l), + }) + + // String + NUL-termination. + m.Put([]byte(s)) + m.putZeros(1) + + // Align the attribute. + aligned := alignUp(l, linux.NLA_ALIGNTO) + m.putZeros(aligned - l) +} + +// MessageSet contains a series of netlink messages. +type MessageSet struct { + // Multi indicates that this a multi-part message, to be terminated by + // NLMSG_DONE. NLMSG_DONE is sent even if the set contains only one + // Message. + // + // If Multi is set, all added messages will have NLM_F_MULTI set. + Multi bool + + // PortID is the destination port for all messages. + PortID int32 + + // Seq is the sequence counter for all messages in the set. + Seq uint32 + + // Messages contains the messages in the set. + Messages []*Message +} + +// NewMessageSet creates a new MessageSet. +// +// portID is the destination port to set as PortID in all messages. +// +// seq is the sequence counter to set as seq in all messages in the set. +func NewMessageSet(portID int32, seq uint32) *MessageSet { + return &MessageSet{ + PortID: portID, + Seq: seq, + } +} + +// AddMessage adds a new message to the set and returns it for further +// additions. +// +// The passed header will have Seq, PortID and the multi flag set +// automatically. +func (ms *MessageSet) AddMessage(hdr linux.NetlinkMessageHeader) *Message { + hdr.Seq = ms.Seq + hdr.PortID = uint32(ms.PortID) + if ms.Multi { + hdr.Flags |= linux.NLM_F_MULTI + } + + m := NewMessage(hdr) + ms.Messages = append(ms.Messages, m) + return m +} diff --git a/pkg/sentry/socket/netlink/netlink_state_autogen.go b/pkg/sentry/socket/netlink/netlink_state_autogen.go new file mode 100755 index 000000000..59d902798 --- /dev/null +++ b/pkg/sentry/socket/netlink/netlink_state_autogen.go @@ -0,0 +1,36 @@ +// automatically generated by stateify. + +package netlink + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *Socket) beforeSave() {} +func (x *Socket) save(m state.Map) { + x.beforeSave() + m.Save("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Save("ports", &x.ports) + m.Save("protocol", &x.protocol) + m.Save("ep", &x.ep) + m.Save("connection", &x.connection) + m.Save("bound", &x.bound) + m.Save("portID", &x.portID) + m.Save("sendBufferSize", &x.sendBufferSize) +} + +func (x *Socket) afterLoad() {} +func (x *Socket) load(m state.Map) { + m.Load("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Load("ports", &x.ports) + m.Load("protocol", &x.protocol) + m.Load("ep", &x.ep) + m.Load("connection", &x.connection) + m.Load("bound", &x.bound) + m.Load("portID", &x.portID) + m.Load("sendBufferSize", &x.sendBufferSize) +} + +func init() { + state.Register("netlink.Socket", (*Socket)(nil), state.Fns{Save: (*Socket).save, Load: (*Socket).load}) +} diff --git a/pkg/sentry/socket/netlink/port/port.go b/pkg/sentry/socket/netlink/port/port.go new file mode 100644 index 000000000..e9d3275b1 --- /dev/null +++ b/pkg/sentry/socket/netlink/port/port.go @@ -0,0 +1,116 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package port provides port ID allocation for netlink sockets. +// +// A netlink port is any int32 value. Positive ports are typically equivalent +// to the PID of the binding process. If that port is unavailable, negative +// ports are searched to find a free port that will not conflict with other +// PIDS. +package port + +import ( + "fmt" + "math" + "math/rand" + "sync" +) + +// maxPorts is a sanity limit on the maximum number of ports to allocate per +// protocol. +const maxPorts = 10000 + +// Manager allocates netlink port IDs. +// +// +stateify savable +type Manager struct { + // mu protects the fields below. + mu sync.Mutex `state:"nosave"` + + // ports contains a map of allocated ports for each protocol. + ports map[int]map[int32]struct{} +} + +// New creates a new Manager. +func New() *Manager { + return &Manager{ + ports: make(map[int]map[int32]struct{}), + } +} + +// Allocate reserves a new port ID for protocol. hint will be taken if +// available. +func (m *Manager) Allocate(protocol int, hint int32) (int32, bool) { + m.mu.Lock() + defer m.mu.Unlock() + + proto, ok := m.ports[protocol] + if !ok { + proto = make(map[int32]struct{}) + // Port 0 is reserved for the kernel. + proto[0] = struct{}{} + m.ports[protocol] = proto + } + + if len(proto) >= maxPorts { + return 0, false + } + + if _, ok := proto[hint]; !ok { + // Hint is available, reserve it. + proto[hint] = struct{}{} + return hint, true + } + + // Search for any free port in [math.MinInt32, -4096). The positive + // port space is left open for pid-based allocations. This behavior is + // consistent with Linux. + start := int32(math.MinInt32 + rand.Int63n(math.MaxInt32-4096+1)) + curr := start + for { + if _, ok := proto[curr]; !ok { + proto[curr] = struct{}{} + return curr, true + } + + curr-- + if curr >= -4096 { + curr = -4097 + } + if curr == start { + // Nothing found. We should always find a free port + // because maxPorts < -4096 - MinInt32. + panic(fmt.Sprintf("No free port found in %+v", proto)) + } + } +} + +// Release frees the specified port for protocol. +// +// Preconditions: port is already allocated. +func (m *Manager) Release(protocol int, port int32) { + m.mu.Lock() + defer m.mu.Unlock() + + proto, ok := m.ports[protocol] + if !ok { + panic(fmt.Sprintf("Released port %d for protocol %d which has no allocations", port, protocol)) + } + + if _, ok := proto[port]; !ok { + panic(fmt.Sprintf("Released port %d for protocol %d is not allocated", port, protocol)) + } + + delete(proto, port) +} diff --git a/pkg/sentry/socket/netlink/port/port_state_autogen.go b/pkg/sentry/socket/netlink/port/port_state_autogen.go new file mode 100755 index 000000000..f01d9704f --- /dev/null +++ b/pkg/sentry/socket/netlink/port/port_state_autogen.go @@ -0,0 +1,22 @@ +// automatically generated by stateify. + +package port + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *Manager) beforeSave() {} +func (x *Manager) save(m state.Map) { + x.beforeSave() + m.Save("ports", &x.ports) +} + +func (x *Manager) afterLoad() {} +func (x *Manager) load(m state.Map) { + m.Load("ports", &x.ports) +} + +func init() { + state.Register("port.Manager", (*Manager)(nil), state.Fns{Save: (*Manager).save, Load: (*Manager).load}) +} diff --git a/pkg/sentry/socket/netlink/provider.go b/pkg/sentry/socket/netlink/provider.go new file mode 100644 index 000000000..76cf12fd4 --- /dev/null +++ b/pkg/sentry/socket/netlink/provider.go @@ -0,0 +1,105 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package netlink + +import ( + "fmt" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/syserr" +) + +// Protocol is the implementation of a netlink socket protocol. +type Protocol interface { + // Protocol returns the Linux netlink protocol value. + Protocol() int + + // ProcessMessage processes a single message from userspace. + // + // If err == nil, any messages added to ms will be sent back to the + // other end of the socket. Setting ms.Multi will cause an NLMSG_DONE + // message to be sent even if ms contains no messages. + ProcessMessage(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *MessageSet) *syserr.Error +} + +// Provider is a function that creates a new Protocol for a specific netlink +// protocol. +// +// Note that this is distinct from socket.Provider, which is used for all +// socket families. +type Provider func(t *kernel.Task) (Protocol, *syserr.Error) + +// protocols holds a map of all known address protocols and their provider. +var protocols = make(map[int]Provider) + +// RegisterProvider registers the provider of a given address protocol so that +// netlink sockets of that type can be created via socket(2). +// +// Preconditions: May only be called before any netlink sockets are created. +func RegisterProvider(protocol int, provider Provider) { + if p, ok := protocols[protocol]; ok { + panic(fmt.Sprintf("Netlink protocol %d already provided by %+v", protocol, p)) + } + + protocols[protocol] = provider +} + +// socketProvider implements socket.Provider. +type socketProvider struct { +} + +// Socket implements socket.Provider.Socket. +func (*socketProvider) Socket(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *syserr.Error) { + // Netlink sockets must be specified as datagram or raw, but they + // behave the same regardless of type. + if stype != transport.SockDgram && stype != transport.SockRaw { + return nil, syserr.ErrSocketNotSupported + } + + provider, ok := protocols[protocol] + if !ok { + return nil, syserr.ErrProtocolNotSupported + } + + p, err := provider(t) + if err != nil { + return nil, err + } + + s, err := NewSocket(t, p) + if err != nil { + return nil, err + } + + d := socket.NewDirent(t, netlinkSocketDevice) + defer d.DecRef() + return fs.NewFile(t, d, fs.FileFlags{Read: true, Write: true}, s), nil +} + +// Pair implements socket.Provider.Pair by returning an error. +func (*socketProvider) Pair(*kernel.Task, transport.SockType, int) (*fs.File, *fs.File, *syserr.Error) { + // Netlink sockets never supports creating socket pairs. + return nil, nil, syserr.ErrNotSupported +} + +// init registers the socket provider. +func init() { + socket.RegisterProvider(linux.AF_NETLINK, &socketProvider{}) +} diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go new file mode 100644 index 000000000..9f0a81403 --- /dev/null +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -0,0 +1,197 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package route provides a NETLINK_ROUTE socket protocol. +package route + +import ( + "bytes" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/inet" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink" + "gvisor.googlesource.com/gvisor/pkg/syserr" +) + +// commandKind describes the operational class of a message type. +// +// The route message types use the lower 2 bits of the type to describe class +// of command. +type commandKind int + +const ( + kindNew commandKind = 0x0 + kindDel = 0x1 + kindGet = 0x2 + kindSet = 0x3 +) + +func typeKind(typ uint16) commandKind { + return commandKind(typ & 0x3) +} + +// Protocol implements netlink.Protocol. +// +// +stateify savable +type Protocol struct{} + +var _ netlink.Protocol = (*Protocol)(nil) + +// NewProtocol creates a NETLINK_ROUTE netlink.Protocol. +func NewProtocol(t *kernel.Task) (netlink.Protocol, *syserr.Error) { + return &Protocol{}, nil +} + +// Protocol implements netlink.Protocol.Protocol. +func (p *Protocol) Protocol() int { + return linux.NETLINK_ROUTE +} + +// dumpLinks handles RTM_GETLINK + NLM_F_DUMP requests. +func (p *Protocol) dumpLinks(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *netlink.MessageSet) *syserr.Error { + // NLM_F_DUMP + RTM_GETLINK messages are supposed to include an + // ifinfomsg. However, Linux <3.9 only checked for rtgenmsg, and some + // userspace applications (including glibc) still include rtgenmsg. + // Linux has a workaround based on the total message length. + // + // We don't bother to check for either, since we don't support any + // extra attributes that may be included anyways. + // + // The message may also contain netlink attribute IFLA_EXT_MASK, which + // we don't support. + + // The RTM_GETLINK dump response is a set of messages each containing + // an InterfaceInfoMessage followed by a set of netlink attributes. + + // We always send back an NLMSG_DONE. + ms.Multi = true + + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network devices. + return nil + } + + for id, i := range stack.Interfaces() { + m := ms.AddMessage(linux.NetlinkMessageHeader{ + Type: linux.RTM_NEWLINK, + }) + + m.Put(linux.InterfaceInfoMessage{ + Family: linux.AF_UNSPEC, + Type: i.DeviceType, + Index: id, + Flags: i.Flags, + }) + + m.PutAttrString(linux.IFLA_IFNAME, i.Name) + m.PutAttr(linux.IFLA_MTU, i.MTU) + + mac := make([]byte, 6) + brd := mac + if len(i.Addr) > 0 { + mac = i.Addr + brd = bytes.Repeat([]byte{0xff}, len(i.Addr)) + } + m.PutAttr(linux.IFLA_ADDRESS, mac) + m.PutAttr(linux.IFLA_BROADCAST, brd) + + // TODO(b/68878065): There are many more attributes. + } + + return nil +} + +// dumpAddrs handles RTM_GETADDR + NLM_F_DUMP requests. +func (p *Protocol) dumpAddrs(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *netlink.MessageSet) *syserr.Error { + // RTM_GETADDR dump requests need not contain anything more than the + // netlink header and 1 byte protocol family common to all + // NETLINK_ROUTE requests. + // + // TODO(b/68878065): Filter output by passed protocol family. + + // The RTM_GETADDR dump response is a set of RTM_NEWADDR messages each + // containing an InterfaceAddrMessage followed by a set of netlink + // attributes. + + // We always send back an NLMSG_DONE. + ms.Multi = true + + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network devices. + return nil + } + + for id, as := range stack.InterfaceAddrs() { + for _, a := range as { + m := ms.AddMessage(linux.NetlinkMessageHeader{ + Type: linux.RTM_NEWADDR, + }) + + m.Put(linux.InterfaceAddrMessage{ + Family: a.Family, + PrefixLen: a.PrefixLen, + Index: uint32(id), + }) + + m.PutAttr(linux.IFA_ADDRESS, []byte(a.Addr)) + + // TODO(b/68878065): There are many more attributes. + } + } + + return nil +} + +// ProcessMessage implements netlink.Protocol.ProcessMessage. +func (p *Protocol) ProcessMessage(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *netlink.MessageSet) *syserr.Error { + // All messages start with a 1 byte protocol family. + if len(data) < 1 { + // Linux ignores messages missing the protocol family. See + // net/core/rtnetlink.c:rtnetlink_rcv_msg. + return nil + } + + // Non-GET message types require CAP_NET_ADMIN. + if typeKind(hdr.Type) != kindGet { + creds := auth.CredentialsFromContext(ctx) + if !creds.HasCapability(linux.CAP_NET_ADMIN) { + return syserr.ErrPermissionDenied + } + } + + // TODO(b/68878065): Only the dump variant of the types below are + // supported. + if hdr.Flags&linux.NLM_F_DUMP != linux.NLM_F_DUMP { + return syserr.ErrNotSupported + } + + switch hdr.Type { + case linux.RTM_GETLINK: + return p.dumpLinks(ctx, hdr, data, ms) + case linux.RTM_GETADDR: + return p.dumpAddrs(ctx, hdr, data, ms) + default: + return syserr.ErrNotSupported + } +} + +// init registers the NETLINK_ROUTE provider. +func init() { + netlink.RegisterProvider(linux.NETLINK_ROUTE, NewProtocol) +} diff --git a/pkg/sentry/socket/netlink/route/route_state_autogen.go b/pkg/sentry/socket/netlink/route/route_state_autogen.go new file mode 100755 index 000000000..8431bb3d5 --- /dev/null +++ b/pkg/sentry/socket/netlink/route/route_state_autogen.go @@ -0,0 +1,20 @@ +// automatically generated by stateify. + +package route + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *Protocol) beforeSave() {} +func (x *Protocol) save(m state.Map) { + x.beforeSave() +} + +func (x *Protocol) afterLoad() {} +func (x *Protocol) load(m state.Map) { +} + +func init() { + state.Register("route.Protocol", (*Protocol)(nil), state.Fns{Save: (*Protocol).save, Load: (*Protocol).load}) +} diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go new file mode 100644 index 000000000..afd06ca33 --- /dev/null +++ b/pkg/sentry/socket/netlink/socket.go @@ -0,0 +1,618 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package netlink provides core functionality for netlink sockets. +package netlink + +import ( + "math" + "sync" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/device" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink/port" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +const sizeOfInt32 int = 4 + +const ( + // minBufferSize is the smallest size of a send buffer. + minSendBufferSize = 4 << 10 // 4096 bytes. + + // defaultSendBufferSize is the default size for the send buffer. + defaultSendBufferSize = 16 * 1024 + + // maxBufferSize is the largest size a send buffer can grow to. + maxSendBufferSize = 4 << 20 // 4MB +) + +// netlinkSocketDevice is the netlink socket virtual device. +var netlinkSocketDevice = device.NewAnonDevice() + +// Socket is the base socket type for netlink sockets. +// +// This implementation only supports userspace sending and receiving messages +// to/from the kernel. +// +// Socket implements socket.Socket. +// +// +stateify savable +type Socket struct { + fsutil.FilePipeSeek `state:"nosave"` + fsutil.FileNotDirReaddir `state:"nosave"` + fsutil.FileNoFsync `state:"nosave"` + fsutil.FileNoMMap `state:"nosave"` + fsutil.FileNoSplice `state:"nosave"` + fsutil.FileNoopFlush `state:"nosave"` + fsutil.FileUseInodeUnstableAttr `state:"nosave"` + socket.SendReceiveTimeout + + // ports provides netlink port allocation. + ports *port.Manager + + // protocol is the netlink protocol implementation. + protocol Protocol + + // ep is a datagram unix endpoint used to buffer messages sent from the + // kernel to userspace. RecvMsg reads messages from this endpoint. + ep transport.Endpoint + + // connection is the kernel's connection to ep, used to write messages + // sent to userspace. + connection transport.ConnectedEndpoint + + // mu protects the fields below. + mu sync.Mutex `state:"nosave"` + + // bound indicates that portid is valid. + bound bool + + // portID is the port ID allocated for this socket. + portID int32 + + // sendBufferSize is the send buffer "size". We don't actually have a + // fixed buffer but only consume this many bytes. + sendBufferSize uint32 +} + +var _ socket.Socket = (*Socket)(nil) + +// NewSocket creates a new Socket. +func NewSocket(t *kernel.Task, protocol Protocol) (*Socket, *syserr.Error) { + // Datagram endpoint used to buffer kernel -> user messages. + ep := transport.NewConnectionless() + + // Bind the endpoint for good measure so we can connect to it. The + // bound address will never be exposed. + if err := ep.Bind(tcpip.FullAddress{Addr: "dummy"}, nil); err != nil { + ep.Close() + return nil, err + } + + // Create a connection from which the kernel can write messages. + connection, err := ep.(transport.BoundEndpoint).UnidirectionalConnect() + if err != nil { + ep.Close() + return nil, err + } + + return &Socket{ + ports: t.Kernel().NetlinkPorts(), + protocol: protocol, + ep: ep, + connection: connection, + sendBufferSize: defaultSendBufferSize, + }, nil +} + +// Release implements fs.FileOperations.Release. +func (s *Socket) Release() { + s.connection.Release() + s.ep.Close() + + if s.bound { + s.ports.Release(s.protocol.Protocol(), s.portID) + } +} + +// Readiness implements waiter.Waitable.Readiness. +func (s *Socket) Readiness(mask waiter.EventMask) waiter.EventMask { + // ep holds messages to be read and thus handles EventIn readiness. + ready := s.ep.Readiness(mask) + + if mask&waiter.EventOut == waiter.EventOut { + // sendMsg handles messages synchronously and is thus always + // ready for writing. + ready |= waiter.EventOut + } + + return ready +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (s *Socket) EventRegister(e *waiter.Entry, mask waiter.EventMask) { + s.ep.EventRegister(e, mask) + // Writable readiness never changes, so no registration is needed. +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (s *Socket) EventUnregister(e *waiter.Entry) { + s.ep.EventUnregister(e) +} + +// Ioctl implements fs.FileOperations.Ioctl. +func (s *Socket) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + // TODO(b/68878065): no ioctls supported. + return 0, syserror.ENOTTY +} + +// ExtractSockAddr extracts the SockAddrNetlink from b. +func ExtractSockAddr(b []byte) (*linux.SockAddrNetlink, *syserr.Error) { + if len(b) < linux.SockAddrNetlinkSize { + return nil, syserr.ErrBadAddress + } + + var sa linux.SockAddrNetlink + binary.Unmarshal(b[:linux.SockAddrNetlinkSize], usermem.ByteOrder, &sa) + + if sa.Family != linux.AF_NETLINK { + return nil, syserr.ErrInvalidArgument + } + + return &sa, nil +} + +// bindPort binds this socket to a port, preferring 'port' if it is available. +// +// port of 0 defaults to the ThreadGroup ID. +// +// Preconditions: mu is held. +func (s *Socket) bindPort(t *kernel.Task, port int32) *syserr.Error { + if s.bound { + // Re-binding is only allowed if the port doesn't change. + if port != s.portID { + return syserr.ErrInvalidArgument + } + + return nil + } + + if port == 0 { + port = int32(t.ThreadGroup().ID()) + } + port, ok := s.ports.Allocate(s.protocol.Protocol(), port) + if !ok { + return syserr.ErrBusy + } + + s.portID = port + s.bound = true + return nil +} + +// Bind implements socket.Socket.Bind. +func (s *Socket) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { + a, err := ExtractSockAddr(sockaddr) + if err != nil { + return err + } + + // No support for multicast groups yet. + if a.Groups != 0 { + return syserr.ErrPermissionDenied + } + + s.mu.Lock() + defer s.mu.Unlock() + + return s.bindPort(t, int32(a.PortID)) +} + +// Connect implements socket.Socket.Connect. +func (s *Socket) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { + a, err := ExtractSockAddr(sockaddr) + if err != nil { + return err + } + + // No support for multicast groups yet. + if a.Groups != 0 { + return syserr.ErrPermissionDenied + } + + s.mu.Lock() + defer s.mu.Unlock() + + if a.PortID == 0 { + // Netlink sockets default to connected to the kernel, but + // connecting anyways automatically binds if not already bound. + if !s.bound { + // Pass port 0 to get an auto-selected port ID. + return s.bindPort(t, 0) + } + return nil + } + + // We don't support non-kernel destination ports. Linux returns EPERM + // if applications attempt to do this without NL_CFG_F_NONROOT_SEND, so + // we emulate that. + return syserr.ErrPermissionDenied +} + +// Accept implements socket.Socket.Accept. +func (s *Socket) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) { + // Netlink sockets never support accept. + return 0, nil, 0, syserr.ErrNotSupported +} + +// Listen implements socket.Socket.Listen. +func (s *Socket) Listen(t *kernel.Task, backlog int) *syserr.Error { + // Netlink sockets never support listen. + return syserr.ErrNotSupported +} + +// Shutdown implements socket.Socket.Shutdown. +func (s *Socket) Shutdown(t *kernel.Task, how int) *syserr.Error { + // Netlink sockets never support shutdown. + return syserr.ErrNotSupported +} + +// GetSockOpt implements socket.Socket.GetSockOpt. +func (s *Socket) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) { + switch level { + case linux.SOL_SOCKET: + switch name { + case linux.SO_SNDBUF: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + s.mu.Lock() + defer s.mu.Unlock() + return int32(s.sendBufferSize), nil + + case linux.SO_RCVBUF: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + // We don't have limit on receiving size. + return int32(math.MaxInt32), nil + + default: + socket.GetSockOptEmitUnimplementedEvent(t, name) + } + case linux.SOL_NETLINK: + switch name { + case linux.NETLINK_BROADCAST_ERROR, + linux.NETLINK_CAP_ACK, + linux.NETLINK_DUMP_STRICT_CHK, + linux.NETLINK_EXT_ACK, + linux.NETLINK_LIST_MEMBERSHIPS, + linux.NETLINK_NO_ENOBUFS, + linux.NETLINK_PKTINFO: + + t.Kernel().EmitUnimplementedEvent(t) + } + } + // TODO(b/68878065): other sockopts are not supported. + return nil, syserr.ErrProtocolNotAvailable +} + +// SetSockOpt implements socket.Socket.SetSockOpt. +func (s *Socket) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error { + switch level { + case linux.SOL_SOCKET: + switch name { + case linux.SO_SNDBUF: + if len(opt) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + size := usermem.ByteOrder.Uint32(opt) + if size < minSendBufferSize { + size = minSendBufferSize + } else if size > maxSendBufferSize { + size = maxSendBufferSize + } + s.mu.Lock() + s.sendBufferSize = size + s.mu.Unlock() + return nil + case linux.SO_RCVBUF: + if len(opt) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + // We don't have limit on receiving size. So just accept anything as + // valid for compatibility. + return nil + default: + socket.SetSockOptEmitUnimplementedEvent(t, name) + } + + case linux.SOL_NETLINK: + switch name { + case linux.NETLINK_ADD_MEMBERSHIP, + linux.NETLINK_BROADCAST_ERROR, + linux.NETLINK_CAP_ACK, + linux.NETLINK_DROP_MEMBERSHIP, + linux.NETLINK_DUMP_STRICT_CHK, + linux.NETLINK_EXT_ACK, + linux.NETLINK_LISTEN_ALL_NSID, + linux.NETLINK_NO_ENOBUFS, + linux.NETLINK_PKTINFO: + + t.Kernel().EmitUnimplementedEvent(t) + } + + } + // TODO(b/68878065): other sockopts are not supported. + return syserr.ErrProtocolNotAvailable +} + +// GetSockName implements socket.Socket.GetSockName. +func (s *Socket) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + s.mu.Lock() + defer s.mu.Unlock() + + sa := linux.SockAddrNetlink{ + Family: linux.AF_NETLINK, + PortID: uint32(s.portID), + } + return sa, uint32(binary.Size(sa)), nil +} + +// GetPeerName implements socket.Socket.GetPeerName. +func (s *Socket) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + sa := linux.SockAddrNetlink{ + Family: linux.AF_NETLINK, + // TODO(b/68878065): Support non-kernel peers. For now the peer + // must be the kernel. + PortID: 0, + } + return sa, uint32(binary.Size(sa)), nil +} + +// RecvMsg implements socket.Socket.RecvMsg. +func (s *Socket) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { + from := linux.SockAddrNetlink{ + Family: linux.AF_NETLINK, + PortID: 0, + } + fromLen := uint32(binary.Size(from)) + + trunc := flags&linux.MSG_TRUNC != 0 + + r := unix.EndpointReader{ + Endpoint: s.ep, + Peek: flags&linux.MSG_PEEK != 0, + } + + if n, err := dst.CopyOutFrom(t, &r); err != syserror.ErrWouldBlock || flags&linux.MSG_DONTWAIT != 0 { + var mflags int + if n < int64(r.MsgSize) { + mflags |= linux.MSG_TRUNC + } + if trunc { + n = int64(r.MsgSize) + } + return int(n), mflags, from, fromLen, socket.ControlMessages{}, syserr.FromError(err) + } + + // We'll have to block. Register for notification and keep trying to + // receive all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + for { + if n, err := dst.CopyOutFrom(t, &r); err != syserror.ErrWouldBlock { + var mflags int + if n < int64(r.MsgSize) { + mflags |= linux.MSG_TRUNC + } + if trunc { + n = int64(r.MsgSize) + } + return int(n), mflags, from, fromLen, socket.ControlMessages{}, syserr.FromError(err) + } + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain + } + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err) + } + } +} + +// Read implements fs.FileOperations.Read. +func (s *Socket) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { + if dst.NumBytes() == 0 { + return 0, nil + } + return dst.CopyOutFrom(ctx, &unix.EndpointReader{ + Endpoint: s.ep, + }) +} + +// sendResponse sends the response messages in ms back to userspace. +func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error { + // Linux combines multiple netlink messages into a single datagram. + bufs := make([][]byte, 0, len(ms.Messages)) + for _, m := range ms.Messages { + bufs = append(bufs, m.Finalize()) + } + + if len(bufs) > 0 { + // RecvMsg never receives the address, so we don't need to send + // one. + _, notify, err := s.connection.Send(bufs, transport.ControlMessages{}, tcpip.FullAddress{}) + // If the buffer is full, we simply drop messages, just like + // Linux. + if err != nil && err != syserr.ErrWouldBlock { + return err + } + if notify { + s.connection.SendNotify() + } + } + + // N.B. multi-part messages should still send NLMSG_DONE even if + // MessageSet contains no messages. + // + // N.B. NLMSG_DONE is always sent in a different datagram. See + // net/netlink/af_netlink.c:netlink_dump. + if ms.Multi { + m := NewMessage(linux.NetlinkMessageHeader{ + Type: linux.NLMSG_DONE, + Flags: linux.NLM_F_MULTI, + Seq: ms.Seq, + PortID: uint32(ms.PortID), + }) + + _, notify, err := s.connection.Send([][]byte{m.Finalize()}, transport.ControlMessages{}, tcpip.FullAddress{}) + if err != nil && err != syserr.ErrWouldBlock { + return err + } + if notify { + s.connection.SendNotify() + } + } + + return nil +} + +// processMessages handles each message in buf, passing it to the protocol +// handler for final handling. +func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error { + for len(buf) > 0 { + if len(buf) < linux.NetlinkMessageHeaderSize { + // Linux ignores messages that are too short. See + // net/netlink/af_netlink.c:netlink_rcv_skb. + break + } + + var hdr linux.NetlinkMessageHeader + binary.Unmarshal(buf[:linux.NetlinkMessageHeaderSize], usermem.ByteOrder, &hdr) + + if hdr.Length < linux.NetlinkMessageHeaderSize || uint64(hdr.Length) > uint64(len(buf)) { + // Linux ignores malformed messages. See + // net/netlink/af_netlink.c:netlink_rcv_skb. + break + } + + // Data from this message. + data := buf[linux.NetlinkMessageHeaderSize:hdr.Length] + + // Advance to the next message. + next := alignUp(int(hdr.Length), linux.NLMSG_ALIGNTO) + if next >= len(buf)-1 { + next = len(buf) - 1 + } + buf = buf[next:] + + // Ignore control messages. + if hdr.Type < linux.NLMSG_MIN_TYPE { + continue + } + + // TODO(b/68877377): ACKs not supported yet. + if hdr.Flags&linux.NLM_F_ACK == linux.NLM_F_ACK { + return syserr.ErrNotSupported + } + + ms := NewMessageSet(s.portID, hdr.Seq) + if err := s.protocol.ProcessMessage(ctx, hdr, data, ms); err != nil { + return err + } + + if err := s.sendResponse(ctx, ms); err != nil { + return err + } + } + + return nil +} + +// sendMsg is the core of message send, used for SendMsg and Write. +func (s *Socket) sendMsg(ctx context.Context, src usermem.IOSequence, to []byte, flags int, controlMessages socket.ControlMessages) (int, *syserr.Error) { + dstPort := int32(0) + + if len(to) != 0 { + a, err := ExtractSockAddr(to) + if err != nil { + return 0, err + } + + // No support for multicast groups yet. + if a.Groups != 0 { + return 0, syserr.ErrPermissionDenied + } + + dstPort = int32(a.PortID) + } + + if dstPort != 0 { + // Non-kernel destinations not supported yet. Treat as if + // NL_CFG_F_NONROOT_SEND is not set. + return 0, syserr.ErrPermissionDenied + } + + s.mu.Lock() + defer s.mu.Unlock() + + // For simplicity, and consistency with Linux, we copy in the entire + // message up front. + if src.NumBytes() > int64(s.sendBufferSize) { + return 0, syserr.ErrMessageTooLong + } + + buf := make([]byte, src.NumBytes()) + n, err := src.CopyIn(ctx, buf) + if err != nil { + // Don't partially consume messages. + return 0, syserr.FromError(err) + } + + if err := s.processMessages(ctx, buf); err != nil { + return 0, err + } + + return n, nil +} + +// SendMsg implements socket.Socket.SendMsg. +func (s *Socket) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) { + return s.sendMsg(t, src, to, flags, controlMessages) +} + +// Write implements fs.FileOperations.Write. +func (s *Socket) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { + n, err := s.sendMsg(ctx, src, nil, 0, socket.ControlMessages{}) + return int64(n), err.ToError() +} diff --git a/pkg/sentry/socket/rpcinet/conn/conn.go b/pkg/sentry/socket/rpcinet/conn/conn.go new file mode 100644 index 000000000..f537c7f63 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/conn/conn.go @@ -0,0 +1,187 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package conn is an RPC connection to a syscall RPC server. +package conn + +import ( + "fmt" + "sync" + "sync/atomic" + "syscall" + + "github.com/golang/protobuf/proto" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/unet" + + pb "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto" +) + +type request struct { + response []byte + ready chan struct{} + ignoreResult bool +} + +// RPCConnection represents a single RPC connection to a syscall gofer. +type RPCConnection struct { + // reqID is the ID of the last request and must be accessed atomically. + reqID uint64 + + sendMu sync.Mutex + socket *unet.Socket + + reqMu sync.Mutex + requests map[uint64]request +} + +// NewRPCConnection initializes a RPC connection to a socket gofer. +func NewRPCConnection(s *unet.Socket) *RPCConnection { + conn := &RPCConnection{socket: s, requests: map[uint64]request{}} + go func() { // S/R-FIXME(b/77962828) + var nums [16]byte + for { + for n := 0; n < len(nums); { + nn, err := conn.socket.Read(nums[n:]) + if err != nil { + panic(fmt.Sprint("error reading length from socket rpc gofer: ", err)) + } + n += nn + } + + b := make([]byte, binary.LittleEndian.Uint64(nums[:8])) + id := binary.LittleEndian.Uint64(nums[8:]) + + for n := 0; n < len(b); { + nn, err := conn.socket.Read(b[n:]) + if err != nil { + panic(fmt.Sprint("error reading request from socket rpc gofer: ", err)) + } + n += nn + } + + conn.reqMu.Lock() + r := conn.requests[id] + if r.ignoreResult { + delete(conn.requests, id) + } else { + r.response = b + conn.requests[id] = r + } + conn.reqMu.Unlock() + close(r.ready) + } + }() + return conn +} + +// NewRequest makes a request to the RPC gofer and returns the request ID and a +// channel which will be closed once the request completes. +func (c *RPCConnection) NewRequest(req pb.SyscallRequest, ignoreResult bool) (uint64, chan struct{}) { + b, err := proto.Marshal(&req) + if err != nil { + panic(fmt.Sprint("invalid proto: ", err)) + } + + id := atomic.AddUint64(&c.reqID, 1) + ch := make(chan struct{}) + + c.reqMu.Lock() + c.requests[id] = request{ready: ch, ignoreResult: ignoreResult} + c.reqMu.Unlock() + + c.sendMu.Lock() + defer c.sendMu.Unlock() + + var nums [16]byte + binary.LittleEndian.PutUint64(nums[:8], uint64(len(b))) + binary.LittleEndian.PutUint64(nums[8:], id) + for n := 0; n < len(nums); { + nn, err := c.socket.Write(nums[n:]) + if err != nil { + panic(fmt.Sprint("error writing length and ID to socket gofer: ", err)) + } + n += nn + } + + for n := 0; n < len(b); { + nn, err := c.socket.Write(b[n:]) + if err != nil { + panic(fmt.Sprint("error writing request to socket gofer: ", err)) + } + n += nn + } + + return id, ch +} + +// RPCReadFile will execute the ReadFile helper RPC method which avoids the +// common pattern of open(2), read(2), close(2) by doing all three operations +// as a single RPC. It will read the entire file or return EFBIG if the file +// was too large. +func (c *RPCConnection) RPCReadFile(path string) ([]byte, *syserr.Error) { + req := &pb.SyscallRequest_ReadFile{&pb.ReadFileRequest{ + Path: path, + }} + + id, ch := c.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-ch + + res := c.Request(id).Result.(*pb.SyscallResponse_ReadFile).ReadFile.Result + if e, ok := res.(*pb.ReadFileResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.ReadFileResponse_Data).Data, nil +} + +// RPCWriteFile will execute the WriteFile helper RPC method which avoids the +// common pattern of open(2), write(2), write(2), close(2) by doing all +// operations as a single RPC. +func (c *RPCConnection) RPCWriteFile(path string, data []byte) (int64, *syserr.Error) { + req := &pb.SyscallRequest_WriteFile{&pb.WriteFileRequest{ + Path: path, + Content: data, + }} + + id, ch := c.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-ch + + res := c.Request(id).Result.(*pb.SyscallResponse_WriteFile).WriteFile + if e := res.ErrorNumber; e != 0 { + return int64(res.Written), syserr.FromHost(syscall.Errno(e)) + } + + return int64(res.Written), nil +} + +// Request retrieves the request corresponding to the given request ID. +// +// The channel returned by NewRequest must have been closed before Request can +// be called. This will happen automatically, do not manually close the +// channel. +func (c *RPCConnection) Request(id uint64) pb.SyscallResponse { + c.reqMu.Lock() + r := c.requests[id] + delete(c.requests, id) + c.reqMu.Unlock() + + var resp pb.SyscallResponse + if err := proto.Unmarshal(r.response, &resp); err != nil { + panic(fmt.Sprint("invalid proto: ", err)) + } + + return resp +} diff --git a/pkg/sentry/socket/rpcinet/conn/conn_state_autogen.go b/pkg/sentry/socket/rpcinet/conn/conn_state_autogen.go new file mode 100755 index 000000000..f6c927a60 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/conn/conn_state_autogen.go @@ -0,0 +1,4 @@ +// automatically generated by stateify. + +package conn + diff --git a/pkg/sentry/socket/rpcinet/device.go b/pkg/sentry/socket/rpcinet/device.go new file mode 100644 index 000000000..44c0a39b7 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/device.go @@ -0,0 +1,19 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpcinet + +import "gvisor.googlesource.com/gvisor/pkg/sentry/device" + +var socketDevice = device.NewAnonDevice() diff --git a/pkg/sentry/socket/rpcinet/notifier/notifier.go b/pkg/sentry/socket/rpcinet/notifier/notifier.go new file mode 100644 index 000000000..601e05994 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/notifier/notifier.go @@ -0,0 +1,230 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package notifier implements an FD notifier implementation over RPC. +package notifier + +import ( + "fmt" + "sync" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/conn" + pb "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +type fdInfo struct { + queue *waiter.Queue + waiting bool +} + +// Notifier holds all the state necessary to issue notifications when IO events +// occur in the observed FDs. +type Notifier struct { + // rpcConn is the connection that is used for sending RPCs. + rpcConn *conn.RPCConnection + + // epFD is the epoll file descriptor used to register for io + // notifications. + epFD uint32 + + // mu protects fdMap. + mu sync.Mutex + + // fdMap maps file descriptors to their notification queues and waiting + // status. + fdMap map[uint32]*fdInfo +} + +// NewRPCNotifier creates a new notifier object. +func NewRPCNotifier(cn *conn.RPCConnection) (*Notifier, error) { + id, c := cn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_EpollCreate1{&pb.EpollCreate1Request{}}}, false /* ignoreResult */) + <-c + + res := cn.Request(id).Result.(*pb.SyscallResponse_EpollCreate1).EpollCreate1.Result + if e, ok := res.(*pb.EpollCreate1Response_ErrorNumber); ok { + return nil, syscall.Errno(e.ErrorNumber) + } + + w := &Notifier{ + rpcConn: cn, + epFD: res.(*pb.EpollCreate1Response_Fd).Fd, + fdMap: make(map[uint32]*fdInfo), + } + + go w.waitAndNotify() // S/R-FIXME(b/77962828) + + return w, nil +} + +// waitFD waits on mask for fd. The fdMap mutex must be hold. +func (n *Notifier) waitFD(fd uint32, fi *fdInfo, mask waiter.EventMask) error { + if !fi.waiting && mask == 0 { + return nil + } + + e := pb.EpollEvent{ + Events: mask.ToLinux() | -syscall.EPOLLET, + Fd: fd, + } + + switch { + case !fi.waiting && mask != 0: + id, c := n.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_EpollCtl{&pb.EpollCtlRequest{Epfd: n.epFD, Op: syscall.EPOLL_CTL_ADD, Fd: fd, Event: &e}}}, false /* ignoreResult */) + <-c + + e := n.rpcConn.Request(id).Result.(*pb.SyscallResponse_EpollCtl).EpollCtl.ErrorNumber + if e != 0 { + return syscall.Errno(e) + } + + fi.waiting = true + case fi.waiting && mask == 0: + id, c := n.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_EpollCtl{&pb.EpollCtlRequest{Epfd: n.epFD, Op: syscall.EPOLL_CTL_DEL, Fd: fd}}}, false /* ignoreResult */) + <-c + n.rpcConn.Request(id) + + fi.waiting = false + case fi.waiting && mask != 0: + id, c := n.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_EpollCtl{&pb.EpollCtlRequest{Epfd: n.epFD, Op: syscall.EPOLL_CTL_MOD, Fd: fd, Event: &e}}}, false /* ignoreResult */) + <-c + + e := n.rpcConn.Request(id).Result.(*pb.SyscallResponse_EpollCtl).EpollCtl.ErrorNumber + if e != 0 { + return syscall.Errno(e) + } + } + + return nil +} + +// addFD adds an FD to the list of FDs observed by n. +func (n *Notifier) addFD(fd uint32, queue *waiter.Queue) { + n.mu.Lock() + defer n.mu.Unlock() + + // Panic if we're already notifying on this FD. + if _, ok := n.fdMap[fd]; ok { + panic(fmt.Sprintf("File descriptor %d added twice", fd)) + } + + // We have nothing to wait for at the moment. Just add it to the map. + n.fdMap[fd] = &fdInfo{queue: queue} +} + +// updateFD updates the set of events the FD needs to be notified on. +func (n *Notifier) updateFD(fd uint32) error { + n.mu.Lock() + defer n.mu.Unlock() + + if fi, ok := n.fdMap[fd]; ok { + return n.waitFD(fd, fi, fi.queue.Events()) + } + + return nil +} + +// RemoveFD removes an FD from the list of FDs observed by n. +func (n *Notifier) removeFD(fd uint32) { + n.mu.Lock() + defer n.mu.Unlock() + + // Remove from map, then from epoll object. + n.waitFD(fd, n.fdMap[fd], 0) + delete(n.fdMap, fd) +} + +// hasFD returns true if the FD is in the list of observed FDs. +func (n *Notifier) hasFD(fd uint32) bool { + n.mu.Lock() + defer n.mu.Unlock() + + _, ok := n.fdMap[fd] + return ok +} + +// waitAndNotify loops waiting for io event notifications from the epoll +// object. Once notifications arrive, they are dispatched to the +// registered queue. +func (n *Notifier) waitAndNotify() error { + for { + id, c := n.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_EpollWait{&pb.EpollWaitRequest{Fd: n.epFD, NumEvents: 100, Msec: -1}}}, false /* ignoreResult */) + <-c + + res := n.rpcConn.Request(id).Result.(*pb.SyscallResponse_EpollWait).EpollWait.Result + if e, ok := res.(*pb.EpollWaitResponse_ErrorNumber); ok { + err := syscall.Errno(e.ErrorNumber) + // NOTE(magi): I don't think epoll_wait can return EAGAIN but I'm being + // conseratively careful here since exiting the notification thread + // would be really bad. + if err == syscall.EINTR || err == syscall.EAGAIN { + continue + } + return err + } + + n.mu.Lock() + for _, e := range res.(*pb.EpollWaitResponse_Events).Events.Events { + if fi, ok := n.fdMap[e.Fd]; ok { + fi.queue.Notify(waiter.EventMaskFromLinux(e.Events)) + } + } + n.mu.Unlock() + } +} + +// AddFD adds an FD to the list of observed FDs. +func (n *Notifier) AddFD(fd uint32, queue *waiter.Queue) error { + n.addFD(fd, queue) + return nil +} + +// UpdateFD updates the set of events the FD needs to be notified on. +func (n *Notifier) UpdateFD(fd uint32) error { + return n.updateFD(fd) +} + +// RemoveFD removes an FD from the list of observed FDs. +func (n *Notifier) RemoveFD(fd uint32) { + n.removeFD(fd) +} + +// HasFD returns true if the FD is in the list of observed FDs. +// +// This should only be used by tests to assert that FDs are correctly +// registered. +func (n *Notifier) HasFD(fd uint32) bool { + return n.hasFD(fd) +} + +// NonBlockingPoll polls the given fd in non-blocking fashion. It is used just +// to query the FD's current state; this method will block on the RPC response +// although the syscall is non-blocking. +func (n *Notifier) NonBlockingPoll(fd uint32, mask waiter.EventMask) waiter.EventMask { + for { + id, c := n.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Poll{&pb.PollRequest{Fd: fd, Events: mask.ToLinux()}}}, false /* ignoreResult */) + <-c + + res := n.rpcConn.Request(id).Result.(*pb.SyscallResponse_Poll).Poll.Result + if e, ok := res.(*pb.PollResponse_ErrorNumber); ok { + if syscall.Errno(e.ErrorNumber) == syscall.EINTR { + continue + } + return mask + } + + return waiter.EventMaskFromLinux(res.(*pb.PollResponse_Events).Events) + } +} diff --git a/pkg/sentry/socket/rpcinet/notifier/notifier_state_autogen.go b/pkg/sentry/socket/rpcinet/notifier/notifier_state_autogen.go new file mode 100755 index 000000000..f108d91c1 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/notifier/notifier_state_autogen.go @@ -0,0 +1,4 @@ +// automatically generated by stateify. + +package notifier + diff --git a/pkg/sentry/socket/rpcinet/rpcinet.go b/pkg/sentry/socket/rpcinet/rpcinet.go new file mode 100644 index 000000000..5d4fd4dac --- /dev/null +++ b/pkg/sentry/socket/rpcinet/rpcinet.go @@ -0,0 +1,16 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package rpcinet implements sockets using an RPC for each syscall. +package rpcinet diff --git a/pkg/sentry/socket/rpcinet/rpcinet_state_autogen.go b/pkg/sentry/socket/rpcinet/rpcinet_state_autogen.go new file mode 100755 index 000000000..d3076c7e3 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/rpcinet_state_autogen.go @@ -0,0 +1,4 @@ +// automatically generated by stateify. + +package rpcinet + diff --git a/pkg/sentry/socket/rpcinet/socket.go b/pkg/sentry/socket/rpcinet/socket.go new file mode 100644 index 000000000..55e0b6665 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/socket.go @@ -0,0 +1,887 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpcinet + +import ( + "sync/atomic" + "syscall" + "time" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/conn" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/notifier" + pb "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// socketOperations implements fs.FileOperations and socket.Socket for a socket +// implemented using a host socket. +type socketOperations struct { + fsutil.FilePipeSeek `state:"nosave"` + fsutil.FileNotDirReaddir `state:"nosave"` + fsutil.FileNoFsync `state:"nosave"` + fsutil.FileNoMMap `state:"nosave"` + fsutil.FileNoSplice `state:"nosave"` + fsutil.FileNoopFlush `state:"nosave"` + fsutil.FileUseInodeUnstableAttr `state:"nosave"` + socket.SendReceiveTimeout + + family int // Read-only. + fd uint32 // must be O_NONBLOCK + wq *waiter.Queue + rpcConn *conn.RPCConnection + notifier *notifier.Notifier + + // shState is the state of the connection with respect to shutdown. Because + // we're mixing non-blocking semantics on the other side we have to adapt for + // some strange differences between blocking and non-blocking sockets. + shState int32 +} + +// Verify that we actually implement socket.Socket. +var _ = socket.Socket(&socketOperations{}) + +// New creates a new RPC socket. +func newSocketFile(ctx context.Context, stack *Stack, family int, skType int, protocol int) (*fs.File, *syserr.Error) { + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Socket{&pb.SocketRequest{Family: int64(family), Type: int64(skType | syscall.SOCK_NONBLOCK), Protocol: int64(protocol)}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Socket).Socket.Result + if e, ok := res.(*pb.SocketResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + fd := res.(*pb.SocketResponse_Fd).Fd + + var wq waiter.Queue + stack.notifier.AddFD(fd, &wq) + + dirent := socket.NewDirent(ctx, socketDevice) + defer dirent.DecRef() + return fs.NewFile(ctx, dirent, fs.FileFlags{Read: true, Write: true}, &socketOperations{ + family: family, + wq: &wq, + fd: fd, + rpcConn: stack.rpcConn, + notifier: stack.notifier, + }), nil +} + +func isBlockingErrno(err error) bool { + return err == syscall.EAGAIN || err == syscall.EWOULDBLOCK +} + +func translateIOSyscallError(err error) error { + if isBlockingErrno(err) { + return syserror.ErrWouldBlock + } + return err +} + +// setShutdownFlags will set the shutdown flag so we can handle blocking reads +// after a read shutdown. +func (s *socketOperations) setShutdownFlags(how int) { + var f tcpip.ShutdownFlags + switch how { + case linux.SHUT_RD: + f = tcpip.ShutdownRead + case linux.SHUT_WR: + f = tcpip.ShutdownWrite + case linux.SHUT_RDWR: + f = tcpip.ShutdownWrite | tcpip.ShutdownRead + } + + // Atomically update the flags. + for { + old := atomic.LoadInt32(&s.shState) + if atomic.CompareAndSwapInt32(&s.shState, old, old|int32(f)) { + break + } + } +} + +func (s *socketOperations) resetShutdownFlags() { + atomic.StoreInt32(&s.shState, 0) +} + +func (s *socketOperations) isShutRdSet() bool { + return atomic.LoadInt32(&s.shState)&int32(tcpip.ShutdownRead) != 0 +} + +func (s *socketOperations) isShutWrSet() bool { + return atomic.LoadInt32(&s.shState)&int32(tcpip.ShutdownWrite) != 0 +} + +// Release implements fs.FileOperations.Release. +func (s *socketOperations) Release() { + s.notifier.RemoveFD(s.fd) + + // We always need to close the FD. + _, _ = s.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Close{&pb.CloseRequest{Fd: s.fd}}}, true /* ignoreResult */) +} + +// Readiness implements waiter.Waitable.Readiness. +func (s *socketOperations) Readiness(mask waiter.EventMask) waiter.EventMask { + return s.notifier.NonBlockingPoll(s.fd, mask) +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (s *socketOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { + s.wq.EventRegister(e, mask) + s.notifier.UpdateFD(s.fd) +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (s *socketOperations) EventUnregister(e *waiter.Entry) { + s.wq.EventUnregister(e) + s.notifier.UpdateFD(s.fd) +} + +func rpcRead(t *kernel.Task, req *pb.SyscallRequest_Read) (*pb.ReadResponse_Data, *syserr.Error) { + s := t.NetworkContext().(*Stack) + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Read).Read.Result + if e, ok := res.(*pb.ReadResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.ReadResponse_Data), nil +} + +// Read implements fs.FileOperations.Read. +func (s *socketOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { + req := &pb.SyscallRequest_Read{&pb.ReadRequest{ + Fd: s.fd, + Length: uint32(dst.NumBytes()), + }} + + res, se := rpcRead(ctx.(*kernel.Task), req) + if se == nil { + n, e := dst.CopyOut(ctx, res.Data) + return int64(n), e + } + + return 0, se.ToError() +} + +func rpcWrite(t *kernel.Task, req *pb.SyscallRequest_Write) (uint32, *syserr.Error) { + s := t.NetworkContext().(*Stack) + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Write).Write.Result + if e, ok := res.(*pb.WriteResponse_ErrorNumber); ok { + return 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.WriteResponse_Length).Length, nil +} + +// Write implements fs.FileOperations.Write. +func (s *socketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { + t := ctx.(*kernel.Task) + v := buffer.NewView(int(src.NumBytes())) + + // Copy all the data into the buffer. + if _, err := src.CopyIn(t, v); err != nil { + return 0, err + } + + n, err := rpcWrite(t, &pb.SyscallRequest_Write{&pb.WriteRequest{Fd: s.fd, Data: v}}) + if n > 0 && n < uint32(src.NumBytes()) { + // The FileOperations.Write interface expects us to return ErrWouldBlock in + // the event of a partial write. + return int64(n), syserror.ErrWouldBlock + } + return int64(n), err.ToError() +} + +func rpcConnect(t *kernel.Task, fd uint32, sockaddr []byte) *syserr.Error { + s := t.NetworkContext().(*Stack) + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Connect{&pb.ConnectRequest{Fd: uint32(fd), Address: sockaddr}}}, false /* ignoreResult */) + <-c + + if e := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Connect).Connect.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + return nil +} + +// Connect implements socket.Socket.Connect. +func (s *socketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { + if !blocking { + e := rpcConnect(t, s.fd, sockaddr) + if e == nil { + // Reset the shutdown state on new connects. + s.resetShutdownFlags() + } + return e + } + + // Register for notification when the endpoint becomes writable, then + // initiate the connection. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut|waiter.EventIn|waiter.EventHUp) + defer s.EventUnregister(&e) + for { + if err := rpcConnect(t, s.fd, sockaddr); err == nil || err != syserr.ErrInProgress && err != syserr.ErrAlreadyInProgress { + if err == nil { + // Reset the shutdown state on new connects. + s.resetShutdownFlags() + } + return err + } + + // It's pending, so we have to wait for a notification, and fetch the + // result once the wait completes. + if err := t.Block(ch); err != nil { + return syserr.FromError(err) + } + } +} + +func rpcAccept(t *kernel.Task, fd uint32, peer bool) (*pb.AcceptResponse_ResultPayload, *syserr.Error) { + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Accept{&pb.AcceptRequest{Fd: fd, Peer: peer, Flags: syscall.SOCK_NONBLOCK}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Accept).Accept.Result + if e, ok := res.(*pb.AcceptResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + return res.(*pb.AcceptResponse_Payload).Payload, nil +} + +// Accept implements socket.Socket.Accept. +func (s *socketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) { + payload, se := rpcAccept(t, s.fd, peerRequested) + + // Check if we need to block. + if blocking && se == syserr.ErrTryAgain { + // Register for notifications. + e, ch := waiter.NewChannelEntry(nil) + // FIXME(b/119878986): This waiter.EventHUp is a partial + // measure, need to figure out how to translate linux events to + // internal events. + s.EventRegister(&e, waiter.EventIn|waiter.EventHUp) + defer s.EventUnregister(&e) + + // Try to accept the connection again; if it fails, then wait until we + // get a notification. + for { + if payload, se = rpcAccept(t, s.fd, peerRequested); se != syserr.ErrTryAgain { + break + } + + if err := t.Block(ch); err != nil { + return 0, nil, 0, syserr.FromError(err) + } + } + } + + // Handle any error from accept. + if se != nil { + return 0, nil, 0, se + } + + var wq waiter.Queue + s.notifier.AddFD(payload.Fd, &wq) + + dirent := socket.NewDirent(t, socketDevice) + defer dirent.DecRef() + file := fs.NewFile(t, dirent, fs.FileFlags{Read: true, Write: true, NonBlocking: flags&linux.SOCK_NONBLOCK != 0}, &socketOperations{ + wq: &wq, + fd: payload.Fd, + rpcConn: s.rpcConn, + notifier: s.notifier, + }) + defer file.DecRef() + + fdFlags := kernel.FDFlags{ + CloseOnExec: flags&linux.SOCK_CLOEXEC != 0, + } + fd, err := t.FDMap().NewFDFrom(0, file, fdFlags, t.ThreadGroup().Limits()) + if err != nil { + return 0, nil, 0, syserr.FromError(err) + } + t.Kernel().RecordSocket(file, s.family) + + if peerRequested { + return fd, payload.Address.Address, payload.Address.Length, nil + } + + return fd, nil, 0, nil +} + +// Bind implements socket.Socket.Bind. +func (s *socketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Bind{&pb.BindRequest{Fd: s.fd, Address: sockaddr}}}, false /* ignoreResult */) + <-c + + if e := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Bind).Bind.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + return nil +} + +// Listen implements socket.Socket.Listen. +func (s *socketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error { + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Listen{&pb.ListenRequest{Fd: s.fd, Backlog: int64(backlog)}}}, false /* ignoreResult */) + <-c + + if e := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Listen).Listen.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + return nil +} + +// Shutdown implements socket.Socket.Shutdown. +func (s *socketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { + // We save the shutdown state because of strange differences on linux + // related to recvs on blocking vs. non-blocking sockets after a SHUT_RD. + // We need to emulate that behavior on the blocking side. + // TODO(b/120096741): There is a possible race that can exist with loopback, + // where data could possibly be lost. + s.setShutdownFlags(how) + + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Shutdown{&pb.ShutdownRequest{Fd: s.fd, How: int64(how)}}}, false /* ignoreResult */) + <-c + + if e := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Shutdown).Shutdown.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + + return nil +} + +// GetSockOpt implements socket.Socket.GetSockOpt. +func (s *socketOperations) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) { + // SO_RCVTIMEO and SO_SNDTIMEO are special because blocking is performed + // within the sentry. + if level == linux.SOL_SOCKET && name == linux.SO_RCVTIMEO { + if outLen < linux.SizeOfTimeval { + return nil, syserr.ErrInvalidArgument + } + + return linux.NsecToTimeval(s.RecvTimeout()), nil + } + if level == linux.SOL_SOCKET && name == linux.SO_SNDTIMEO { + if outLen < linux.SizeOfTimeval { + return nil, syserr.ErrInvalidArgument + } + + return linux.NsecToTimeval(s.SendTimeout()), nil + } + + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_GetSockOpt{&pb.GetSockOptRequest{Fd: s.fd, Level: int64(level), Name: int64(name), Length: uint32(outLen)}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_GetSockOpt).GetSockOpt.Result + if e, ok := res.(*pb.GetSockOptResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.GetSockOptResponse_Opt).Opt, nil +} + +// SetSockOpt implements socket.Socket.SetSockOpt. +func (s *socketOperations) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error { + // Because blocking actually happens within the sentry we need to inspect + // this socket option to determine if it's a SO_RCVTIMEO or SO_SNDTIMEO, + // and if so, we will save it and use it as the deadline for recv(2) + // or send(2) related syscalls. + if level == linux.SOL_SOCKET && name == linux.SO_RCVTIMEO { + if len(opt) < linux.SizeOfTimeval { + return syserr.ErrInvalidArgument + } + + var v linux.Timeval + binary.Unmarshal(opt[:linux.SizeOfTimeval], usermem.ByteOrder, &v) + if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) { + return syserr.ErrDomain + } + s.SetRecvTimeout(v.ToNsecCapped()) + return nil + } + if level == linux.SOL_SOCKET && name == linux.SO_SNDTIMEO { + if len(opt) < linux.SizeOfTimeval { + return syserr.ErrInvalidArgument + } + + var v linux.Timeval + binary.Unmarshal(opt[:linux.SizeOfTimeval], usermem.ByteOrder, &v) + if v.Usec < 0 || v.Usec >= int64(time.Second/time.Microsecond) { + return syserr.ErrDomain + } + s.SetSendTimeout(v.ToNsecCapped()) + return nil + } + + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_SetSockOpt{&pb.SetSockOptRequest{Fd: s.fd, Level: int64(level), Name: int64(name), Opt: opt}}}, false /* ignoreResult */) + <-c + + if e := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_SetSockOpt).SetSockOpt.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + return nil +} + +// GetPeerName implements socket.Socket.GetPeerName. +func (s *socketOperations) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_GetPeerName{&pb.GetPeerNameRequest{Fd: s.fd}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_GetPeerName).GetPeerName.Result + if e, ok := res.(*pb.GetPeerNameResponse_ErrorNumber); ok { + return nil, 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + addr := res.(*pb.GetPeerNameResponse_Address).Address + return addr.Address, addr.Length, nil +} + +// GetSockName implements socket.Socket.GetSockName. +func (s *socketOperations) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + stack := t.NetworkContext().(*Stack) + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_GetSockName{&pb.GetSockNameRequest{Fd: s.fd}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_GetSockName).GetSockName.Result + if e, ok := res.(*pb.GetSockNameResponse_ErrorNumber); ok { + return nil, 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + addr := res.(*pb.GetSockNameResponse_Address).Address + return addr.Address, addr.Length, nil +} + +func rpcIoctl(t *kernel.Task, fd, cmd uint32, arg []byte) ([]byte, error) { + stack := t.NetworkContext().(*Stack) + + id, c := stack.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Ioctl{&pb.IOCtlRequest{Fd: fd, Cmd: cmd, Arg: arg}}}, false /* ignoreResult */) + <-c + + res := stack.rpcConn.Request(id).Result.(*pb.SyscallResponse_Ioctl).Ioctl.Result + if e, ok := res.(*pb.IOCtlResponse_ErrorNumber); ok { + return nil, syscall.Errno(e.ErrorNumber) + } + + return res.(*pb.IOCtlResponse_Value).Value, nil +} + +// ifconfIoctlFromStack populates a struct ifconf for the SIOCGIFCONF ioctl. +func ifconfIoctlFromStack(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error { + // If Ptr is NULL, return the necessary buffer size via Len. + // Otherwise, write up to Len bytes starting at Ptr containing ifreq + // structs. + t := ctx.(*kernel.Task) + s := t.NetworkContext().(*Stack) + if s == nil { + return syserr.ErrNoDevice.ToError() + } + + if ifc.Ptr == 0 { + ifc.Len = int32(len(s.Interfaces())) * int32(linux.SizeOfIFReq) + return nil + } + + max := ifc.Len + ifc.Len = 0 + for key, ifaceAddrs := range s.InterfaceAddrs() { + iface := s.Interfaces()[key] + for _, ifaceAddr := range ifaceAddrs { + // Don't write past the end of the buffer. + if ifc.Len+int32(linux.SizeOfIFReq) > max { + break + } + if ifaceAddr.Family != linux.AF_INET { + continue + } + + // Populate ifr.ifr_addr. + ifr := linux.IFReq{} + ifr.SetName(iface.Name) + usermem.ByteOrder.PutUint16(ifr.Data[0:2], uint16(ifaceAddr.Family)) + usermem.ByteOrder.PutUint16(ifr.Data[2:4], 0) + copy(ifr.Data[4:8], ifaceAddr.Addr[:4]) + + // Copy the ifr to userspace. + dst := uintptr(ifc.Ptr) + uintptr(ifc.Len) + ifc.Len += int32(linux.SizeOfIFReq) + if _, err := usermem.CopyObjectOut(ctx, io, usermem.Addr(dst), ifr, usermem.IOOpts{ + AddressSpaceActive: true, + }); err != nil { + return err + } + } + } + return nil +} + +// Ioctl implements fs.FileOperations.Ioctl. +func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + t := ctx.(*kernel.Task) + + cmd := uint32(args[1].Int()) + arg := args[2].Pointer() + + var buf []byte + switch cmd { + // The following ioctls take 4 byte argument parameters. + case syscall.TIOCINQ, + syscall.TIOCOUTQ: + buf = make([]byte, 4) + // The following ioctls have args which are sizeof(struct ifreq). + case syscall.SIOCGIFADDR, + syscall.SIOCGIFBRDADDR, + syscall.SIOCGIFDSTADDR, + syscall.SIOCGIFFLAGS, + syscall.SIOCGIFHWADDR, + syscall.SIOCGIFINDEX, + syscall.SIOCGIFMAP, + syscall.SIOCGIFMETRIC, + syscall.SIOCGIFMTU, + syscall.SIOCGIFNAME, + syscall.SIOCGIFNETMASK, + syscall.SIOCGIFTXQLEN: + buf = make([]byte, linux.SizeOfIFReq) + case syscall.SIOCGIFCONF: + // SIOCGIFCONF has slightly different behavior than the others, in that it + // will need to populate the array of ifreqs. + var ifc linux.IFConf + if _, err := usermem.CopyObjectIn(ctx, io, args[2].Pointer(), &ifc, usermem.IOOpts{ + AddressSpaceActive: true, + }); err != nil { + return 0, err + } + + if err := ifconfIoctlFromStack(ctx, io, &ifc); err != nil { + return 0, err + } + _, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), ifc, usermem.IOOpts{ + AddressSpaceActive: true, + }) + + return 0, err + + case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG: + unimpl.EmitUnimplementedEvent(ctx) + + default: + return 0, syserror.ENOTTY + } + + _, err := io.CopyIn(ctx, arg, buf, usermem.IOOpts{ + AddressSpaceActive: true, + }) + + if err != nil { + return 0, err + } + + v, err := rpcIoctl(t, s.fd, cmd, buf) + if err != nil { + return 0, err + } + + if len(v) != len(buf) { + return 0, syserror.EINVAL + } + + _, err = io.CopyOut(ctx, arg, v, usermem.IOOpts{ + AddressSpaceActive: true, + }) + return 0, err +} + +func rpcRecvMsg(t *kernel.Task, req *pb.SyscallRequest_Recvmsg) (*pb.RecvmsgResponse_ResultPayload, *syserr.Error) { + s := t.NetworkContext().(*Stack) + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Recvmsg).Recvmsg.Result + if e, ok := res.(*pb.RecvmsgResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.RecvmsgResponse_Payload).Payload, nil +} + +// Because we only support SO_TIMESTAMP we will search control messages for +// that value and set it if so, all other control messages will be ignored. +func (s *socketOperations) extractControlMessages(payload *pb.RecvmsgResponse_ResultPayload) socket.ControlMessages { + c := socket.ControlMessages{} + if len(payload.GetCmsgData()) > 0 { + // Parse the control messages looking for SO_TIMESTAMP. + msgs, e := syscall.ParseSocketControlMessage(payload.GetCmsgData()) + if e != nil { + return socket.ControlMessages{} + } + for _, m := range msgs { + if m.Header.Level != linux.SOL_SOCKET || m.Header.Type != linux.SO_TIMESTAMP { + continue + } + + // Let's parse the time stamp and set it. + if len(m.Data) < linux.SizeOfTimeval { + // Give up on locating the SO_TIMESTAMP option. + return socket.ControlMessages{} + } + + var v linux.Timeval + binary.Unmarshal(m.Data[:linux.SizeOfTimeval], usermem.ByteOrder, &v) + c.IP.HasTimestamp = true + c.IP.Timestamp = v.ToNsecCapped() + break + } + } + return c +} + +// RecvMsg implements socket.Socket.RecvMsg. +func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (int, int, interface{}, uint32, socket.ControlMessages, *syserr.Error) { + req := &pb.SyscallRequest_Recvmsg{&pb.RecvmsgRequest{ + Fd: s.fd, + Length: uint32(dst.NumBytes()), + Sender: senderRequested, + Trunc: flags&linux.MSG_TRUNC != 0, + Peek: flags&linux.MSG_PEEK != 0, + CmsgLength: uint32(controlDataLen), + }} + + res, err := rpcRecvMsg(t, req) + if err == nil { + var e error + var n int + if len(res.Data) > 0 { + n, e = dst.CopyOut(t, res.Data) + if e == nil && n != len(res.Data) { + panic("CopyOut failed to copy full buffer") + } + } + c := s.extractControlMessages(res) + return int(res.Length), 0, res.Address.GetAddress(), res.Address.GetLength(), c, syserr.FromError(e) + } + if err != syserr.ErrWouldBlock && err != syserr.ErrTryAgain || flags&linux.MSG_DONTWAIT != 0 { + return 0, 0, nil, 0, socket.ControlMessages{}, err + } + + // We'll have to block. Register for notifications and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + for { + res, err := rpcRecvMsg(t, req) + if err == nil { + var e error + var n int + if len(res.Data) > 0 { + n, e = dst.CopyOut(t, res.Data) + if e == nil && n != len(res.Data) { + panic("CopyOut failed to copy full buffer") + } + } + c := s.extractControlMessages(res) + return int(res.Length), 0, res.Address.GetAddress(), res.Address.GetLength(), c, syserr.FromError(e) + } + if err != syserr.ErrWouldBlock && err != syserr.ErrTryAgain { + return 0, 0, nil, 0, socket.ControlMessages{}, err + } + + if s.isShutRdSet() { + // Blocking would have caused us to block indefinitely so we return 0, + // this is the same behavior as Linux. + return 0, 0, nil, 0, socket.ControlMessages{}, nil + } + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain + } + return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err) + } + } +} + +func rpcSendMsg(t *kernel.Task, req *pb.SyscallRequest_Sendmsg) (uint32, *syserr.Error) { + s := t.NetworkContext().(*Stack) + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Sendmsg).Sendmsg.Result + if e, ok := res.(*pb.SendmsgResponse_ErrorNumber); ok { + return 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.SendmsgResponse_Length).Length, nil +} + +// SendMsg implements socket.Socket.SendMsg. +func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) { + // Whitelist flags. + if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_EOR|syscall.MSG_FASTOPEN|syscall.MSG_MORE|syscall.MSG_NOSIGNAL) != 0 { + return 0, syserr.ErrInvalidArgument + } + + // Reject Unix control messages. + if !controlMessages.Unix.Empty() { + return 0, syserr.ErrInvalidArgument + } + + v := buffer.NewView(int(src.NumBytes())) + + // Copy all the data into the buffer. + if _, err := src.CopyIn(t, v); err != nil { + return 0, syserr.FromError(err) + } + + // TODO(bgeffon): this needs to change to map directly to a SendMsg syscall + // in the RPC. + totalWritten := 0 + n, err := rpcSendMsg(t, &pb.SyscallRequest_Sendmsg{&pb.SendmsgRequest{ + Fd: uint32(s.fd), + Data: v, + Address: to, + More: flags&linux.MSG_MORE != 0, + EndOfRecord: flags&linux.MSG_EOR != 0, + }}) + + if err != syserr.ErrWouldBlock && err != syserr.ErrTryAgain || flags&linux.MSG_DONTWAIT != 0 { + return int(n), err + } + + if n > 0 { + totalWritten += int(n) + v.TrimFront(int(n)) + } + + // We'll have to block. Register for notification and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + + for { + n, err := rpcSendMsg(t, &pb.SyscallRequest_Sendmsg{&pb.SendmsgRequest{ + Fd: uint32(s.fd), + Data: v, + Address: to, + More: flags&linux.MSG_MORE != 0, + EndOfRecord: flags&linux.MSG_EOR != 0, + }}) + + if n > 0 { + totalWritten += int(n) + v.TrimFront(int(n)) + + if err == nil && totalWritten < int(src.NumBytes()) { + continue + } + } + + if err != syserr.ErrWouldBlock && err != syserr.ErrTryAgain { + // We eat the error in this situation. + return int(totalWritten), nil + } + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + return int(totalWritten), syserr.ErrTryAgain + } + return int(totalWritten), syserr.FromError(err) + } + } +} + +type socketProvider struct { + family int +} + +// Socket implements socket.Provider.Socket. +func (p *socketProvider) Socket(t *kernel.Task, stypeflags transport.SockType, protocol int) (*fs.File, *syserr.Error) { + // Check that we are using the RPC network stack. + stack := t.NetworkContext() + if stack == nil { + return nil, nil + } + + s, ok := stack.(*Stack) + if !ok { + return nil, nil + } + + // Only accept TCP and UDP. + // + // Try to restrict the flags we will accept to minimize backwards + // incompatibility with netstack. + stype := int(stypeflags) & linux.SOCK_TYPE_MASK + switch stype { + case syscall.SOCK_STREAM: + switch protocol { + case 0, syscall.IPPROTO_TCP: + // ok + default: + return nil, nil + } + case syscall.SOCK_DGRAM: + switch protocol { + case 0, syscall.IPPROTO_UDP: + // ok + default: + return nil, nil + } + default: + return nil, nil + } + + return newSocketFile(t, s, p.family, stype, 0) +} + +// Pair implements socket.Provider.Pair. +func (p *socketProvider) Pair(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) { + // Not supported by AF_INET/AF_INET6. + return nil, nil, nil +} + +func init() { + for _, family := range []int{syscall.AF_INET, syscall.AF_INET6} { + socket.RegisterProvider(family, &socketProvider{family}) + } +} diff --git a/pkg/sentry/socket/rpcinet/stack.go b/pkg/sentry/socket/rpcinet/stack.go new file mode 100644 index 000000000..a1be711df --- /dev/null +++ b/pkg/sentry/socket/rpcinet/stack.go @@ -0,0 +1,135 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpcinet + +import ( + "fmt" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/sentry/inet" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/hostinet" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/conn" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/notifier" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/unet" +) + +// Stack implements inet.Stack for RPC backed sockets. +type Stack struct { + interfaces map[int32]inet.Interface + interfaceAddrs map[int32][]inet.InterfaceAddr + rpcConn *conn.RPCConnection + notifier *notifier.Notifier +} + +// NewStack returns a Stack containing the current state of the host network +// stack. +func NewStack(fd int32) (*Stack, error) { + sock, err := unet.NewSocket(int(fd)) + if err != nil { + return nil, err + } + + stack := &Stack{ + interfaces: make(map[int32]inet.Interface), + interfaceAddrs: make(map[int32][]inet.InterfaceAddr), + rpcConn: conn.NewRPCConnection(sock), + } + + var e error + stack.notifier, e = notifier.NewRPCNotifier(stack.rpcConn) + if e != nil { + return nil, e + } + + links, err := stack.DoNetlinkRouteRequest(syscall.RTM_GETLINK) + if err != nil { + return nil, fmt.Errorf("RTM_GETLINK failed: %v", err) + } + + addrs, err := stack.DoNetlinkRouteRequest(syscall.RTM_GETADDR) + if err != nil { + return nil, fmt.Errorf("RTM_GETADDR failed: %v", err) + } + + e = hostinet.ExtractHostInterfaces(links, addrs, stack.interfaces, stack.interfaceAddrs) + if e != nil { + return nil, e + } + + return stack, nil +} + +// RPCReadFile will execute the ReadFile helper RPC method which avoids the +// common pattern of open(2), read(2), close(2) by doing all three operations +// as a single RPC. It will read the entire file or return EFBIG if the file +// was too large. +func (s *Stack) RPCReadFile(path string) ([]byte, *syserr.Error) { + return s.rpcConn.RPCReadFile(path) +} + +// RPCWriteFile will execute the WriteFile helper RPC method which avoids the +// common pattern of open(2), write(2), write(2), close(2) by doing all +// operations as a single RPC. +func (s *Stack) RPCWriteFile(path string, data []byte) (int64, *syserr.Error) { + return s.rpcConn.RPCWriteFile(path, data) +} + +// Interfaces implements inet.Stack.Interfaces. +func (s *Stack) Interfaces() map[int32]inet.Interface { + return s.interfaces +} + +// InterfaceAddrs implements inet.Stack.InterfaceAddrs. +func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { + return s.interfaceAddrs +} + +// SupportsIPv6 implements inet.Stack.SupportsIPv6. +func (s *Stack) SupportsIPv6() bool { + panic("rpcinet handles procfs directly this method should not be called") +} + +// TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize. +func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) { + panic("rpcinet handles procfs directly this method should not be called") +} + +// SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize. +func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error { + panic("rpcinet handles procfs directly this method should not be called") + +} + +// TCPSendBufferSize implements inet.Stack.TCPSendBufferSize. +func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) { + panic("rpcinet handles procfs directly this method should not be called") + +} + +// SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize. +func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error { + panic("rpcinet handles procfs directly this method should not be called") +} + +// TCPSACKEnabled implements inet.Stack.TCPSACKEnabled. +func (s *Stack) TCPSACKEnabled() (bool, error) { + panic("rpcinet handles procfs directly this method should not be called") +} + +// SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled. +func (s *Stack) SetTCPSACKEnabled(enabled bool) error { + panic("rpcinet handles procfs directly this method should not be called") +} diff --git a/pkg/sentry/socket/rpcinet/stack_unsafe.go b/pkg/sentry/socket/rpcinet/stack_unsafe.go new file mode 100644 index 000000000..e53f578ba --- /dev/null +++ b/pkg/sentry/socket/rpcinet/stack_unsafe.go @@ -0,0 +1,193 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpcinet + +import ( + "syscall" + "unsafe" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + pb "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" +) + +// NewNetlinkRouteRequest builds a netlink message for getting the RIB, +// the routing information base. +func newNetlinkRouteRequest(proto, seq, family int) []byte { + rr := &syscall.NetlinkRouteRequest{} + rr.Header.Len = uint32(syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg) + rr.Header.Type = uint16(proto) + rr.Header.Flags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST + rr.Header.Seq = uint32(seq) + rr.Data.Family = uint8(family) + return netlinkRRtoWireFormat(rr) +} + +func netlinkRRtoWireFormat(rr *syscall.NetlinkRouteRequest) []byte { + b := make([]byte, rr.Header.Len) + *(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len + *(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type + *(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags + *(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq + *(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid + b[16] = byte(rr.Data.Family) + return b +} + +func (s *Stack) getNetlinkFd() (uint32, *syserr.Error) { + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Socket{&pb.SocketRequest{Family: int64(syscall.AF_NETLINK), Type: int64(syscall.SOCK_RAW | syscall.SOCK_NONBLOCK), Protocol: int64(syscall.NETLINK_ROUTE)}}}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Socket).Socket.Result + if e, ok := res.(*pb.SocketResponse_ErrorNumber); ok { + return 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + return res.(*pb.SocketResponse_Fd).Fd, nil +} + +func (s *Stack) bindNetlinkFd(fd uint32, sockaddr []byte) *syserr.Error { + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Bind{&pb.BindRequest{Fd: fd, Address: sockaddr}}}, false /* ignoreResult */) + <-c + + if e := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Bind).Bind.ErrorNumber; e != 0 { + return syserr.FromHost(syscall.Errno(e)) + } + return nil +} + +func (s *Stack) closeNetlinkFd(fd uint32) { + _, _ = s.rpcConn.NewRequest(pb.SyscallRequest{Args: &pb.SyscallRequest_Close{&pb.CloseRequest{Fd: fd}}}, true /* ignoreResult */) +} + +func (s *Stack) rpcSendMsg(req *pb.SyscallRequest_Sendmsg) (uint32, *syserr.Error) { + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Sendmsg).Sendmsg.Result + if e, ok := res.(*pb.SendmsgResponse_ErrorNumber); ok { + return 0, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.SendmsgResponse_Length).Length, nil +} + +func (s *Stack) sendMsg(fd uint32, buf []byte, to []byte, flags int) (int, *syserr.Error) { + // Whitelist flags. + if flags&^(syscall.MSG_DONTWAIT|syscall.MSG_EOR|syscall.MSG_FASTOPEN|syscall.MSG_MORE|syscall.MSG_NOSIGNAL) != 0 { + return 0, syserr.ErrInvalidArgument + } + + req := &pb.SyscallRequest_Sendmsg{&pb.SendmsgRequest{ + Fd: fd, + Data: buf, + Address: to, + More: flags&linux.MSG_MORE != 0, + EndOfRecord: flags&linux.MSG_EOR != 0, + }} + + n, err := s.rpcSendMsg(req) + return int(n), err +} + +func (s *Stack) rpcRecvMsg(req *pb.SyscallRequest_Recvmsg) (*pb.RecvmsgResponse_ResultPayload, *syserr.Error) { + id, c := s.rpcConn.NewRequest(pb.SyscallRequest{Args: req}, false /* ignoreResult */) + <-c + + res := s.rpcConn.Request(id).Result.(*pb.SyscallResponse_Recvmsg).Recvmsg.Result + if e, ok := res.(*pb.RecvmsgResponse_ErrorNumber); ok { + return nil, syserr.FromHost(syscall.Errno(e.ErrorNumber)) + } + + return res.(*pb.RecvmsgResponse_Payload).Payload, nil +} + +func (s *Stack) recvMsg(fd, l, flags uint32) ([]byte, *syserr.Error) { + req := &pb.SyscallRequest_Recvmsg{&pb.RecvmsgRequest{ + Fd: fd, + Length: l, + Sender: false, + Trunc: flags&linux.MSG_TRUNC != 0, + Peek: flags&linux.MSG_PEEK != 0, + }} + + res, err := s.rpcRecvMsg(req) + if err != nil { + return nil, err + } + return res.Data, nil +} + +func (s *Stack) netlinkRequest(proto, family int) ([]byte, error) { + fd, err := s.getNetlinkFd() + if err != nil { + return nil, err.ToError() + } + defer s.closeNetlinkFd(fd) + + lsa := syscall.SockaddrNetlink{Family: syscall.AF_NETLINK} + b := binary.Marshal(nil, usermem.ByteOrder, &lsa) + if err := s.bindNetlinkFd(fd, b); err != nil { + return nil, err.ToError() + } + + wb := newNetlinkRouteRequest(proto, 1, family) + _, err = s.sendMsg(fd, wb, b, 0) + if err != nil { + return nil, err.ToError() + } + + var tab []byte +done: + for { + rb, err := s.recvMsg(fd, uint32(syscall.Getpagesize()), 0) + nr := len(rb) + if err != nil { + return nil, err.ToError() + } + + if nr < syscall.NLMSG_HDRLEN { + return nil, syserr.ErrInvalidArgument.ToError() + } + + tab = append(tab, rb...) + msgs, e := syscall.ParseNetlinkMessage(rb) + if e != nil { + return nil, e + } + + for _, m := range msgs { + if m.Header.Type == syscall.NLMSG_DONE { + break done + } + if m.Header.Type == syscall.NLMSG_ERROR { + return nil, syserr.ErrInvalidArgument.ToError() + } + } + } + + return tab, nil +} + +// DoNetlinkRouteRequest returns routing information base, also known as RIB, +// which consists of network facility information, states and parameters. +func (s *Stack) DoNetlinkRouteRequest(req int) ([]syscall.NetlinkMessage, error) { + data, err := s.netlinkRequest(req, syscall.AF_UNSPEC) + if err != nil { + return nil, err + } + return syscall.ParseNetlinkMessage(data) +} diff --git a/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto/syscall_rpc.pb.go b/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto/syscall_rpc.pb.go new file mode 100755 index 000000000..fb68d5294 --- /dev/null +++ b/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto/syscall_rpc.pb.go @@ -0,0 +1,3938 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pkg/sentry/socket/rpcinet/syscall_rpc.proto + +package syscall_rpc + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type SendmsgRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Address []byte `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + More bool `protobuf:"varint,4,opt,name=more,proto3" json:"more,omitempty"` + EndOfRecord bool `protobuf:"varint,5,opt,name=end_of_record,json=endOfRecord,proto3" json:"end_of_record,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SendmsgRequest) Reset() { *m = SendmsgRequest{} } +func (m *SendmsgRequest) String() string { return proto.CompactTextString(m) } +func (*SendmsgRequest) ProtoMessage() {} +func (*SendmsgRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{0} +} + +func (m *SendmsgRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SendmsgRequest.Unmarshal(m, b) +} +func (m *SendmsgRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SendmsgRequest.Marshal(b, m, deterministic) +} +func (m *SendmsgRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendmsgRequest.Merge(m, src) +} +func (m *SendmsgRequest) XXX_Size() int { + return xxx_messageInfo_SendmsgRequest.Size(m) +} +func (m *SendmsgRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SendmsgRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SendmsgRequest proto.InternalMessageInfo + +func (m *SendmsgRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *SendmsgRequest) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *SendmsgRequest) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +func (m *SendmsgRequest) GetMore() bool { + if m != nil { + return m.More + } + return false +} + +func (m *SendmsgRequest) GetEndOfRecord() bool { + if m != nil { + return m.EndOfRecord + } + return false +} + +type SendmsgResponse struct { + // Types that are valid to be assigned to Result: + // *SendmsgResponse_ErrorNumber + // *SendmsgResponse_Length + Result isSendmsgResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SendmsgResponse) Reset() { *m = SendmsgResponse{} } +func (m *SendmsgResponse) String() string { return proto.CompactTextString(m) } +func (*SendmsgResponse) ProtoMessage() {} +func (*SendmsgResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{1} +} + +func (m *SendmsgResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SendmsgResponse.Unmarshal(m, b) +} +func (m *SendmsgResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SendmsgResponse.Marshal(b, m, deterministic) +} +func (m *SendmsgResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendmsgResponse.Merge(m, src) +} +func (m *SendmsgResponse) XXX_Size() int { + return xxx_messageInfo_SendmsgResponse.Size(m) +} +func (m *SendmsgResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SendmsgResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SendmsgResponse proto.InternalMessageInfo + +type isSendmsgResponse_Result interface { + isSendmsgResponse_Result() +} + +type SendmsgResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type SendmsgResponse_Length struct { + Length uint32 `protobuf:"varint,2,opt,name=length,proto3,oneof"` +} + +func (*SendmsgResponse_ErrorNumber) isSendmsgResponse_Result() {} + +func (*SendmsgResponse_Length) isSendmsgResponse_Result() {} + +func (m *SendmsgResponse) GetResult() isSendmsgResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *SendmsgResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*SendmsgResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *SendmsgResponse) GetLength() uint32 { + if x, ok := m.GetResult().(*SendmsgResponse_Length); ok { + return x.Length + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SendmsgResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SendmsgResponse_ErrorNumber)(nil), + (*SendmsgResponse_Length)(nil), + } +} + +type IOCtlRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Cmd uint32 `protobuf:"varint,2,opt,name=cmd,proto3" json:"cmd,omitempty"` + Arg []byte `protobuf:"bytes,3,opt,name=arg,proto3" json:"arg,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IOCtlRequest) Reset() { *m = IOCtlRequest{} } +func (m *IOCtlRequest) String() string { return proto.CompactTextString(m) } +func (*IOCtlRequest) ProtoMessage() {} +func (*IOCtlRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{2} +} + +func (m *IOCtlRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_IOCtlRequest.Unmarshal(m, b) +} +func (m *IOCtlRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_IOCtlRequest.Marshal(b, m, deterministic) +} +func (m *IOCtlRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_IOCtlRequest.Merge(m, src) +} +func (m *IOCtlRequest) XXX_Size() int { + return xxx_messageInfo_IOCtlRequest.Size(m) +} +func (m *IOCtlRequest) XXX_DiscardUnknown() { + xxx_messageInfo_IOCtlRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_IOCtlRequest proto.InternalMessageInfo + +func (m *IOCtlRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *IOCtlRequest) GetCmd() uint32 { + if m != nil { + return m.Cmd + } + return 0 +} + +func (m *IOCtlRequest) GetArg() []byte { + if m != nil { + return m.Arg + } + return nil +} + +type IOCtlResponse struct { + // Types that are valid to be assigned to Result: + // *IOCtlResponse_ErrorNumber + // *IOCtlResponse_Value + Result isIOCtlResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IOCtlResponse) Reset() { *m = IOCtlResponse{} } +func (m *IOCtlResponse) String() string { return proto.CompactTextString(m) } +func (*IOCtlResponse) ProtoMessage() {} +func (*IOCtlResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{3} +} + +func (m *IOCtlResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_IOCtlResponse.Unmarshal(m, b) +} +func (m *IOCtlResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_IOCtlResponse.Marshal(b, m, deterministic) +} +func (m *IOCtlResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_IOCtlResponse.Merge(m, src) +} +func (m *IOCtlResponse) XXX_Size() int { + return xxx_messageInfo_IOCtlResponse.Size(m) +} +func (m *IOCtlResponse) XXX_DiscardUnknown() { + xxx_messageInfo_IOCtlResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_IOCtlResponse proto.InternalMessageInfo + +type isIOCtlResponse_Result interface { + isIOCtlResponse_Result() +} + +type IOCtlResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type IOCtlResponse_Value struct { + Value []byte `protobuf:"bytes,2,opt,name=value,proto3,oneof"` +} + +func (*IOCtlResponse_ErrorNumber) isIOCtlResponse_Result() {} + +func (*IOCtlResponse_Value) isIOCtlResponse_Result() {} + +func (m *IOCtlResponse) GetResult() isIOCtlResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *IOCtlResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*IOCtlResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *IOCtlResponse) GetValue() []byte { + if x, ok := m.GetResult().(*IOCtlResponse_Value); ok { + return x.Value + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*IOCtlResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*IOCtlResponse_ErrorNumber)(nil), + (*IOCtlResponse_Value)(nil), + } +} + +type RecvmsgRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Length uint32 `protobuf:"varint,2,opt,name=length,proto3" json:"length,omitempty"` + Sender bool `protobuf:"varint,3,opt,name=sender,proto3" json:"sender,omitempty"` + Peek bool `protobuf:"varint,4,opt,name=peek,proto3" json:"peek,omitempty"` + Trunc bool `protobuf:"varint,5,opt,name=trunc,proto3" json:"trunc,omitempty"` + CmsgLength uint32 `protobuf:"varint,6,opt,name=cmsg_length,json=cmsgLength,proto3" json:"cmsg_length,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecvmsgRequest) Reset() { *m = RecvmsgRequest{} } +func (m *RecvmsgRequest) String() string { return proto.CompactTextString(m) } +func (*RecvmsgRequest) ProtoMessage() {} +func (*RecvmsgRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{4} +} + +func (m *RecvmsgRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecvmsgRequest.Unmarshal(m, b) +} +func (m *RecvmsgRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecvmsgRequest.Marshal(b, m, deterministic) +} +func (m *RecvmsgRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecvmsgRequest.Merge(m, src) +} +func (m *RecvmsgRequest) XXX_Size() int { + return xxx_messageInfo_RecvmsgRequest.Size(m) +} +func (m *RecvmsgRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RecvmsgRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RecvmsgRequest proto.InternalMessageInfo + +func (m *RecvmsgRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *RecvmsgRequest) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +func (m *RecvmsgRequest) GetSender() bool { + if m != nil { + return m.Sender + } + return false +} + +func (m *RecvmsgRequest) GetPeek() bool { + if m != nil { + return m.Peek + } + return false +} + +func (m *RecvmsgRequest) GetTrunc() bool { + if m != nil { + return m.Trunc + } + return false +} + +func (m *RecvmsgRequest) GetCmsgLength() uint32 { + if m != nil { + return m.CmsgLength + } + return 0 +} + +type OpenRequest struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + Mode uint32 `protobuf:"varint,3,opt,name=mode,proto3" json:"mode,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OpenRequest) Reset() { *m = OpenRequest{} } +func (m *OpenRequest) String() string { return proto.CompactTextString(m) } +func (*OpenRequest) ProtoMessage() {} +func (*OpenRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{5} +} + +func (m *OpenRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OpenRequest.Unmarshal(m, b) +} +func (m *OpenRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OpenRequest.Marshal(b, m, deterministic) +} +func (m *OpenRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_OpenRequest.Merge(m, src) +} +func (m *OpenRequest) XXX_Size() int { + return xxx_messageInfo_OpenRequest.Size(m) +} +func (m *OpenRequest) XXX_DiscardUnknown() { + xxx_messageInfo_OpenRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_OpenRequest proto.InternalMessageInfo + +func (m *OpenRequest) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *OpenRequest) GetFlags() uint32 { + if m != nil { + return m.Flags + } + return 0 +} + +func (m *OpenRequest) GetMode() uint32 { + if m != nil { + return m.Mode + } + return 0 +} + +type OpenResponse struct { + // Types that are valid to be assigned to Result: + // *OpenResponse_ErrorNumber + // *OpenResponse_Fd + Result isOpenResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OpenResponse) Reset() { *m = OpenResponse{} } +func (m *OpenResponse) String() string { return proto.CompactTextString(m) } +func (*OpenResponse) ProtoMessage() {} +func (*OpenResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{6} +} + +func (m *OpenResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OpenResponse.Unmarshal(m, b) +} +func (m *OpenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OpenResponse.Marshal(b, m, deterministic) +} +func (m *OpenResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_OpenResponse.Merge(m, src) +} +func (m *OpenResponse) XXX_Size() int { + return xxx_messageInfo_OpenResponse.Size(m) +} +func (m *OpenResponse) XXX_DiscardUnknown() { + xxx_messageInfo_OpenResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_OpenResponse proto.InternalMessageInfo + +type isOpenResponse_Result interface { + isOpenResponse_Result() +} + +type OpenResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type OpenResponse_Fd struct { + Fd uint32 `protobuf:"varint,2,opt,name=fd,proto3,oneof"` +} + +func (*OpenResponse_ErrorNumber) isOpenResponse_Result() {} + +func (*OpenResponse_Fd) isOpenResponse_Result() {} + +func (m *OpenResponse) GetResult() isOpenResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *OpenResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*OpenResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *OpenResponse) GetFd() uint32 { + if x, ok := m.GetResult().(*OpenResponse_Fd); ok { + return x.Fd + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*OpenResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*OpenResponse_ErrorNumber)(nil), + (*OpenResponse_Fd)(nil), + } +} + +type ReadRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Length uint32 `protobuf:"varint,2,opt,name=length,proto3" json:"length,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadRequest) Reset() { *m = ReadRequest{} } +func (m *ReadRequest) String() string { return proto.CompactTextString(m) } +func (*ReadRequest) ProtoMessage() {} +func (*ReadRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{7} +} + +func (m *ReadRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadRequest.Unmarshal(m, b) +} +func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic) +} +func (m *ReadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadRequest.Merge(m, src) +} +func (m *ReadRequest) XXX_Size() int { + return xxx_messageInfo_ReadRequest.Size(m) +} +func (m *ReadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadRequest proto.InternalMessageInfo + +func (m *ReadRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *ReadRequest) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +type ReadResponse struct { + // Types that are valid to be assigned to Result: + // *ReadResponse_ErrorNumber + // *ReadResponse_Data + Result isReadResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadResponse) Reset() { *m = ReadResponse{} } +func (m *ReadResponse) String() string { return proto.CompactTextString(m) } +func (*ReadResponse) ProtoMessage() {} +func (*ReadResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{8} +} + +func (m *ReadResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadResponse.Unmarshal(m, b) +} +func (m *ReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadResponse.Marshal(b, m, deterministic) +} +func (m *ReadResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadResponse.Merge(m, src) +} +func (m *ReadResponse) XXX_Size() int { + return xxx_messageInfo_ReadResponse.Size(m) +} +func (m *ReadResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReadResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadResponse proto.InternalMessageInfo + +type isReadResponse_Result interface { + isReadResponse_Result() +} + +type ReadResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type ReadResponse_Data struct { + Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` +} + +func (*ReadResponse_ErrorNumber) isReadResponse_Result() {} + +func (*ReadResponse_Data) isReadResponse_Result() {} + +func (m *ReadResponse) GetResult() isReadResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *ReadResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*ReadResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *ReadResponse) GetData() []byte { + if x, ok := m.GetResult().(*ReadResponse_Data); ok { + return x.Data + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ReadResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ReadResponse_ErrorNumber)(nil), + (*ReadResponse_Data)(nil), + } +} + +type ReadFileRequest struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadFileRequest) Reset() { *m = ReadFileRequest{} } +func (m *ReadFileRequest) String() string { return proto.CompactTextString(m) } +func (*ReadFileRequest) ProtoMessage() {} +func (*ReadFileRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{9} +} + +func (m *ReadFileRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadFileRequest.Unmarshal(m, b) +} +func (m *ReadFileRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadFileRequest.Marshal(b, m, deterministic) +} +func (m *ReadFileRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadFileRequest.Merge(m, src) +} +func (m *ReadFileRequest) XXX_Size() int { + return xxx_messageInfo_ReadFileRequest.Size(m) +} +func (m *ReadFileRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReadFileRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadFileRequest proto.InternalMessageInfo + +func (m *ReadFileRequest) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +type ReadFileResponse struct { + // Types that are valid to be assigned to Result: + // *ReadFileResponse_ErrorNumber + // *ReadFileResponse_Data + Result isReadFileResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadFileResponse) Reset() { *m = ReadFileResponse{} } +func (m *ReadFileResponse) String() string { return proto.CompactTextString(m) } +func (*ReadFileResponse) ProtoMessage() {} +func (*ReadFileResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{10} +} + +func (m *ReadFileResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadFileResponse.Unmarshal(m, b) +} +func (m *ReadFileResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadFileResponse.Marshal(b, m, deterministic) +} +func (m *ReadFileResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadFileResponse.Merge(m, src) +} +func (m *ReadFileResponse) XXX_Size() int { + return xxx_messageInfo_ReadFileResponse.Size(m) +} +func (m *ReadFileResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReadFileResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadFileResponse proto.InternalMessageInfo + +type isReadFileResponse_Result interface { + isReadFileResponse_Result() +} + +type ReadFileResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type ReadFileResponse_Data struct { + Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` +} + +func (*ReadFileResponse_ErrorNumber) isReadFileResponse_Result() {} + +func (*ReadFileResponse_Data) isReadFileResponse_Result() {} + +func (m *ReadFileResponse) GetResult() isReadFileResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *ReadFileResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*ReadFileResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *ReadFileResponse) GetData() []byte { + if x, ok := m.GetResult().(*ReadFileResponse_Data); ok { + return x.Data + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ReadFileResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ReadFileResponse_ErrorNumber)(nil), + (*ReadFileResponse_Data)(nil), + } +} + +type WriteRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteRequest) Reset() { *m = WriteRequest{} } +func (m *WriteRequest) String() string { return proto.CompactTextString(m) } +func (*WriteRequest) ProtoMessage() {} +func (*WriteRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{11} +} + +func (m *WriteRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WriteRequest.Unmarshal(m, b) +} +func (m *WriteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WriteRequest.Marshal(b, m, deterministic) +} +func (m *WriteRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteRequest.Merge(m, src) +} +func (m *WriteRequest) XXX_Size() int { + return xxx_messageInfo_WriteRequest.Size(m) +} +func (m *WriteRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WriteRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WriteRequest proto.InternalMessageInfo + +func (m *WriteRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *WriteRequest) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type WriteResponse struct { + // Types that are valid to be assigned to Result: + // *WriteResponse_ErrorNumber + // *WriteResponse_Length + Result isWriteResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteResponse) Reset() { *m = WriteResponse{} } +func (m *WriteResponse) String() string { return proto.CompactTextString(m) } +func (*WriteResponse) ProtoMessage() {} +func (*WriteResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{12} +} + +func (m *WriteResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WriteResponse.Unmarshal(m, b) +} +func (m *WriteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WriteResponse.Marshal(b, m, deterministic) +} +func (m *WriteResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteResponse.Merge(m, src) +} +func (m *WriteResponse) XXX_Size() int { + return xxx_messageInfo_WriteResponse.Size(m) +} +func (m *WriteResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WriteResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WriteResponse proto.InternalMessageInfo + +type isWriteResponse_Result interface { + isWriteResponse_Result() +} + +type WriteResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type WriteResponse_Length struct { + Length uint32 `protobuf:"varint,2,opt,name=length,proto3,oneof"` +} + +func (*WriteResponse_ErrorNumber) isWriteResponse_Result() {} + +func (*WriteResponse_Length) isWriteResponse_Result() {} + +func (m *WriteResponse) GetResult() isWriteResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *WriteResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*WriteResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *WriteResponse) GetLength() uint32 { + if x, ok := m.GetResult().(*WriteResponse_Length); ok { + return x.Length + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*WriteResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*WriteResponse_ErrorNumber)(nil), + (*WriteResponse_Length)(nil), + } +} + +type WriteFileRequest struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteFileRequest) Reset() { *m = WriteFileRequest{} } +func (m *WriteFileRequest) String() string { return proto.CompactTextString(m) } +func (*WriteFileRequest) ProtoMessage() {} +func (*WriteFileRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{13} +} + +func (m *WriteFileRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WriteFileRequest.Unmarshal(m, b) +} +func (m *WriteFileRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WriteFileRequest.Marshal(b, m, deterministic) +} +func (m *WriteFileRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteFileRequest.Merge(m, src) +} +func (m *WriteFileRequest) XXX_Size() int { + return xxx_messageInfo_WriteFileRequest.Size(m) +} +func (m *WriteFileRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WriteFileRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WriteFileRequest proto.InternalMessageInfo + +func (m *WriteFileRequest) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *WriteFileRequest) GetContent() []byte { + if m != nil { + return m.Content + } + return nil +} + +type WriteFileResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + Written uint32 `protobuf:"varint,2,opt,name=written,proto3" json:"written,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteFileResponse) Reset() { *m = WriteFileResponse{} } +func (m *WriteFileResponse) String() string { return proto.CompactTextString(m) } +func (*WriteFileResponse) ProtoMessage() {} +func (*WriteFileResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{14} +} + +func (m *WriteFileResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WriteFileResponse.Unmarshal(m, b) +} +func (m *WriteFileResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WriteFileResponse.Marshal(b, m, deterministic) +} +func (m *WriteFileResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteFileResponse.Merge(m, src) +} +func (m *WriteFileResponse) XXX_Size() int { + return xxx_messageInfo_WriteFileResponse.Size(m) +} +func (m *WriteFileResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WriteFileResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WriteFileResponse proto.InternalMessageInfo + +func (m *WriteFileResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +func (m *WriteFileResponse) GetWritten() uint32 { + if m != nil { + return m.Written + } + return 0 +} + +type AddressResponse struct { + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Length uint32 `protobuf:"varint,2,opt,name=length,proto3" json:"length,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddressResponse) Reset() { *m = AddressResponse{} } +func (m *AddressResponse) String() string { return proto.CompactTextString(m) } +func (*AddressResponse) ProtoMessage() {} +func (*AddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{15} +} + +func (m *AddressResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddressResponse.Unmarshal(m, b) +} +func (m *AddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddressResponse.Marshal(b, m, deterministic) +} +func (m *AddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddressResponse.Merge(m, src) +} +func (m *AddressResponse) XXX_Size() int { + return xxx_messageInfo_AddressResponse.Size(m) +} +func (m *AddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AddressResponse proto.InternalMessageInfo + +func (m *AddressResponse) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +func (m *AddressResponse) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +type RecvmsgResponse struct { + // Types that are valid to be assigned to Result: + // *RecvmsgResponse_ErrorNumber + // *RecvmsgResponse_Payload + Result isRecvmsgResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecvmsgResponse) Reset() { *m = RecvmsgResponse{} } +func (m *RecvmsgResponse) String() string { return proto.CompactTextString(m) } +func (*RecvmsgResponse) ProtoMessage() {} +func (*RecvmsgResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{16} +} + +func (m *RecvmsgResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecvmsgResponse.Unmarshal(m, b) +} +func (m *RecvmsgResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecvmsgResponse.Marshal(b, m, deterministic) +} +func (m *RecvmsgResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecvmsgResponse.Merge(m, src) +} +func (m *RecvmsgResponse) XXX_Size() int { + return xxx_messageInfo_RecvmsgResponse.Size(m) +} +func (m *RecvmsgResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RecvmsgResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RecvmsgResponse proto.InternalMessageInfo + +type isRecvmsgResponse_Result interface { + isRecvmsgResponse_Result() +} + +type RecvmsgResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type RecvmsgResponse_Payload struct { + Payload *RecvmsgResponse_ResultPayload `protobuf:"bytes,2,opt,name=payload,proto3,oneof"` +} + +func (*RecvmsgResponse_ErrorNumber) isRecvmsgResponse_Result() {} + +func (*RecvmsgResponse_Payload) isRecvmsgResponse_Result() {} + +func (m *RecvmsgResponse) GetResult() isRecvmsgResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *RecvmsgResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*RecvmsgResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *RecvmsgResponse) GetPayload() *RecvmsgResponse_ResultPayload { + if x, ok := m.GetResult().(*RecvmsgResponse_Payload); ok { + return x.Payload + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*RecvmsgResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*RecvmsgResponse_ErrorNumber)(nil), + (*RecvmsgResponse_Payload)(nil), + } +} + +type RecvmsgResponse_ResultPayload struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Address *AddressResponse `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + Length uint32 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` + CmsgData []byte `protobuf:"bytes,4,opt,name=cmsg_data,json=cmsgData,proto3" json:"cmsg_data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RecvmsgResponse_ResultPayload) Reset() { *m = RecvmsgResponse_ResultPayload{} } +func (m *RecvmsgResponse_ResultPayload) String() string { return proto.CompactTextString(m) } +func (*RecvmsgResponse_ResultPayload) ProtoMessage() {} +func (*RecvmsgResponse_ResultPayload) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{16, 0} +} + +func (m *RecvmsgResponse_ResultPayload) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RecvmsgResponse_ResultPayload.Unmarshal(m, b) +} +func (m *RecvmsgResponse_ResultPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RecvmsgResponse_ResultPayload.Marshal(b, m, deterministic) +} +func (m *RecvmsgResponse_ResultPayload) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecvmsgResponse_ResultPayload.Merge(m, src) +} +func (m *RecvmsgResponse_ResultPayload) XXX_Size() int { + return xxx_messageInfo_RecvmsgResponse_ResultPayload.Size(m) +} +func (m *RecvmsgResponse_ResultPayload) XXX_DiscardUnknown() { + xxx_messageInfo_RecvmsgResponse_ResultPayload.DiscardUnknown(m) +} + +var xxx_messageInfo_RecvmsgResponse_ResultPayload proto.InternalMessageInfo + +func (m *RecvmsgResponse_ResultPayload) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *RecvmsgResponse_ResultPayload) GetAddress() *AddressResponse { + if m != nil { + return m.Address + } + return nil +} + +func (m *RecvmsgResponse_ResultPayload) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +func (m *RecvmsgResponse_ResultPayload) GetCmsgData() []byte { + if m != nil { + return m.CmsgData + } + return nil +} + +type BindRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Address []byte `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BindRequest) Reset() { *m = BindRequest{} } +func (m *BindRequest) String() string { return proto.CompactTextString(m) } +func (*BindRequest) ProtoMessage() {} +func (*BindRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{17} +} + +func (m *BindRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BindRequest.Unmarshal(m, b) +} +func (m *BindRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BindRequest.Marshal(b, m, deterministic) +} +func (m *BindRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_BindRequest.Merge(m, src) +} +func (m *BindRequest) XXX_Size() int { + return xxx_messageInfo_BindRequest.Size(m) +} +func (m *BindRequest) XXX_DiscardUnknown() { + xxx_messageInfo_BindRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_BindRequest proto.InternalMessageInfo + +func (m *BindRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *BindRequest) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +type BindResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BindResponse) Reset() { *m = BindResponse{} } +func (m *BindResponse) String() string { return proto.CompactTextString(m) } +func (*BindResponse) ProtoMessage() {} +func (*BindResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{18} +} + +func (m *BindResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BindResponse.Unmarshal(m, b) +} +func (m *BindResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BindResponse.Marshal(b, m, deterministic) +} +func (m *BindResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_BindResponse.Merge(m, src) +} +func (m *BindResponse) XXX_Size() int { + return xxx_messageInfo_BindResponse.Size(m) +} +func (m *BindResponse) XXX_DiscardUnknown() { + xxx_messageInfo_BindResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_BindResponse proto.InternalMessageInfo + +func (m *BindResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type AcceptRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Peer bool `protobuf:"varint,2,opt,name=peer,proto3" json:"peer,omitempty"` + Flags int64 `protobuf:"varint,3,opt,name=flags,proto3" json:"flags,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AcceptRequest) Reset() { *m = AcceptRequest{} } +func (m *AcceptRequest) String() string { return proto.CompactTextString(m) } +func (*AcceptRequest) ProtoMessage() {} +func (*AcceptRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{19} +} + +func (m *AcceptRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AcceptRequest.Unmarshal(m, b) +} +func (m *AcceptRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AcceptRequest.Marshal(b, m, deterministic) +} +func (m *AcceptRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptRequest.Merge(m, src) +} +func (m *AcceptRequest) XXX_Size() int { + return xxx_messageInfo_AcceptRequest.Size(m) +} +func (m *AcceptRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AcceptRequest proto.InternalMessageInfo + +func (m *AcceptRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *AcceptRequest) GetPeer() bool { + if m != nil { + return m.Peer + } + return false +} + +func (m *AcceptRequest) GetFlags() int64 { + if m != nil { + return m.Flags + } + return 0 +} + +type AcceptResponse struct { + // Types that are valid to be assigned to Result: + // *AcceptResponse_ErrorNumber + // *AcceptResponse_Payload + Result isAcceptResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AcceptResponse) Reset() { *m = AcceptResponse{} } +func (m *AcceptResponse) String() string { return proto.CompactTextString(m) } +func (*AcceptResponse) ProtoMessage() {} +func (*AcceptResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{20} +} + +func (m *AcceptResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AcceptResponse.Unmarshal(m, b) +} +func (m *AcceptResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AcceptResponse.Marshal(b, m, deterministic) +} +func (m *AcceptResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptResponse.Merge(m, src) +} +func (m *AcceptResponse) XXX_Size() int { + return xxx_messageInfo_AcceptResponse.Size(m) +} +func (m *AcceptResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AcceptResponse proto.InternalMessageInfo + +type isAcceptResponse_Result interface { + isAcceptResponse_Result() +} + +type AcceptResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type AcceptResponse_Payload struct { + Payload *AcceptResponse_ResultPayload `protobuf:"bytes,2,opt,name=payload,proto3,oneof"` +} + +func (*AcceptResponse_ErrorNumber) isAcceptResponse_Result() {} + +func (*AcceptResponse_Payload) isAcceptResponse_Result() {} + +func (m *AcceptResponse) GetResult() isAcceptResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *AcceptResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*AcceptResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *AcceptResponse) GetPayload() *AcceptResponse_ResultPayload { + if x, ok := m.GetResult().(*AcceptResponse_Payload); ok { + return x.Payload + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*AcceptResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*AcceptResponse_ErrorNumber)(nil), + (*AcceptResponse_Payload)(nil), + } +} + +type AcceptResponse_ResultPayload struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Address *AddressResponse `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AcceptResponse_ResultPayload) Reset() { *m = AcceptResponse_ResultPayload{} } +func (m *AcceptResponse_ResultPayload) String() string { return proto.CompactTextString(m) } +func (*AcceptResponse_ResultPayload) ProtoMessage() {} +func (*AcceptResponse_ResultPayload) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{20, 0} +} + +func (m *AcceptResponse_ResultPayload) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AcceptResponse_ResultPayload.Unmarshal(m, b) +} +func (m *AcceptResponse_ResultPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AcceptResponse_ResultPayload.Marshal(b, m, deterministic) +} +func (m *AcceptResponse_ResultPayload) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptResponse_ResultPayload.Merge(m, src) +} +func (m *AcceptResponse_ResultPayload) XXX_Size() int { + return xxx_messageInfo_AcceptResponse_ResultPayload.Size(m) +} +func (m *AcceptResponse_ResultPayload) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptResponse_ResultPayload.DiscardUnknown(m) +} + +var xxx_messageInfo_AcceptResponse_ResultPayload proto.InternalMessageInfo + +func (m *AcceptResponse_ResultPayload) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *AcceptResponse_ResultPayload) GetAddress() *AddressResponse { + if m != nil { + return m.Address + } + return nil +} + +type ConnectRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Address []byte `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectRequest) Reset() { *m = ConnectRequest{} } +func (m *ConnectRequest) String() string { return proto.CompactTextString(m) } +func (*ConnectRequest) ProtoMessage() {} +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{21} +} + +func (m *ConnectRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectRequest.Unmarshal(m, b) +} +func (m *ConnectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectRequest.Marshal(b, m, deterministic) +} +func (m *ConnectRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectRequest.Merge(m, src) +} +func (m *ConnectRequest) XXX_Size() int { + return xxx_messageInfo_ConnectRequest.Size(m) +} +func (m *ConnectRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectRequest proto.InternalMessageInfo + +func (m *ConnectRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *ConnectRequest) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +type ConnectResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConnectResponse) Reset() { *m = ConnectResponse{} } +func (m *ConnectResponse) String() string { return proto.CompactTextString(m) } +func (*ConnectResponse) ProtoMessage() {} +func (*ConnectResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{22} +} + +func (m *ConnectResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConnectResponse.Unmarshal(m, b) +} +func (m *ConnectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConnectResponse.Marshal(b, m, deterministic) +} +func (m *ConnectResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectResponse.Merge(m, src) +} +func (m *ConnectResponse) XXX_Size() int { + return xxx_messageInfo_ConnectResponse.Size(m) +} +func (m *ConnectResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectResponse proto.InternalMessageInfo + +func (m *ConnectResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type ListenRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Backlog int64 `protobuf:"varint,2,opt,name=backlog,proto3" json:"backlog,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListenRequest) Reset() { *m = ListenRequest{} } +func (m *ListenRequest) String() string { return proto.CompactTextString(m) } +func (*ListenRequest) ProtoMessage() {} +func (*ListenRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{23} +} + +func (m *ListenRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListenRequest.Unmarshal(m, b) +} +func (m *ListenRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListenRequest.Marshal(b, m, deterministic) +} +func (m *ListenRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListenRequest.Merge(m, src) +} +func (m *ListenRequest) XXX_Size() int { + return xxx_messageInfo_ListenRequest.Size(m) +} +func (m *ListenRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListenRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListenRequest proto.InternalMessageInfo + +func (m *ListenRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *ListenRequest) GetBacklog() int64 { + if m != nil { + return m.Backlog + } + return 0 +} + +type ListenResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListenResponse) Reset() { *m = ListenResponse{} } +func (m *ListenResponse) String() string { return proto.CompactTextString(m) } +func (*ListenResponse) ProtoMessage() {} +func (*ListenResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{24} +} + +func (m *ListenResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListenResponse.Unmarshal(m, b) +} +func (m *ListenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListenResponse.Marshal(b, m, deterministic) +} +func (m *ListenResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListenResponse.Merge(m, src) +} +func (m *ListenResponse) XXX_Size() int { + return xxx_messageInfo_ListenResponse.Size(m) +} +func (m *ListenResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListenResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListenResponse proto.InternalMessageInfo + +func (m *ListenResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type ShutdownRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + How int64 `protobuf:"varint,2,opt,name=how,proto3" json:"how,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ShutdownRequest) Reset() { *m = ShutdownRequest{} } +func (m *ShutdownRequest) String() string { return proto.CompactTextString(m) } +func (*ShutdownRequest) ProtoMessage() {} +func (*ShutdownRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{25} +} + +func (m *ShutdownRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ShutdownRequest.Unmarshal(m, b) +} +func (m *ShutdownRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ShutdownRequest.Marshal(b, m, deterministic) +} +func (m *ShutdownRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ShutdownRequest.Merge(m, src) +} +func (m *ShutdownRequest) XXX_Size() int { + return xxx_messageInfo_ShutdownRequest.Size(m) +} +func (m *ShutdownRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ShutdownRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ShutdownRequest proto.InternalMessageInfo + +func (m *ShutdownRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *ShutdownRequest) GetHow() int64 { + if m != nil { + return m.How + } + return 0 +} + +type ShutdownResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ShutdownResponse) Reset() { *m = ShutdownResponse{} } +func (m *ShutdownResponse) String() string { return proto.CompactTextString(m) } +func (*ShutdownResponse) ProtoMessage() {} +func (*ShutdownResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{26} +} + +func (m *ShutdownResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ShutdownResponse.Unmarshal(m, b) +} +func (m *ShutdownResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ShutdownResponse.Marshal(b, m, deterministic) +} +func (m *ShutdownResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ShutdownResponse.Merge(m, src) +} +func (m *ShutdownResponse) XXX_Size() int { + return xxx_messageInfo_ShutdownResponse.Size(m) +} +func (m *ShutdownResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ShutdownResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ShutdownResponse proto.InternalMessageInfo + +func (m *ShutdownResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type CloseRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CloseRequest) Reset() { *m = CloseRequest{} } +func (m *CloseRequest) String() string { return proto.CompactTextString(m) } +func (*CloseRequest) ProtoMessage() {} +func (*CloseRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{27} +} + +func (m *CloseRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CloseRequest.Unmarshal(m, b) +} +func (m *CloseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CloseRequest.Marshal(b, m, deterministic) +} +func (m *CloseRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseRequest.Merge(m, src) +} +func (m *CloseRequest) XXX_Size() int { + return xxx_messageInfo_CloseRequest.Size(m) +} +func (m *CloseRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CloseRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CloseRequest proto.InternalMessageInfo + +func (m *CloseRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +type CloseResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CloseResponse) Reset() { *m = CloseResponse{} } +func (m *CloseResponse) String() string { return proto.CompactTextString(m) } +func (*CloseResponse) ProtoMessage() {} +func (*CloseResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{28} +} + +func (m *CloseResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CloseResponse.Unmarshal(m, b) +} +func (m *CloseResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CloseResponse.Marshal(b, m, deterministic) +} +func (m *CloseResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseResponse.Merge(m, src) +} +func (m *CloseResponse) XXX_Size() int { + return xxx_messageInfo_CloseResponse.Size(m) +} +func (m *CloseResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CloseResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CloseResponse proto.InternalMessageInfo + +func (m *CloseResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type GetSockOptRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Level int64 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` + Name int64 `protobuf:"varint,3,opt,name=name,proto3" json:"name,omitempty"` + Length uint32 `protobuf:"varint,4,opt,name=length,proto3" json:"length,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSockOptRequest) Reset() { *m = GetSockOptRequest{} } +func (m *GetSockOptRequest) String() string { return proto.CompactTextString(m) } +func (*GetSockOptRequest) ProtoMessage() {} +func (*GetSockOptRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{29} +} + +func (m *GetSockOptRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSockOptRequest.Unmarshal(m, b) +} +func (m *GetSockOptRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSockOptRequest.Marshal(b, m, deterministic) +} +func (m *GetSockOptRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSockOptRequest.Merge(m, src) +} +func (m *GetSockOptRequest) XXX_Size() int { + return xxx_messageInfo_GetSockOptRequest.Size(m) +} +func (m *GetSockOptRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSockOptRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSockOptRequest proto.InternalMessageInfo + +func (m *GetSockOptRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *GetSockOptRequest) GetLevel() int64 { + if m != nil { + return m.Level + } + return 0 +} + +func (m *GetSockOptRequest) GetName() int64 { + if m != nil { + return m.Name + } + return 0 +} + +func (m *GetSockOptRequest) GetLength() uint32 { + if m != nil { + return m.Length + } + return 0 +} + +type GetSockOptResponse struct { + // Types that are valid to be assigned to Result: + // *GetSockOptResponse_ErrorNumber + // *GetSockOptResponse_Opt + Result isGetSockOptResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSockOptResponse) Reset() { *m = GetSockOptResponse{} } +func (m *GetSockOptResponse) String() string { return proto.CompactTextString(m) } +func (*GetSockOptResponse) ProtoMessage() {} +func (*GetSockOptResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{30} +} + +func (m *GetSockOptResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSockOptResponse.Unmarshal(m, b) +} +func (m *GetSockOptResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSockOptResponse.Marshal(b, m, deterministic) +} +func (m *GetSockOptResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSockOptResponse.Merge(m, src) +} +func (m *GetSockOptResponse) XXX_Size() int { + return xxx_messageInfo_GetSockOptResponse.Size(m) +} +func (m *GetSockOptResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSockOptResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSockOptResponse proto.InternalMessageInfo + +type isGetSockOptResponse_Result interface { + isGetSockOptResponse_Result() +} + +type GetSockOptResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type GetSockOptResponse_Opt struct { + Opt []byte `protobuf:"bytes,2,opt,name=opt,proto3,oneof"` +} + +func (*GetSockOptResponse_ErrorNumber) isGetSockOptResponse_Result() {} + +func (*GetSockOptResponse_Opt) isGetSockOptResponse_Result() {} + +func (m *GetSockOptResponse) GetResult() isGetSockOptResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *GetSockOptResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*GetSockOptResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *GetSockOptResponse) GetOpt() []byte { + if x, ok := m.GetResult().(*GetSockOptResponse_Opt); ok { + return x.Opt + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*GetSockOptResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*GetSockOptResponse_ErrorNumber)(nil), + (*GetSockOptResponse_Opt)(nil), + } +} + +type SetSockOptRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Level int64 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` + Name int64 `protobuf:"varint,3,opt,name=name,proto3" json:"name,omitempty"` + Opt []byte `protobuf:"bytes,4,opt,name=opt,proto3" json:"opt,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SetSockOptRequest) Reset() { *m = SetSockOptRequest{} } +func (m *SetSockOptRequest) String() string { return proto.CompactTextString(m) } +func (*SetSockOptRequest) ProtoMessage() {} +func (*SetSockOptRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{31} +} + +func (m *SetSockOptRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetSockOptRequest.Unmarshal(m, b) +} +func (m *SetSockOptRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetSockOptRequest.Marshal(b, m, deterministic) +} +func (m *SetSockOptRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetSockOptRequest.Merge(m, src) +} +func (m *SetSockOptRequest) XXX_Size() int { + return xxx_messageInfo_SetSockOptRequest.Size(m) +} +func (m *SetSockOptRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetSockOptRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SetSockOptRequest proto.InternalMessageInfo + +func (m *SetSockOptRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *SetSockOptRequest) GetLevel() int64 { + if m != nil { + return m.Level + } + return 0 +} + +func (m *SetSockOptRequest) GetName() int64 { + if m != nil { + return m.Name + } + return 0 +} + +func (m *SetSockOptRequest) GetOpt() []byte { + if m != nil { + return m.Opt + } + return nil +} + +type SetSockOptResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SetSockOptResponse) Reset() { *m = SetSockOptResponse{} } +func (m *SetSockOptResponse) String() string { return proto.CompactTextString(m) } +func (*SetSockOptResponse) ProtoMessage() {} +func (*SetSockOptResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{32} +} + +func (m *SetSockOptResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetSockOptResponse.Unmarshal(m, b) +} +func (m *SetSockOptResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetSockOptResponse.Marshal(b, m, deterministic) +} +func (m *SetSockOptResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetSockOptResponse.Merge(m, src) +} +func (m *SetSockOptResponse) XXX_Size() int { + return xxx_messageInfo_SetSockOptResponse.Size(m) +} +func (m *SetSockOptResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SetSockOptResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SetSockOptResponse proto.InternalMessageInfo + +func (m *SetSockOptResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type GetSockNameRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSockNameRequest) Reset() { *m = GetSockNameRequest{} } +func (m *GetSockNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetSockNameRequest) ProtoMessage() {} +func (*GetSockNameRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{33} +} + +func (m *GetSockNameRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSockNameRequest.Unmarshal(m, b) +} +func (m *GetSockNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSockNameRequest.Marshal(b, m, deterministic) +} +func (m *GetSockNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSockNameRequest.Merge(m, src) +} +func (m *GetSockNameRequest) XXX_Size() int { + return xxx_messageInfo_GetSockNameRequest.Size(m) +} +func (m *GetSockNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSockNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSockNameRequest proto.InternalMessageInfo + +func (m *GetSockNameRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +type GetSockNameResponse struct { + // Types that are valid to be assigned to Result: + // *GetSockNameResponse_ErrorNumber + // *GetSockNameResponse_Address + Result isGetSockNameResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSockNameResponse) Reset() { *m = GetSockNameResponse{} } +func (m *GetSockNameResponse) String() string { return proto.CompactTextString(m) } +func (*GetSockNameResponse) ProtoMessage() {} +func (*GetSockNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{34} +} + +func (m *GetSockNameResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSockNameResponse.Unmarshal(m, b) +} +func (m *GetSockNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSockNameResponse.Marshal(b, m, deterministic) +} +func (m *GetSockNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSockNameResponse.Merge(m, src) +} +func (m *GetSockNameResponse) XXX_Size() int { + return xxx_messageInfo_GetSockNameResponse.Size(m) +} +func (m *GetSockNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSockNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSockNameResponse proto.InternalMessageInfo + +type isGetSockNameResponse_Result interface { + isGetSockNameResponse_Result() +} + +type GetSockNameResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type GetSockNameResponse_Address struct { + Address *AddressResponse `protobuf:"bytes,2,opt,name=address,proto3,oneof"` +} + +func (*GetSockNameResponse_ErrorNumber) isGetSockNameResponse_Result() {} + +func (*GetSockNameResponse_Address) isGetSockNameResponse_Result() {} + +func (m *GetSockNameResponse) GetResult() isGetSockNameResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *GetSockNameResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*GetSockNameResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *GetSockNameResponse) GetAddress() *AddressResponse { + if x, ok := m.GetResult().(*GetSockNameResponse_Address); ok { + return x.Address + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*GetSockNameResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*GetSockNameResponse_ErrorNumber)(nil), + (*GetSockNameResponse_Address)(nil), + } +} + +type GetPeerNameRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetPeerNameRequest) Reset() { *m = GetPeerNameRequest{} } +func (m *GetPeerNameRequest) String() string { return proto.CompactTextString(m) } +func (*GetPeerNameRequest) ProtoMessage() {} +func (*GetPeerNameRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{35} +} + +func (m *GetPeerNameRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPeerNameRequest.Unmarshal(m, b) +} +func (m *GetPeerNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPeerNameRequest.Marshal(b, m, deterministic) +} +func (m *GetPeerNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPeerNameRequest.Merge(m, src) +} +func (m *GetPeerNameRequest) XXX_Size() int { + return xxx_messageInfo_GetPeerNameRequest.Size(m) +} +func (m *GetPeerNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetPeerNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPeerNameRequest proto.InternalMessageInfo + +func (m *GetPeerNameRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +type GetPeerNameResponse struct { + // Types that are valid to be assigned to Result: + // *GetPeerNameResponse_ErrorNumber + // *GetPeerNameResponse_Address + Result isGetPeerNameResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetPeerNameResponse) Reset() { *m = GetPeerNameResponse{} } +func (m *GetPeerNameResponse) String() string { return proto.CompactTextString(m) } +func (*GetPeerNameResponse) ProtoMessage() {} +func (*GetPeerNameResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{36} +} + +func (m *GetPeerNameResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPeerNameResponse.Unmarshal(m, b) +} +func (m *GetPeerNameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPeerNameResponse.Marshal(b, m, deterministic) +} +func (m *GetPeerNameResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPeerNameResponse.Merge(m, src) +} +func (m *GetPeerNameResponse) XXX_Size() int { + return xxx_messageInfo_GetPeerNameResponse.Size(m) +} +func (m *GetPeerNameResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetPeerNameResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPeerNameResponse proto.InternalMessageInfo + +type isGetPeerNameResponse_Result interface { + isGetPeerNameResponse_Result() +} + +type GetPeerNameResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type GetPeerNameResponse_Address struct { + Address *AddressResponse `protobuf:"bytes,2,opt,name=address,proto3,oneof"` +} + +func (*GetPeerNameResponse_ErrorNumber) isGetPeerNameResponse_Result() {} + +func (*GetPeerNameResponse_Address) isGetPeerNameResponse_Result() {} + +func (m *GetPeerNameResponse) GetResult() isGetPeerNameResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *GetPeerNameResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*GetPeerNameResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *GetPeerNameResponse) GetAddress() *AddressResponse { + if x, ok := m.GetResult().(*GetPeerNameResponse_Address); ok { + return x.Address + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*GetPeerNameResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*GetPeerNameResponse_ErrorNumber)(nil), + (*GetPeerNameResponse_Address)(nil), + } +} + +type SocketRequest struct { + Family int64 `protobuf:"varint,1,opt,name=family,proto3" json:"family,omitempty"` + Type int64 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"` + Protocol int64 `protobuf:"varint,3,opt,name=protocol,proto3" json:"protocol,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SocketRequest) Reset() { *m = SocketRequest{} } +func (m *SocketRequest) String() string { return proto.CompactTextString(m) } +func (*SocketRequest) ProtoMessage() {} +func (*SocketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{37} +} + +func (m *SocketRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SocketRequest.Unmarshal(m, b) +} +func (m *SocketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SocketRequest.Marshal(b, m, deterministic) +} +func (m *SocketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SocketRequest.Merge(m, src) +} +func (m *SocketRequest) XXX_Size() int { + return xxx_messageInfo_SocketRequest.Size(m) +} +func (m *SocketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SocketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SocketRequest proto.InternalMessageInfo + +func (m *SocketRequest) GetFamily() int64 { + if m != nil { + return m.Family + } + return 0 +} + +func (m *SocketRequest) GetType() int64 { + if m != nil { + return m.Type + } + return 0 +} + +func (m *SocketRequest) GetProtocol() int64 { + if m != nil { + return m.Protocol + } + return 0 +} + +type SocketResponse struct { + // Types that are valid to be assigned to Result: + // *SocketResponse_ErrorNumber + // *SocketResponse_Fd + Result isSocketResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SocketResponse) Reset() { *m = SocketResponse{} } +func (m *SocketResponse) String() string { return proto.CompactTextString(m) } +func (*SocketResponse) ProtoMessage() {} +func (*SocketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{38} +} + +func (m *SocketResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SocketResponse.Unmarshal(m, b) +} +func (m *SocketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SocketResponse.Marshal(b, m, deterministic) +} +func (m *SocketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SocketResponse.Merge(m, src) +} +func (m *SocketResponse) XXX_Size() int { + return xxx_messageInfo_SocketResponse.Size(m) +} +func (m *SocketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SocketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SocketResponse proto.InternalMessageInfo + +type isSocketResponse_Result interface { + isSocketResponse_Result() +} + +type SocketResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type SocketResponse_Fd struct { + Fd uint32 `protobuf:"varint,2,opt,name=fd,proto3,oneof"` +} + +func (*SocketResponse_ErrorNumber) isSocketResponse_Result() {} + +func (*SocketResponse_Fd) isSocketResponse_Result() {} + +func (m *SocketResponse) GetResult() isSocketResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *SocketResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*SocketResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *SocketResponse) GetFd() uint32 { + if x, ok := m.GetResult().(*SocketResponse_Fd); ok { + return x.Fd + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SocketResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SocketResponse_ErrorNumber)(nil), + (*SocketResponse_Fd)(nil), + } +} + +type EpollWaitRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + NumEvents uint32 `protobuf:"varint,2,opt,name=num_events,json=numEvents,proto3" json:"num_events,omitempty"` + Msec int64 `protobuf:"zigzag64,3,opt,name=msec,proto3" json:"msec,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollWaitRequest) Reset() { *m = EpollWaitRequest{} } +func (m *EpollWaitRequest) String() string { return proto.CompactTextString(m) } +func (*EpollWaitRequest) ProtoMessage() {} +func (*EpollWaitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{39} +} + +func (m *EpollWaitRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollWaitRequest.Unmarshal(m, b) +} +func (m *EpollWaitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollWaitRequest.Marshal(b, m, deterministic) +} +func (m *EpollWaitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollWaitRequest.Merge(m, src) +} +func (m *EpollWaitRequest) XXX_Size() int { + return xxx_messageInfo_EpollWaitRequest.Size(m) +} +func (m *EpollWaitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EpollWaitRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollWaitRequest proto.InternalMessageInfo + +func (m *EpollWaitRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *EpollWaitRequest) GetNumEvents() uint32 { + if m != nil { + return m.NumEvents + } + return 0 +} + +func (m *EpollWaitRequest) GetMsec() int64 { + if m != nil { + return m.Msec + } + return 0 +} + +type EpollEvent struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Events uint32 `protobuf:"varint,2,opt,name=events,proto3" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollEvent) Reset() { *m = EpollEvent{} } +func (m *EpollEvent) String() string { return proto.CompactTextString(m) } +func (*EpollEvent) ProtoMessage() {} +func (*EpollEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{40} +} + +func (m *EpollEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollEvent.Unmarshal(m, b) +} +func (m *EpollEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollEvent.Marshal(b, m, deterministic) +} +func (m *EpollEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollEvent.Merge(m, src) +} +func (m *EpollEvent) XXX_Size() int { + return xxx_messageInfo_EpollEvent.Size(m) +} +func (m *EpollEvent) XXX_DiscardUnknown() { + xxx_messageInfo_EpollEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollEvent proto.InternalMessageInfo + +func (m *EpollEvent) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *EpollEvent) GetEvents() uint32 { + if m != nil { + return m.Events + } + return 0 +} + +type EpollEvents struct { + Events []*EpollEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollEvents) Reset() { *m = EpollEvents{} } +func (m *EpollEvents) String() string { return proto.CompactTextString(m) } +func (*EpollEvents) ProtoMessage() {} +func (*EpollEvents) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{41} +} + +func (m *EpollEvents) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollEvents.Unmarshal(m, b) +} +func (m *EpollEvents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollEvents.Marshal(b, m, deterministic) +} +func (m *EpollEvents) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollEvents.Merge(m, src) +} +func (m *EpollEvents) XXX_Size() int { + return xxx_messageInfo_EpollEvents.Size(m) +} +func (m *EpollEvents) XXX_DiscardUnknown() { + xxx_messageInfo_EpollEvents.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollEvents proto.InternalMessageInfo + +func (m *EpollEvents) GetEvents() []*EpollEvent { + if m != nil { + return m.Events + } + return nil +} + +type EpollWaitResponse struct { + // Types that are valid to be assigned to Result: + // *EpollWaitResponse_ErrorNumber + // *EpollWaitResponse_Events + Result isEpollWaitResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollWaitResponse) Reset() { *m = EpollWaitResponse{} } +func (m *EpollWaitResponse) String() string { return proto.CompactTextString(m) } +func (*EpollWaitResponse) ProtoMessage() {} +func (*EpollWaitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{42} +} + +func (m *EpollWaitResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollWaitResponse.Unmarshal(m, b) +} +func (m *EpollWaitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollWaitResponse.Marshal(b, m, deterministic) +} +func (m *EpollWaitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollWaitResponse.Merge(m, src) +} +func (m *EpollWaitResponse) XXX_Size() int { + return xxx_messageInfo_EpollWaitResponse.Size(m) +} +func (m *EpollWaitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EpollWaitResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollWaitResponse proto.InternalMessageInfo + +type isEpollWaitResponse_Result interface { + isEpollWaitResponse_Result() +} + +type EpollWaitResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type EpollWaitResponse_Events struct { + Events *EpollEvents `protobuf:"bytes,2,opt,name=events,proto3,oneof"` +} + +func (*EpollWaitResponse_ErrorNumber) isEpollWaitResponse_Result() {} + +func (*EpollWaitResponse_Events) isEpollWaitResponse_Result() {} + +func (m *EpollWaitResponse) GetResult() isEpollWaitResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *EpollWaitResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*EpollWaitResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *EpollWaitResponse) GetEvents() *EpollEvents { + if x, ok := m.GetResult().(*EpollWaitResponse_Events); ok { + return x.Events + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*EpollWaitResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*EpollWaitResponse_ErrorNumber)(nil), + (*EpollWaitResponse_Events)(nil), + } +} + +type EpollCtlRequest struct { + Epfd uint32 `protobuf:"varint,1,opt,name=epfd,proto3" json:"epfd,omitempty"` + Op int64 `protobuf:"varint,2,opt,name=op,proto3" json:"op,omitempty"` + Fd uint32 `protobuf:"varint,3,opt,name=fd,proto3" json:"fd,omitempty"` + Event *EpollEvent `protobuf:"bytes,4,opt,name=event,proto3" json:"event,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollCtlRequest) Reset() { *m = EpollCtlRequest{} } +func (m *EpollCtlRequest) String() string { return proto.CompactTextString(m) } +func (*EpollCtlRequest) ProtoMessage() {} +func (*EpollCtlRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{43} +} + +func (m *EpollCtlRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollCtlRequest.Unmarshal(m, b) +} +func (m *EpollCtlRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollCtlRequest.Marshal(b, m, deterministic) +} +func (m *EpollCtlRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollCtlRequest.Merge(m, src) +} +func (m *EpollCtlRequest) XXX_Size() int { + return xxx_messageInfo_EpollCtlRequest.Size(m) +} +func (m *EpollCtlRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EpollCtlRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollCtlRequest proto.InternalMessageInfo + +func (m *EpollCtlRequest) GetEpfd() uint32 { + if m != nil { + return m.Epfd + } + return 0 +} + +func (m *EpollCtlRequest) GetOp() int64 { + if m != nil { + return m.Op + } + return 0 +} + +func (m *EpollCtlRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *EpollCtlRequest) GetEvent() *EpollEvent { + if m != nil { + return m.Event + } + return nil +} + +type EpollCtlResponse struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3" json:"error_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollCtlResponse) Reset() { *m = EpollCtlResponse{} } +func (m *EpollCtlResponse) String() string { return proto.CompactTextString(m) } +func (*EpollCtlResponse) ProtoMessage() {} +func (*EpollCtlResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{44} +} + +func (m *EpollCtlResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollCtlResponse.Unmarshal(m, b) +} +func (m *EpollCtlResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollCtlResponse.Marshal(b, m, deterministic) +} +func (m *EpollCtlResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollCtlResponse.Merge(m, src) +} +func (m *EpollCtlResponse) XXX_Size() int { + return xxx_messageInfo_EpollCtlResponse.Size(m) +} +func (m *EpollCtlResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EpollCtlResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollCtlResponse proto.InternalMessageInfo + +func (m *EpollCtlResponse) GetErrorNumber() uint32 { + if m != nil { + return m.ErrorNumber + } + return 0 +} + +type EpollCreate1Request struct { + Flag int64 `protobuf:"varint,1,opt,name=flag,proto3" json:"flag,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollCreate1Request) Reset() { *m = EpollCreate1Request{} } +func (m *EpollCreate1Request) String() string { return proto.CompactTextString(m) } +func (*EpollCreate1Request) ProtoMessage() {} +func (*EpollCreate1Request) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{45} +} + +func (m *EpollCreate1Request) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollCreate1Request.Unmarshal(m, b) +} +func (m *EpollCreate1Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollCreate1Request.Marshal(b, m, deterministic) +} +func (m *EpollCreate1Request) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollCreate1Request.Merge(m, src) +} +func (m *EpollCreate1Request) XXX_Size() int { + return xxx_messageInfo_EpollCreate1Request.Size(m) +} +func (m *EpollCreate1Request) XXX_DiscardUnknown() { + xxx_messageInfo_EpollCreate1Request.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollCreate1Request proto.InternalMessageInfo + +func (m *EpollCreate1Request) GetFlag() int64 { + if m != nil { + return m.Flag + } + return 0 +} + +type EpollCreate1Response struct { + // Types that are valid to be assigned to Result: + // *EpollCreate1Response_ErrorNumber + // *EpollCreate1Response_Fd + Result isEpollCreate1Response_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EpollCreate1Response) Reset() { *m = EpollCreate1Response{} } +func (m *EpollCreate1Response) String() string { return proto.CompactTextString(m) } +func (*EpollCreate1Response) ProtoMessage() {} +func (*EpollCreate1Response) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{46} +} + +func (m *EpollCreate1Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EpollCreate1Response.Unmarshal(m, b) +} +func (m *EpollCreate1Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EpollCreate1Response.Marshal(b, m, deterministic) +} +func (m *EpollCreate1Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_EpollCreate1Response.Merge(m, src) +} +func (m *EpollCreate1Response) XXX_Size() int { + return xxx_messageInfo_EpollCreate1Response.Size(m) +} +func (m *EpollCreate1Response) XXX_DiscardUnknown() { + xxx_messageInfo_EpollCreate1Response.DiscardUnknown(m) +} + +var xxx_messageInfo_EpollCreate1Response proto.InternalMessageInfo + +type isEpollCreate1Response_Result interface { + isEpollCreate1Response_Result() +} + +type EpollCreate1Response_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type EpollCreate1Response_Fd struct { + Fd uint32 `protobuf:"varint,2,opt,name=fd,proto3,oneof"` +} + +func (*EpollCreate1Response_ErrorNumber) isEpollCreate1Response_Result() {} + +func (*EpollCreate1Response_Fd) isEpollCreate1Response_Result() {} + +func (m *EpollCreate1Response) GetResult() isEpollCreate1Response_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *EpollCreate1Response) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*EpollCreate1Response_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *EpollCreate1Response) GetFd() uint32 { + if x, ok := m.GetResult().(*EpollCreate1Response_Fd); ok { + return x.Fd + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*EpollCreate1Response) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*EpollCreate1Response_ErrorNumber)(nil), + (*EpollCreate1Response_Fd)(nil), + } +} + +type PollRequest struct { + Fd uint32 `protobuf:"varint,1,opt,name=fd,proto3" json:"fd,omitempty"` + Events uint32 `protobuf:"varint,2,opt,name=events,proto3" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PollRequest) Reset() { *m = PollRequest{} } +func (m *PollRequest) String() string { return proto.CompactTextString(m) } +func (*PollRequest) ProtoMessage() {} +func (*PollRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{47} +} + +func (m *PollRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PollRequest.Unmarshal(m, b) +} +func (m *PollRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PollRequest.Marshal(b, m, deterministic) +} +func (m *PollRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PollRequest.Merge(m, src) +} +func (m *PollRequest) XXX_Size() int { + return xxx_messageInfo_PollRequest.Size(m) +} +func (m *PollRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PollRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PollRequest proto.InternalMessageInfo + +func (m *PollRequest) GetFd() uint32 { + if m != nil { + return m.Fd + } + return 0 +} + +func (m *PollRequest) GetEvents() uint32 { + if m != nil { + return m.Events + } + return 0 +} + +type PollResponse struct { + // Types that are valid to be assigned to Result: + // *PollResponse_ErrorNumber + // *PollResponse_Events + Result isPollResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PollResponse) Reset() { *m = PollResponse{} } +func (m *PollResponse) String() string { return proto.CompactTextString(m) } +func (*PollResponse) ProtoMessage() {} +func (*PollResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{48} +} + +func (m *PollResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PollResponse.Unmarshal(m, b) +} +func (m *PollResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PollResponse.Marshal(b, m, deterministic) +} +func (m *PollResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PollResponse.Merge(m, src) +} +func (m *PollResponse) XXX_Size() int { + return xxx_messageInfo_PollResponse.Size(m) +} +func (m *PollResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PollResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PollResponse proto.InternalMessageInfo + +type isPollResponse_Result interface { + isPollResponse_Result() +} + +type PollResponse_ErrorNumber struct { + ErrorNumber uint32 `protobuf:"varint,1,opt,name=error_number,json=errorNumber,proto3,oneof"` +} + +type PollResponse_Events struct { + Events uint32 `protobuf:"varint,2,opt,name=events,proto3,oneof"` +} + +func (*PollResponse_ErrorNumber) isPollResponse_Result() {} + +func (*PollResponse_Events) isPollResponse_Result() {} + +func (m *PollResponse) GetResult() isPollResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *PollResponse) GetErrorNumber() uint32 { + if x, ok := m.GetResult().(*PollResponse_ErrorNumber); ok { + return x.ErrorNumber + } + return 0 +} + +func (m *PollResponse) GetEvents() uint32 { + if x, ok := m.GetResult().(*PollResponse_Events); ok { + return x.Events + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*PollResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*PollResponse_ErrorNumber)(nil), + (*PollResponse_Events)(nil), + } +} + +type SyscallRequest struct { + // Types that are valid to be assigned to Args: + // *SyscallRequest_Socket + // *SyscallRequest_Sendmsg + // *SyscallRequest_Recvmsg + // *SyscallRequest_Bind + // *SyscallRequest_Accept + // *SyscallRequest_Connect + // *SyscallRequest_Listen + // *SyscallRequest_Shutdown + // *SyscallRequest_Close + // *SyscallRequest_GetSockOpt + // *SyscallRequest_SetSockOpt + // *SyscallRequest_GetSockName + // *SyscallRequest_GetPeerName + // *SyscallRequest_EpollWait + // *SyscallRequest_EpollCtl + // *SyscallRequest_EpollCreate1 + // *SyscallRequest_Poll + // *SyscallRequest_Read + // *SyscallRequest_Write + // *SyscallRequest_Open + // *SyscallRequest_Ioctl + // *SyscallRequest_WriteFile + // *SyscallRequest_ReadFile + Args isSyscallRequest_Args `protobuf_oneof:"args"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyscallRequest) Reset() { *m = SyscallRequest{} } +func (m *SyscallRequest) String() string { return proto.CompactTextString(m) } +func (*SyscallRequest) ProtoMessage() {} +func (*SyscallRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{49} +} + +func (m *SyscallRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyscallRequest.Unmarshal(m, b) +} +func (m *SyscallRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyscallRequest.Marshal(b, m, deterministic) +} +func (m *SyscallRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyscallRequest.Merge(m, src) +} +func (m *SyscallRequest) XXX_Size() int { + return xxx_messageInfo_SyscallRequest.Size(m) +} +func (m *SyscallRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SyscallRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SyscallRequest proto.InternalMessageInfo + +type isSyscallRequest_Args interface { + isSyscallRequest_Args() +} + +type SyscallRequest_Socket struct { + Socket *SocketRequest `protobuf:"bytes,1,opt,name=socket,proto3,oneof"` +} + +type SyscallRequest_Sendmsg struct { + Sendmsg *SendmsgRequest `protobuf:"bytes,2,opt,name=sendmsg,proto3,oneof"` +} + +type SyscallRequest_Recvmsg struct { + Recvmsg *RecvmsgRequest `protobuf:"bytes,3,opt,name=recvmsg,proto3,oneof"` +} + +type SyscallRequest_Bind struct { + Bind *BindRequest `protobuf:"bytes,4,opt,name=bind,proto3,oneof"` +} + +type SyscallRequest_Accept struct { + Accept *AcceptRequest `protobuf:"bytes,5,opt,name=accept,proto3,oneof"` +} + +type SyscallRequest_Connect struct { + Connect *ConnectRequest `protobuf:"bytes,6,opt,name=connect,proto3,oneof"` +} + +type SyscallRequest_Listen struct { + Listen *ListenRequest `protobuf:"bytes,7,opt,name=listen,proto3,oneof"` +} + +type SyscallRequest_Shutdown struct { + Shutdown *ShutdownRequest `protobuf:"bytes,8,opt,name=shutdown,proto3,oneof"` +} + +type SyscallRequest_Close struct { + Close *CloseRequest `protobuf:"bytes,9,opt,name=close,proto3,oneof"` +} + +type SyscallRequest_GetSockOpt struct { + GetSockOpt *GetSockOptRequest `protobuf:"bytes,10,opt,name=get_sock_opt,json=getSockOpt,proto3,oneof"` +} + +type SyscallRequest_SetSockOpt struct { + SetSockOpt *SetSockOptRequest `protobuf:"bytes,11,opt,name=set_sock_opt,json=setSockOpt,proto3,oneof"` +} + +type SyscallRequest_GetSockName struct { + GetSockName *GetSockNameRequest `protobuf:"bytes,12,opt,name=get_sock_name,json=getSockName,proto3,oneof"` +} + +type SyscallRequest_GetPeerName struct { + GetPeerName *GetPeerNameRequest `protobuf:"bytes,13,opt,name=get_peer_name,json=getPeerName,proto3,oneof"` +} + +type SyscallRequest_EpollWait struct { + EpollWait *EpollWaitRequest `protobuf:"bytes,14,opt,name=epoll_wait,json=epollWait,proto3,oneof"` +} + +type SyscallRequest_EpollCtl struct { + EpollCtl *EpollCtlRequest `protobuf:"bytes,15,opt,name=epoll_ctl,json=epollCtl,proto3,oneof"` +} + +type SyscallRequest_EpollCreate1 struct { + EpollCreate1 *EpollCreate1Request `protobuf:"bytes,16,opt,name=epoll_create1,json=epollCreate1,proto3,oneof"` +} + +type SyscallRequest_Poll struct { + Poll *PollRequest `protobuf:"bytes,17,opt,name=poll,proto3,oneof"` +} + +type SyscallRequest_Read struct { + Read *ReadRequest `protobuf:"bytes,18,opt,name=read,proto3,oneof"` +} + +type SyscallRequest_Write struct { + Write *WriteRequest `protobuf:"bytes,19,opt,name=write,proto3,oneof"` +} + +type SyscallRequest_Open struct { + Open *OpenRequest `protobuf:"bytes,20,opt,name=open,proto3,oneof"` +} + +type SyscallRequest_Ioctl struct { + Ioctl *IOCtlRequest `protobuf:"bytes,21,opt,name=ioctl,proto3,oneof"` +} + +type SyscallRequest_WriteFile struct { + WriteFile *WriteFileRequest `protobuf:"bytes,22,opt,name=write_file,json=writeFile,proto3,oneof"` +} + +type SyscallRequest_ReadFile struct { + ReadFile *ReadFileRequest `protobuf:"bytes,23,opt,name=read_file,json=readFile,proto3,oneof"` +} + +func (*SyscallRequest_Socket) isSyscallRequest_Args() {} + +func (*SyscallRequest_Sendmsg) isSyscallRequest_Args() {} + +func (*SyscallRequest_Recvmsg) isSyscallRequest_Args() {} + +func (*SyscallRequest_Bind) isSyscallRequest_Args() {} + +func (*SyscallRequest_Accept) isSyscallRequest_Args() {} + +func (*SyscallRequest_Connect) isSyscallRequest_Args() {} + +func (*SyscallRequest_Listen) isSyscallRequest_Args() {} + +func (*SyscallRequest_Shutdown) isSyscallRequest_Args() {} + +func (*SyscallRequest_Close) isSyscallRequest_Args() {} + +func (*SyscallRequest_GetSockOpt) isSyscallRequest_Args() {} + +func (*SyscallRequest_SetSockOpt) isSyscallRequest_Args() {} + +func (*SyscallRequest_GetSockName) isSyscallRequest_Args() {} + +func (*SyscallRequest_GetPeerName) isSyscallRequest_Args() {} + +func (*SyscallRequest_EpollWait) isSyscallRequest_Args() {} + +func (*SyscallRequest_EpollCtl) isSyscallRequest_Args() {} + +func (*SyscallRequest_EpollCreate1) isSyscallRequest_Args() {} + +func (*SyscallRequest_Poll) isSyscallRequest_Args() {} + +func (*SyscallRequest_Read) isSyscallRequest_Args() {} + +func (*SyscallRequest_Write) isSyscallRequest_Args() {} + +func (*SyscallRequest_Open) isSyscallRequest_Args() {} + +func (*SyscallRequest_Ioctl) isSyscallRequest_Args() {} + +func (*SyscallRequest_WriteFile) isSyscallRequest_Args() {} + +func (*SyscallRequest_ReadFile) isSyscallRequest_Args() {} + +func (m *SyscallRequest) GetArgs() isSyscallRequest_Args { + if m != nil { + return m.Args + } + return nil +} + +func (m *SyscallRequest) GetSocket() *SocketRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Socket); ok { + return x.Socket + } + return nil +} + +func (m *SyscallRequest) GetSendmsg() *SendmsgRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Sendmsg); ok { + return x.Sendmsg + } + return nil +} + +func (m *SyscallRequest) GetRecvmsg() *RecvmsgRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Recvmsg); ok { + return x.Recvmsg + } + return nil +} + +func (m *SyscallRequest) GetBind() *BindRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Bind); ok { + return x.Bind + } + return nil +} + +func (m *SyscallRequest) GetAccept() *AcceptRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Accept); ok { + return x.Accept + } + return nil +} + +func (m *SyscallRequest) GetConnect() *ConnectRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Connect); ok { + return x.Connect + } + return nil +} + +func (m *SyscallRequest) GetListen() *ListenRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Listen); ok { + return x.Listen + } + return nil +} + +func (m *SyscallRequest) GetShutdown() *ShutdownRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Shutdown); ok { + return x.Shutdown + } + return nil +} + +func (m *SyscallRequest) GetClose() *CloseRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Close); ok { + return x.Close + } + return nil +} + +func (m *SyscallRequest) GetGetSockOpt() *GetSockOptRequest { + if x, ok := m.GetArgs().(*SyscallRequest_GetSockOpt); ok { + return x.GetSockOpt + } + return nil +} + +func (m *SyscallRequest) GetSetSockOpt() *SetSockOptRequest { + if x, ok := m.GetArgs().(*SyscallRequest_SetSockOpt); ok { + return x.SetSockOpt + } + return nil +} + +func (m *SyscallRequest) GetGetSockName() *GetSockNameRequest { + if x, ok := m.GetArgs().(*SyscallRequest_GetSockName); ok { + return x.GetSockName + } + return nil +} + +func (m *SyscallRequest) GetGetPeerName() *GetPeerNameRequest { + if x, ok := m.GetArgs().(*SyscallRequest_GetPeerName); ok { + return x.GetPeerName + } + return nil +} + +func (m *SyscallRequest) GetEpollWait() *EpollWaitRequest { + if x, ok := m.GetArgs().(*SyscallRequest_EpollWait); ok { + return x.EpollWait + } + return nil +} + +func (m *SyscallRequest) GetEpollCtl() *EpollCtlRequest { + if x, ok := m.GetArgs().(*SyscallRequest_EpollCtl); ok { + return x.EpollCtl + } + return nil +} + +func (m *SyscallRequest) GetEpollCreate1() *EpollCreate1Request { + if x, ok := m.GetArgs().(*SyscallRequest_EpollCreate1); ok { + return x.EpollCreate1 + } + return nil +} + +func (m *SyscallRequest) GetPoll() *PollRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Poll); ok { + return x.Poll + } + return nil +} + +func (m *SyscallRequest) GetRead() *ReadRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Read); ok { + return x.Read + } + return nil +} + +func (m *SyscallRequest) GetWrite() *WriteRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Write); ok { + return x.Write + } + return nil +} + +func (m *SyscallRequest) GetOpen() *OpenRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Open); ok { + return x.Open + } + return nil +} + +func (m *SyscallRequest) GetIoctl() *IOCtlRequest { + if x, ok := m.GetArgs().(*SyscallRequest_Ioctl); ok { + return x.Ioctl + } + return nil +} + +func (m *SyscallRequest) GetWriteFile() *WriteFileRequest { + if x, ok := m.GetArgs().(*SyscallRequest_WriteFile); ok { + return x.WriteFile + } + return nil +} + +func (m *SyscallRequest) GetReadFile() *ReadFileRequest { + if x, ok := m.GetArgs().(*SyscallRequest_ReadFile); ok { + return x.ReadFile + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SyscallRequest) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SyscallRequest_Socket)(nil), + (*SyscallRequest_Sendmsg)(nil), + (*SyscallRequest_Recvmsg)(nil), + (*SyscallRequest_Bind)(nil), + (*SyscallRequest_Accept)(nil), + (*SyscallRequest_Connect)(nil), + (*SyscallRequest_Listen)(nil), + (*SyscallRequest_Shutdown)(nil), + (*SyscallRequest_Close)(nil), + (*SyscallRequest_GetSockOpt)(nil), + (*SyscallRequest_SetSockOpt)(nil), + (*SyscallRequest_GetSockName)(nil), + (*SyscallRequest_GetPeerName)(nil), + (*SyscallRequest_EpollWait)(nil), + (*SyscallRequest_EpollCtl)(nil), + (*SyscallRequest_EpollCreate1)(nil), + (*SyscallRequest_Poll)(nil), + (*SyscallRequest_Read)(nil), + (*SyscallRequest_Write)(nil), + (*SyscallRequest_Open)(nil), + (*SyscallRequest_Ioctl)(nil), + (*SyscallRequest_WriteFile)(nil), + (*SyscallRequest_ReadFile)(nil), + } +} + +type SyscallResponse struct { + // Types that are valid to be assigned to Result: + // *SyscallResponse_Socket + // *SyscallResponse_Sendmsg + // *SyscallResponse_Recvmsg + // *SyscallResponse_Bind + // *SyscallResponse_Accept + // *SyscallResponse_Connect + // *SyscallResponse_Listen + // *SyscallResponse_Shutdown + // *SyscallResponse_Close + // *SyscallResponse_GetSockOpt + // *SyscallResponse_SetSockOpt + // *SyscallResponse_GetSockName + // *SyscallResponse_GetPeerName + // *SyscallResponse_EpollWait + // *SyscallResponse_EpollCtl + // *SyscallResponse_EpollCreate1 + // *SyscallResponse_Poll + // *SyscallResponse_Read + // *SyscallResponse_Write + // *SyscallResponse_Open + // *SyscallResponse_Ioctl + // *SyscallResponse_WriteFile + // *SyscallResponse_ReadFile + Result isSyscallResponse_Result `protobuf_oneof:"result"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyscallResponse) Reset() { *m = SyscallResponse{} } +func (m *SyscallResponse) String() string { return proto.CompactTextString(m) } +func (*SyscallResponse) ProtoMessage() {} +func (*SyscallResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd04f3a8f0c5288b, []int{50} +} + +func (m *SyscallResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyscallResponse.Unmarshal(m, b) +} +func (m *SyscallResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyscallResponse.Marshal(b, m, deterministic) +} +func (m *SyscallResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyscallResponse.Merge(m, src) +} +func (m *SyscallResponse) XXX_Size() int { + return xxx_messageInfo_SyscallResponse.Size(m) +} +func (m *SyscallResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SyscallResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SyscallResponse proto.InternalMessageInfo + +type isSyscallResponse_Result interface { + isSyscallResponse_Result() +} + +type SyscallResponse_Socket struct { + Socket *SocketResponse `protobuf:"bytes,1,opt,name=socket,proto3,oneof"` +} + +type SyscallResponse_Sendmsg struct { + Sendmsg *SendmsgResponse `protobuf:"bytes,2,opt,name=sendmsg,proto3,oneof"` +} + +type SyscallResponse_Recvmsg struct { + Recvmsg *RecvmsgResponse `protobuf:"bytes,3,opt,name=recvmsg,proto3,oneof"` +} + +type SyscallResponse_Bind struct { + Bind *BindResponse `protobuf:"bytes,4,opt,name=bind,proto3,oneof"` +} + +type SyscallResponse_Accept struct { + Accept *AcceptResponse `protobuf:"bytes,5,opt,name=accept,proto3,oneof"` +} + +type SyscallResponse_Connect struct { + Connect *ConnectResponse `protobuf:"bytes,6,opt,name=connect,proto3,oneof"` +} + +type SyscallResponse_Listen struct { + Listen *ListenResponse `protobuf:"bytes,7,opt,name=listen,proto3,oneof"` +} + +type SyscallResponse_Shutdown struct { + Shutdown *ShutdownResponse `protobuf:"bytes,8,opt,name=shutdown,proto3,oneof"` +} + +type SyscallResponse_Close struct { + Close *CloseResponse `protobuf:"bytes,9,opt,name=close,proto3,oneof"` +} + +type SyscallResponse_GetSockOpt struct { + GetSockOpt *GetSockOptResponse `protobuf:"bytes,10,opt,name=get_sock_opt,json=getSockOpt,proto3,oneof"` +} + +type SyscallResponse_SetSockOpt struct { + SetSockOpt *SetSockOptResponse `protobuf:"bytes,11,opt,name=set_sock_opt,json=setSockOpt,proto3,oneof"` +} + +type SyscallResponse_GetSockName struct { + GetSockName *GetSockNameResponse `protobuf:"bytes,12,opt,name=get_sock_name,json=getSockName,proto3,oneof"` +} + +type SyscallResponse_GetPeerName struct { + GetPeerName *GetPeerNameResponse `protobuf:"bytes,13,opt,name=get_peer_name,json=getPeerName,proto3,oneof"` +} + +type SyscallResponse_EpollWait struct { + EpollWait *EpollWaitResponse `protobuf:"bytes,14,opt,name=epoll_wait,json=epollWait,proto3,oneof"` +} + +type SyscallResponse_EpollCtl struct { + EpollCtl *EpollCtlResponse `protobuf:"bytes,15,opt,name=epoll_ctl,json=epollCtl,proto3,oneof"` +} + +type SyscallResponse_EpollCreate1 struct { + EpollCreate1 *EpollCreate1Response `protobuf:"bytes,16,opt,name=epoll_create1,json=epollCreate1,proto3,oneof"` +} + +type SyscallResponse_Poll struct { + Poll *PollResponse `protobuf:"bytes,17,opt,name=poll,proto3,oneof"` +} + +type SyscallResponse_Read struct { + Read *ReadResponse `protobuf:"bytes,18,opt,name=read,proto3,oneof"` +} + +type SyscallResponse_Write struct { + Write *WriteResponse `protobuf:"bytes,19,opt,name=write,proto3,oneof"` +} + +type SyscallResponse_Open struct { + Open *OpenResponse `protobuf:"bytes,20,opt,name=open,proto3,oneof"` +} + +type SyscallResponse_Ioctl struct { + Ioctl *IOCtlResponse `protobuf:"bytes,21,opt,name=ioctl,proto3,oneof"` +} + +type SyscallResponse_WriteFile struct { + WriteFile *WriteFileResponse `protobuf:"bytes,22,opt,name=write_file,json=writeFile,proto3,oneof"` +} + +type SyscallResponse_ReadFile struct { + ReadFile *ReadFileResponse `protobuf:"bytes,23,opt,name=read_file,json=readFile,proto3,oneof"` +} + +func (*SyscallResponse_Socket) isSyscallResponse_Result() {} + +func (*SyscallResponse_Sendmsg) isSyscallResponse_Result() {} + +func (*SyscallResponse_Recvmsg) isSyscallResponse_Result() {} + +func (*SyscallResponse_Bind) isSyscallResponse_Result() {} + +func (*SyscallResponse_Accept) isSyscallResponse_Result() {} + +func (*SyscallResponse_Connect) isSyscallResponse_Result() {} + +func (*SyscallResponse_Listen) isSyscallResponse_Result() {} + +func (*SyscallResponse_Shutdown) isSyscallResponse_Result() {} + +func (*SyscallResponse_Close) isSyscallResponse_Result() {} + +func (*SyscallResponse_GetSockOpt) isSyscallResponse_Result() {} + +func (*SyscallResponse_SetSockOpt) isSyscallResponse_Result() {} + +func (*SyscallResponse_GetSockName) isSyscallResponse_Result() {} + +func (*SyscallResponse_GetPeerName) isSyscallResponse_Result() {} + +func (*SyscallResponse_EpollWait) isSyscallResponse_Result() {} + +func (*SyscallResponse_EpollCtl) isSyscallResponse_Result() {} + +func (*SyscallResponse_EpollCreate1) isSyscallResponse_Result() {} + +func (*SyscallResponse_Poll) isSyscallResponse_Result() {} + +func (*SyscallResponse_Read) isSyscallResponse_Result() {} + +func (*SyscallResponse_Write) isSyscallResponse_Result() {} + +func (*SyscallResponse_Open) isSyscallResponse_Result() {} + +func (*SyscallResponse_Ioctl) isSyscallResponse_Result() {} + +func (*SyscallResponse_WriteFile) isSyscallResponse_Result() {} + +func (*SyscallResponse_ReadFile) isSyscallResponse_Result() {} + +func (m *SyscallResponse) GetResult() isSyscallResponse_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *SyscallResponse) GetSocket() *SocketResponse { + if x, ok := m.GetResult().(*SyscallResponse_Socket); ok { + return x.Socket + } + return nil +} + +func (m *SyscallResponse) GetSendmsg() *SendmsgResponse { + if x, ok := m.GetResult().(*SyscallResponse_Sendmsg); ok { + return x.Sendmsg + } + return nil +} + +func (m *SyscallResponse) GetRecvmsg() *RecvmsgResponse { + if x, ok := m.GetResult().(*SyscallResponse_Recvmsg); ok { + return x.Recvmsg + } + return nil +} + +func (m *SyscallResponse) GetBind() *BindResponse { + if x, ok := m.GetResult().(*SyscallResponse_Bind); ok { + return x.Bind + } + return nil +} + +func (m *SyscallResponse) GetAccept() *AcceptResponse { + if x, ok := m.GetResult().(*SyscallResponse_Accept); ok { + return x.Accept + } + return nil +} + +func (m *SyscallResponse) GetConnect() *ConnectResponse { + if x, ok := m.GetResult().(*SyscallResponse_Connect); ok { + return x.Connect + } + return nil +} + +func (m *SyscallResponse) GetListen() *ListenResponse { + if x, ok := m.GetResult().(*SyscallResponse_Listen); ok { + return x.Listen + } + return nil +} + +func (m *SyscallResponse) GetShutdown() *ShutdownResponse { + if x, ok := m.GetResult().(*SyscallResponse_Shutdown); ok { + return x.Shutdown + } + return nil +} + +func (m *SyscallResponse) GetClose() *CloseResponse { + if x, ok := m.GetResult().(*SyscallResponse_Close); ok { + return x.Close + } + return nil +} + +func (m *SyscallResponse) GetGetSockOpt() *GetSockOptResponse { + if x, ok := m.GetResult().(*SyscallResponse_GetSockOpt); ok { + return x.GetSockOpt + } + return nil +} + +func (m *SyscallResponse) GetSetSockOpt() *SetSockOptResponse { + if x, ok := m.GetResult().(*SyscallResponse_SetSockOpt); ok { + return x.SetSockOpt + } + return nil +} + +func (m *SyscallResponse) GetGetSockName() *GetSockNameResponse { + if x, ok := m.GetResult().(*SyscallResponse_GetSockName); ok { + return x.GetSockName + } + return nil +} + +func (m *SyscallResponse) GetGetPeerName() *GetPeerNameResponse { + if x, ok := m.GetResult().(*SyscallResponse_GetPeerName); ok { + return x.GetPeerName + } + return nil +} + +func (m *SyscallResponse) GetEpollWait() *EpollWaitResponse { + if x, ok := m.GetResult().(*SyscallResponse_EpollWait); ok { + return x.EpollWait + } + return nil +} + +func (m *SyscallResponse) GetEpollCtl() *EpollCtlResponse { + if x, ok := m.GetResult().(*SyscallResponse_EpollCtl); ok { + return x.EpollCtl + } + return nil +} + +func (m *SyscallResponse) GetEpollCreate1() *EpollCreate1Response { + if x, ok := m.GetResult().(*SyscallResponse_EpollCreate1); ok { + return x.EpollCreate1 + } + return nil +} + +func (m *SyscallResponse) GetPoll() *PollResponse { + if x, ok := m.GetResult().(*SyscallResponse_Poll); ok { + return x.Poll + } + return nil +} + +func (m *SyscallResponse) GetRead() *ReadResponse { + if x, ok := m.GetResult().(*SyscallResponse_Read); ok { + return x.Read + } + return nil +} + +func (m *SyscallResponse) GetWrite() *WriteResponse { + if x, ok := m.GetResult().(*SyscallResponse_Write); ok { + return x.Write + } + return nil +} + +func (m *SyscallResponse) GetOpen() *OpenResponse { + if x, ok := m.GetResult().(*SyscallResponse_Open); ok { + return x.Open + } + return nil +} + +func (m *SyscallResponse) GetIoctl() *IOCtlResponse { + if x, ok := m.GetResult().(*SyscallResponse_Ioctl); ok { + return x.Ioctl + } + return nil +} + +func (m *SyscallResponse) GetWriteFile() *WriteFileResponse { + if x, ok := m.GetResult().(*SyscallResponse_WriteFile); ok { + return x.WriteFile + } + return nil +} + +func (m *SyscallResponse) GetReadFile() *ReadFileResponse { + if x, ok := m.GetResult().(*SyscallResponse_ReadFile); ok { + return x.ReadFile + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SyscallResponse) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SyscallResponse_Socket)(nil), + (*SyscallResponse_Sendmsg)(nil), + (*SyscallResponse_Recvmsg)(nil), + (*SyscallResponse_Bind)(nil), + (*SyscallResponse_Accept)(nil), + (*SyscallResponse_Connect)(nil), + (*SyscallResponse_Listen)(nil), + (*SyscallResponse_Shutdown)(nil), + (*SyscallResponse_Close)(nil), + (*SyscallResponse_GetSockOpt)(nil), + (*SyscallResponse_SetSockOpt)(nil), + (*SyscallResponse_GetSockName)(nil), + (*SyscallResponse_GetPeerName)(nil), + (*SyscallResponse_EpollWait)(nil), + (*SyscallResponse_EpollCtl)(nil), + (*SyscallResponse_EpollCreate1)(nil), + (*SyscallResponse_Poll)(nil), + (*SyscallResponse_Read)(nil), + (*SyscallResponse_Write)(nil), + (*SyscallResponse_Open)(nil), + (*SyscallResponse_Ioctl)(nil), + (*SyscallResponse_WriteFile)(nil), + (*SyscallResponse_ReadFile)(nil), + } +} + +func init() { + proto.RegisterType((*SendmsgRequest)(nil), "syscall_rpc.SendmsgRequest") + proto.RegisterType((*SendmsgResponse)(nil), "syscall_rpc.SendmsgResponse") + proto.RegisterType((*IOCtlRequest)(nil), "syscall_rpc.IOCtlRequest") + proto.RegisterType((*IOCtlResponse)(nil), "syscall_rpc.IOCtlResponse") + proto.RegisterType((*RecvmsgRequest)(nil), "syscall_rpc.RecvmsgRequest") + proto.RegisterType((*OpenRequest)(nil), "syscall_rpc.OpenRequest") + proto.RegisterType((*OpenResponse)(nil), "syscall_rpc.OpenResponse") + proto.RegisterType((*ReadRequest)(nil), "syscall_rpc.ReadRequest") + proto.RegisterType((*ReadResponse)(nil), "syscall_rpc.ReadResponse") + proto.RegisterType((*ReadFileRequest)(nil), "syscall_rpc.ReadFileRequest") + proto.RegisterType((*ReadFileResponse)(nil), "syscall_rpc.ReadFileResponse") + proto.RegisterType((*WriteRequest)(nil), "syscall_rpc.WriteRequest") + proto.RegisterType((*WriteResponse)(nil), "syscall_rpc.WriteResponse") + proto.RegisterType((*WriteFileRequest)(nil), "syscall_rpc.WriteFileRequest") + proto.RegisterType((*WriteFileResponse)(nil), "syscall_rpc.WriteFileResponse") + proto.RegisterType((*AddressResponse)(nil), "syscall_rpc.AddressResponse") + proto.RegisterType((*RecvmsgResponse)(nil), "syscall_rpc.RecvmsgResponse") + proto.RegisterType((*RecvmsgResponse_ResultPayload)(nil), "syscall_rpc.RecvmsgResponse.ResultPayload") + proto.RegisterType((*BindRequest)(nil), "syscall_rpc.BindRequest") + proto.RegisterType((*BindResponse)(nil), "syscall_rpc.BindResponse") + proto.RegisterType((*AcceptRequest)(nil), "syscall_rpc.AcceptRequest") + proto.RegisterType((*AcceptResponse)(nil), "syscall_rpc.AcceptResponse") + proto.RegisterType((*AcceptResponse_ResultPayload)(nil), "syscall_rpc.AcceptResponse.ResultPayload") + proto.RegisterType((*ConnectRequest)(nil), "syscall_rpc.ConnectRequest") + proto.RegisterType((*ConnectResponse)(nil), "syscall_rpc.ConnectResponse") + proto.RegisterType((*ListenRequest)(nil), "syscall_rpc.ListenRequest") + proto.RegisterType((*ListenResponse)(nil), "syscall_rpc.ListenResponse") + proto.RegisterType((*ShutdownRequest)(nil), "syscall_rpc.ShutdownRequest") + proto.RegisterType((*ShutdownResponse)(nil), "syscall_rpc.ShutdownResponse") + proto.RegisterType((*CloseRequest)(nil), "syscall_rpc.CloseRequest") + proto.RegisterType((*CloseResponse)(nil), "syscall_rpc.CloseResponse") + proto.RegisterType((*GetSockOptRequest)(nil), "syscall_rpc.GetSockOptRequest") + proto.RegisterType((*GetSockOptResponse)(nil), "syscall_rpc.GetSockOptResponse") + proto.RegisterType((*SetSockOptRequest)(nil), "syscall_rpc.SetSockOptRequest") + proto.RegisterType((*SetSockOptResponse)(nil), "syscall_rpc.SetSockOptResponse") + proto.RegisterType((*GetSockNameRequest)(nil), "syscall_rpc.GetSockNameRequest") + proto.RegisterType((*GetSockNameResponse)(nil), "syscall_rpc.GetSockNameResponse") + proto.RegisterType((*GetPeerNameRequest)(nil), "syscall_rpc.GetPeerNameRequest") + proto.RegisterType((*GetPeerNameResponse)(nil), "syscall_rpc.GetPeerNameResponse") + proto.RegisterType((*SocketRequest)(nil), "syscall_rpc.SocketRequest") + proto.RegisterType((*SocketResponse)(nil), "syscall_rpc.SocketResponse") + proto.RegisterType((*EpollWaitRequest)(nil), "syscall_rpc.EpollWaitRequest") + proto.RegisterType((*EpollEvent)(nil), "syscall_rpc.EpollEvent") + proto.RegisterType((*EpollEvents)(nil), "syscall_rpc.EpollEvents") + proto.RegisterType((*EpollWaitResponse)(nil), "syscall_rpc.EpollWaitResponse") + proto.RegisterType((*EpollCtlRequest)(nil), "syscall_rpc.EpollCtlRequest") + proto.RegisterType((*EpollCtlResponse)(nil), "syscall_rpc.EpollCtlResponse") + proto.RegisterType((*EpollCreate1Request)(nil), "syscall_rpc.EpollCreate1Request") + proto.RegisterType((*EpollCreate1Response)(nil), "syscall_rpc.EpollCreate1Response") + proto.RegisterType((*PollRequest)(nil), "syscall_rpc.PollRequest") + proto.RegisterType((*PollResponse)(nil), "syscall_rpc.PollResponse") + proto.RegisterType((*SyscallRequest)(nil), "syscall_rpc.SyscallRequest") + proto.RegisterType((*SyscallResponse)(nil), "syscall_rpc.SyscallResponse") +} + +func init() { + proto.RegisterFile("pkg/sentry/socket/rpcinet/syscall_rpc.proto", fileDescriptor_dd04f3a8f0c5288b) +} + +var fileDescriptor_dd04f3a8f0c5288b = []byte{ + // 1838 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x98, 0xdd, 0x52, 0xe3, 0xc8, + 0x15, 0x80, 0x6d, 0x6c, 0xc0, 0x1c, 0xff, 0xa2, 0x21, 0xac, 0xf8, 0x67, 0x95, 0xa4, 0x8a, 0x4d, + 0x2a, 0xb8, 0xc6, 0x33, 0xec, 0x90, 0xdd, 0xad, 0xdd, 0x2c, 0x04, 0xd6, 0x53, 0x99, 0x1a, 0x88, + 0x5c, 0x09, 0xa9, 0xe4, 0xc2, 0x25, 0xa4, 0xb6, 0x71, 0x21, 0x4b, 0x8a, 0x24, 0x43, 0x71, 0x93, + 0x07, 0xc8, 0x75, 0xee, 0x72, 0x91, 0x87, 0xca, 0x03, 0xe4, 0x09, 0xf2, 0x0e, 0xa9, 0xd3, 0xdd, + 0x92, 0xba, 0x45, 0x8b, 0xc1, 0x29, 0x6a, 0xef, 0xd4, 0xad, 0xf3, 0xd7, 0xe7, 0x74, 0x7f, 0x3a, + 0x2d, 0xf8, 0x65, 0x70, 0x3b, 0xee, 0x46, 0xc4, 0x8b, 0xc3, 0x87, 0x6e, 0xe4, 0xdb, 0xb7, 0x24, + 0xee, 0x86, 0x81, 0x3d, 0xf1, 0x48, 0xdc, 0x8d, 0x1e, 0x22, 0xdb, 0x72, 0xdd, 0x61, 0x18, 0xd8, + 0x87, 0x41, 0xe8, 0xc7, 0xbe, 0x56, 0x17, 0xa6, 0x8c, 0xbf, 0x97, 0xa1, 0x35, 0x20, 0x9e, 0x33, + 0x8d, 0xc6, 0x26, 0xf9, 0xeb, 0x8c, 0x44, 0xb1, 0xd6, 0x82, 0x85, 0x91, 0xa3, 0x97, 0xf7, 0xcb, + 0x07, 0x4d, 0x73, 0x61, 0xe4, 0x68, 0xeb, 0x50, 0x75, 0xac, 0xd8, 0xd2, 0x17, 0xf6, 0xcb, 0x07, + 0x8d, 0x93, 0x85, 0x5a, 0xd9, 0xa4, 0x63, 0x4d, 0x87, 0x65, 0xcb, 0x71, 0x42, 0x12, 0x45, 0x7a, + 0x05, 0x5f, 0x99, 0xc9, 0x50, 0xd3, 0xa0, 0x3a, 0xf5, 0x43, 0xa2, 0x57, 0xf7, 0xcb, 0x07, 0x35, + 0x93, 0x3e, 0x6b, 0x06, 0x34, 0x89, 0xe7, 0x0c, 0xfd, 0xd1, 0x30, 0x24, 0xb6, 0x1f, 0x3a, 0xfa, + 0x22, 0x7d, 0x59, 0x27, 0x9e, 0x73, 0x31, 0x32, 0xe9, 0x94, 0xf1, 0x67, 0x68, 0xa7, 0xb1, 0x44, + 0x81, 0xef, 0x45, 0x44, 0xfb, 0x29, 0x34, 0x48, 0x18, 0xfa, 0xe1, 0xd0, 0x9b, 0x4d, 0xaf, 0x49, + 0xc8, 0xc2, 0xea, 0x97, 0xcc, 0x3a, 0x9d, 0xfd, 0x48, 0x27, 0x35, 0x1d, 0x96, 0x5c, 0xe2, 0x8d, + 0xe3, 0x1b, 0x1a, 0x23, 0xbe, 0xe6, 0xe3, 0x93, 0x1a, 0x2c, 0x85, 0x24, 0x9a, 0xb9, 0xb1, 0x71, + 0x02, 0x8d, 0xf7, 0x17, 0xa7, 0xb1, 0x5b, 0xb4, 0xca, 0x0e, 0x54, 0xec, 0xa9, 0xc3, 0x0c, 0x98, + 0xf8, 0x88, 0x33, 0x56, 0x38, 0xe6, 0x6b, 0xc3, 0x47, 0xe3, 0x8f, 0xd0, 0xe4, 0x36, 0xe6, 0x89, + 0x6e, 0x1d, 0x16, 0xef, 0x2c, 0x77, 0x46, 0x58, 0x02, 0xfb, 0x25, 0x93, 0x0d, 0x85, 0xd8, 0xfe, + 0x59, 0x86, 0x96, 0x49, 0xec, 0xbb, 0x27, 0x8b, 0x20, 0x2d, 0x31, 0x59, 0x20, 0xce, 0x47, 0xc4, + 0x73, 0x48, 0x48, 0xe3, 0xac, 0x99, 0x7c, 0x84, 0x25, 0x08, 0x08, 0xb9, 0x4d, 0x4a, 0x80, 0xcf, + 0xda, 0x1a, 0x2c, 0xc6, 0xe1, 0xcc, 0xb3, 0x79, 0xea, 0xd9, 0x40, 0xdb, 0x83, 0xba, 0x3d, 0x8d, + 0xc6, 0x43, 0x6e, 0x7e, 0x89, 0x9a, 0x07, 0x9c, 0xfa, 0x40, 0x67, 0x8c, 0xdf, 0x41, 0xfd, 0x22, + 0x20, 0x5e, 0x12, 0x19, 0x5a, 0xb6, 0xe2, 0x1b, 0x1a, 0x5b, 0xc3, 0xa4, 0xcf, 0x68, 0x79, 0xe4, + 0x5a, 0xe3, 0x88, 0x07, 0xc7, 0x06, 0x6c, 0x1b, 0x38, 0x84, 0x46, 0xd6, 0x34, 0xe9, 0xb3, 0x71, + 0x01, 0x0d, 0x66, 0x6c, 0x9e, 0x0c, 0x76, 0x68, 0x32, 0x92, 0xda, 0x2e, 0x8c, 0x1c, 0x21, 0x77, + 0x47, 0x50, 0x37, 0x89, 0xe5, 0xcc, 0x99, 0x37, 0xe3, 0x0a, 0x1a, 0x4c, 0x6d, 0xbe, 0x7d, 0x96, + 0x3b, 0x09, 0xfd, 0x12, 0x3b, 0x0b, 0x42, 0x3c, 0x3f, 0x87, 0x36, 0x1a, 0x3e, 0x9f, 0xb8, 0x44, + 0x95, 0xb1, 0x15, 0x96, 0x31, 0xe3, 0x2f, 0xd0, 0xc9, 0xc4, 0x5e, 0x3a, 0x86, 0x2f, 0xa1, 0x71, + 0x15, 0x4e, 0x62, 0x32, 0xe7, 0x89, 0x36, 0xfe, 0x04, 0x4d, 0xae, 0xf7, 0xd2, 0xa7, 0xef, 0x37, + 0xd0, 0xa1, 0x96, 0x3f, 0x91, 0x16, 0x64, 0x8a, 0xed, 0x7b, 0x31, 0xf1, 0x62, 0x16, 0x9c, 0x99, + 0x0c, 0x8d, 0x4b, 0x58, 0x15, 0x2c, 0xf0, 0xf8, 0x3e, 0x57, 0xc5, 0x97, 0x8f, 0x6e, 0xf9, 0x3e, + 0x9c, 0xc4, 0x31, 0xf1, 0xf8, 0x0e, 0x48, 0x86, 0xc6, 0x29, 0xb4, 0xbf, 0x67, 0xc0, 0x4a, 0xed, + 0x09, 0x48, 0x2b, 0xcb, 0x48, 0x2b, 0xda, 0x47, 0xff, 0x5a, 0xc0, 0x7a, 0xf3, 0xa3, 0x3b, 0x4f, + 0xd6, 0xce, 0x61, 0x39, 0xb0, 0x1e, 0x5c, 0xdf, 0x62, 0x1b, 0xbb, 0xde, 0xfb, 0xc5, 0xa1, 0x88, + 0xea, 0x9c, 0xcd, 0x43, 0x93, 0xe6, 0xf1, 0x92, 0x69, 0xf4, 0x4b, 0x66, 0xa2, 0xbc, 0xf9, 0x8f, + 0x32, 0x34, 0xa5, 0x97, 0x69, 0x75, 0xcb, 0x39, 0x5e, 0x7f, 0x99, 0x2d, 0x8e, 0x79, 0xdc, 0x96, + 0x3c, 0xe6, 0x72, 0xa1, 0x5a, 0x7a, 0x45, 0x42, 0xcf, 0x16, 0xac, 0x50, 0x70, 0x50, 0x67, 0x55, + 0x9a, 0xae, 0x1a, 0x4e, 0xfc, 0x56, 0xde, 0x8c, 0xef, 0xa0, 0x7e, 0x32, 0xf1, 0x0a, 0x0f, 0xa8, + 0x2e, 0x47, 0x95, 0xa5, 0xdc, 0x78, 0x0d, 0x0d, 0xa6, 0xf8, 0xec, 0x62, 0x1b, 0xef, 0xa1, 0xf9, + 0xbd, 0x6d, 0x93, 0x20, 0x2e, 0xf2, 0xc6, 0xb0, 0x18, 0x52, 0x57, 0x0c, 0x8b, 0x61, 0x06, 0x2f, + 0x5c, 0x5e, 0x85, 0xc3, 0xcb, 0xf8, 0x4f, 0x19, 0x5a, 0x89, 0xad, 0x79, 0xea, 0x7a, 0x96, 0xaf, + 0xeb, 0x17, 0x72, 0x96, 0x25, 0x93, 0xc5, 0x65, 0xbd, 0xca, 0x57, 0x35, 0xbf, 0x92, 0xff, 0xb3, + 0x9a, 0x42, 0x61, 0xbe, 0x82, 0xd6, 0xa9, 0xef, 0x79, 0xc4, 0x8e, 0xe7, 0xaf, 0xcd, 0x5b, 0x68, + 0xa7, 0xba, 0xcf, 0x2f, 0xcf, 0xaf, 0xa1, 0xf9, 0x61, 0x12, 0xc5, 0xd9, 0xb7, 0x44, 0xe1, 0xf0, + 0xda, 0xb2, 0x6f, 0x5d, 0x7f, 0x4c, 0x1d, 0x56, 0xcc, 0x64, 0x68, 0xbc, 0x81, 0x56, 0xa2, 0xfa, + 0x7c, 0x7f, 0x6f, 0xa0, 0x3d, 0xb8, 0x99, 0xc5, 0x8e, 0x7f, 0xef, 0x3d, 0xf1, 0xd9, 0xbf, 0xf1, + 0xef, 0xb9, 0x37, 0x7c, 0x34, 0x8e, 0xa0, 0x93, 0x29, 0x3d, 0xdf, 0xd7, 0x2e, 0x34, 0x4e, 0x5d, + 0x3f, 0x2a, 0x62, 0xae, 0xd1, 0x83, 0x26, 0x7f, 0xff, 0x7c, 0x9b, 0x04, 0x56, 0x7f, 0x20, 0xf1, + 0xc0, 0xb7, 0x6f, 0x2f, 0x8a, 0xb7, 0xf4, 0x1a, 0x2c, 0xba, 0xe4, 0x8e, 0xb8, 0x7c, 0x0d, 0x6c, + 0x80, 0x1b, 0xdd, 0xb3, 0xa6, 0x84, 0xef, 0x69, 0xfa, 0x2c, 0x1c, 0xe4, 0x6a, 0xee, 0x5b, 0xa8, + 0x89, 0x6e, 0xe6, 0xd9, 0xed, 0x1a, 0x54, 0xfc, 0x20, 0x4e, 0x3b, 0x1b, 0x1c, 0x08, 0x3b, 0x6c, + 0x08, 0xab, 0x83, 0x17, 0x8c, 0xbf, 0xc3, 0x9c, 0x31, 0xd4, 0xe0, 0xa3, 0xf1, 0x0e, 0xb4, 0xc1, + 0xe3, 0xc8, 0x9f, 0x91, 0xd9, 0x9f, 0xa5, 0x4b, 0xfe, 0x68, 0x4d, 0x0b, 0x6b, 0xf6, 0x37, 0x78, + 0x25, 0x49, 0xcd, 0x93, 0x99, 0xe3, 0xb9, 0xce, 0x27, 0x1e, 0xfd, 0xc7, 0x27, 0x94, 0x45, 0x79, + 0x49, 0x48, 0xf8, 0xe9, 0x28, 0x33, 0xa9, 0x1f, 0x3b, 0xca, 0x2b, 0x68, 0x0e, 0xe8, 0x9d, 0x23, + 0x09, 0x70, 0x1d, 0x96, 0x46, 0xd6, 0x74, 0xe2, 0x3e, 0x50, 0x9f, 0x15, 0x93, 0x8f, 0xb0, 0xa6, + 0xf1, 0x43, 0x40, 0x78, 0xa1, 0xe9, 0xb3, 0xb6, 0x09, 0x35, 0x7a, 0x2b, 0xb1, 0x7d, 0x97, 0xd7, + 0x3a, 0x1d, 0x1b, 0xbf, 0x87, 0x56, 0x62, 0xf8, 0xa5, 0xba, 0xc5, 0x3f, 0x40, 0xe7, 0x2c, 0xf0, + 0x5d, 0xf7, 0xca, 0x9a, 0x14, 0x6e, 0xc8, 0x1d, 0x00, 0x6f, 0x36, 0x1d, 0x92, 0x3b, 0xe2, 0xc5, + 0x49, 0x47, 0xbb, 0xe2, 0xcd, 0xa6, 0x67, 0x74, 0x82, 0x76, 0xb5, 0x11, 0xb1, 0x69, 0xb4, 0x9a, + 0x49, 0x9f, 0x8d, 0xb7, 0x00, 0xd4, 0x2c, 0x15, 0x51, 0xf5, 0xa0, 0x92, 0x31, 0x3e, 0x32, 0xbe, + 0x85, 0x7a, 0xa6, 0x15, 0x69, 0xdd, 0x54, 0xac, 0xbc, 0x5f, 0x39, 0xa8, 0xf7, 0x3e, 0x93, 0x4a, + 0x91, 0x49, 0xa6, 0xfa, 0x77, 0xb0, 0x2a, 0x2c, 0x66, 0x9e, 0x14, 0xf5, 0xa4, 0x88, 0xea, 0x3d, + 0xbd, 0xc0, 0x55, 0x84, 0xcd, 0x1c, 0x93, 0x14, 0x92, 0x18, 0x43, 0x9b, 0x8a, 0x08, 0xb7, 0x29, + 0x0d, 0xaa, 0x24, 0x48, 0x17, 0x4d, 0x9f, 0x31, 0x0d, 0x7e, 0xc0, 0x8b, 0xbd, 0xe0, 0x07, 0x3c, + 0x2d, 0x95, 0x34, 0x2d, 0xbf, 0x82, 0x45, 0x6a, 0x9a, 0x1e, 0xe8, 0x27, 0x96, 0xcb, 0xa4, 0x90, + 0xcb, 0x99, 0xd7, 0xe7, 0x9f, 0xf4, 0x2f, 0xe0, 0x15, 0x53, 0x0b, 0x89, 0x15, 0x93, 0xd7, 0x42, + 0xc0, 0xf8, 0x9d, 0xe7, 0x3b, 0x94, 0x3e, 0x1b, 0x57, 0xb0, 0x26, 0x8b, 0xbe, 0xe0, 0x1d, 0xe5, + 0xd2, 0x77, 0xdd, 0x27, 0xee, 0x28, 0xca, 0xfd, 0x71, 0x05, 0x0d, 0xa6, 0x36, 0x67, 0x37, 0x2e, + 0x1a, 0x53, 0x16, 0xf0, 0xdf, 0x00, 0xad, 0x01, 0x4b, 0x76, 0x12, 0xd3, 0x5b, 0x58, 0x62, 0x3f, + 0x0e, 0xa8, 0xd5, 0x7a, 0x6f, 0x53, 0xaa, 0x86, 0x74, 0xbe, 0xd1, 0x24, 0x93, 0xd5, 0xde, 0xc1, + 0x72, 0xc4, 0x2e, 0xec, 0x7c, 0x23, 0x6d, 0xc9, 0x6a, 0xd2, 0x8f, 0x05, 0xa4, 0x07, 0x97, 0x46, + 0xc5, 0x90, 0x75, 0xb8, 0x74, 0x43, 0xe4, 0x15, 0xe5, 0xcb, 0x30, 0x2a, 0x72, 0x69, 0xed, 0x10, + 0xaa, 0xd7, 0x13, 0xcf, 0xe1, 0x7b, 0x46, 0xde, 0xb7, 0x42, 0x9b, 0x89, 0x97, 0x22, 0x94, 0xc3, + 0x75, 0x59, 0xb4, 0xe5, 0xa2, 0x97, 0xde, 0xfc, 0xba, 0xa4, 0x66, 0x11, 0xd7, 0xc5, 0x64, 0x31, + 0x3c, 0x9b, 0xb5, 0x37, 0xf4, 0x3e, 0x9c, 0x0f, 0x4f, 0x6e, 0x9b, 0x30, 0x3c, 0x2e, 0x8d, 0xee, + 0x5c, 0xda, 0xa6, 0xe8, 0xcb, 0x0a, 0x77, 0x52, 0xf3, 0x43, 0xef, 0x49, 0x74, 0x42, 0xfb, 0x0a, + 0x6a, 0x11, 0x6f, 0x39, 0xf4, 0x9a, 0x02, 0xc3, 0xb9, 0x26, 0xa6, 0x5f, 0x32, 0x53, 0x79, 0xed, + 0x35, 0x2c, 0xda, 0xd8, 0x57, 0xe8, 0x2b, 0x54, 0x71, 0x43, 0x0e, 0x54, 0xe8, 0x48, 0xfa, 0x25, + 0x93, 0x49, 0x6a, 0x27, 0xd0, 0x18, 0x93, 0x78, 0x88, 0x35, 0x1c, 0xe2, 0x07, 0x15, 0xa8, 0xe6, + 0xae, 0xa4, 0xf9, 0xa8, 0xef, 0xe8, 0x97, 0x4c, 0x18, 0xa7, 0x93, 0x68, 0x23, 0x12, 0x6d, 0xd4, + 0x15, 0x36, 0x06, 0x2a, 0x1b, 0x51, 0x66, 0xe3, 0x0c, 0x9a, 0x69, 0x1c, 0xf4, 0x63, 0xdf, 0xa0, + 0x46, 0xf6, 0x54, 0x81, 0x08, 0x1f, 0x40, 0xdc, 0xf1, 0xe3, 0x6c, 0x36, 0x31, 0x83, 0xbd, 0x3c, + 0x33, 0xd3, 0x54, 0x9b, 0xc9, 0x7d, 0x47, 0xb9, 0x99, 0x64, 0x56, 0xfb, 0x16, 0x80, 0xe0, 0xe9, + 0x1f, 0xde, 0x5b, 0x93, 0x58, 0x6f, 0x51, 0x1b, 0x3b, 0x8f, 0x99, 0x24, 0x7c, 0x39, 0xfa, 0x25, + 0x73, 0x85, 0x24, 0x73, 0xda, 0xd7, 0xc0, 0x06, 0x43, 0x3b, 0x76, 0xf5, 0xb6, 0xa2, 0x8a, 0x39, + 0x66, 0x62, 0x15, 0x09, 0x9f, 0xd2, 0x7e, 0x80, 0x26, 0x57, 0x66, 0xec, 0xd1, 0x3b, 0xd4, 0xc0, + 0xbe, 0xc2, 0x80, 0xc4, 0xb1, 0x7e, 0xc9, 0x6c, 0x10, 0x61, 0x1a, 0xcf, 0x07, 0x0e, 0xf5, 0x55, + 0xc5, 0xf9, 0x10, 0x18, 0x84, 0xe7, 0x03, 0xe5, 0x50, 0x3e, 0x24, 0x96, 0xa3, 0x6b, 0x0a, 0x79, + 0xe1, 0xbf, 0x0a, 0xca, 0xa3, 0x1c, 0x6e, 0x37, 0xbc, 0x3f, 0x13, 0xfd, 0x95, 0x62, 0xbb, 0x89, + 0x3f, 0x1d, 0x70, 0xbb, 0x51, 0x49, 0x74, 0xe1, 0x07, 0xc4, 0xd3, 0xd7, 0x14, 0x2e, 0x84, 0x1f, + 0x4b, 0xe8, 0x02, 0xe5, 0xd0, 0xc5, 0xc4, 0xc7, 0x24, 0xfe, 0x44, 0xe1, 0x42, 0xfc, 0x87, 0x87, + 0x2e, 0xa8, 0x24, 0xd6, 0x8e, 0xfa, 0x1a, 0x8e, 0x26, 0x2e, 0xd1, 0xd7, 0x15, 0xb5, 0xcb, 0xff, + 0x7d, 0xc0, 0xda, 0xdd, 0x27, 0x73, 0x58, 0x3b, 0x5c, 0x1d, 0x53, 0xff, 0x4c, 0x51, 0xbb, 0xdc, + 0x2f, 0x1d, 0xac, 0x5d, 0xc8, 0xa7, 0x4e, 0x96, 0xa0, 0x6a, 0x85, 0xe3, 0xc8, 0xf8, 0x2f, 0x40, + 0x3b, 0xa5, 0x2a, 0x47, 0xf6, 0x51, 0x0e, 0xab, 0x5b, 0x4a, 0xac, 0xa6, 0xdd, 0x55, 0xc2, 0xd5, + 0xe3, 0x3c, 0x57, 0xb7, 0xd5, 0x5c, 0xcd, 0xda, 0xb2, 0x04, 0xac, 0xc7, 0x79, 0xb0, 0x6e, 0x3f, + 0xf5, 0x5b, 0x41, 0x24, 0x6b, 0x57, 0x22, 0xeb, 0x86, 0x82, 0xac, 0xa9, 0x0e, 0x43, 0xeb, 0x51, + 0x0e, 0xad, 0x5b, 0x4f, 0x5c, 0x74, 0x05, 0xb6, 0x1e, 0xe7, 0xd9, 0xba, 0xad, 0x66, 0x6b, 0x16, + 0x61, 0x02, 0xd7, 0xa3, 0x1c, 0x5c, 0xb7, 0x94, 0x70, 0xcd, 0x1c, 0x72, 0xba, 0x7e, 0xfd, 0x88, + 0xae, 0x3b, 0x05, 0x74, 0x4d, 0x55, 0x33, 0xbc, 0xf6, 0x64, 0xbc, 0x6e, 0xaa, 0xf0, 0x9a, 0xaa, + 0x71, 0xbe, 0x9e, 0x2a, 0xf9, 0xba, 0x57, 0xc8, 0xd7, 0x54, 0x5f, 0x04, 0xec, 0xa9, 0x12, 0xb0, + 0x7b, 0x85, 0x80, 0xcd, 0x8c, 0x08, 0x84, 0x3d, 0x57, 0x13, 0x76, 0xbf, 0x98, 0xb0, 0xa9, 0x19, + 0x09, 0xb1, 0xe7, 0x6a, 0xc4, 0xee, 0x17, 0x23, 0x56, 0xb2, 0x93, 0x32, 0xf6, 0x3b, 0x05, 0x63, + 0x77, 0x8b, 0x18, 0x9b, 0x9a, 0x10, 0x20, 0xfb, 0xcd, 0x63, 0xc8, 0xee, 0x14, 0x40, 0x36, 0x2b, + 0x66, 0x4a, 0xd9, 0xbe, 0x9a, 0xb2, 0x9f, 0x3f, 0x41, 0xd9, 0xd4, 0x8a, 0x8c, 0xd9, 0xae, 0x84, + 0xd9, 0x0d, 0x05, 0x66, 0xb3, 0xc3, 0x42, 0x39, 0xdb, 0x95, 0x38, 0xbb, 0xa1, 0xe0, 0x6c, 0xa6, + 0x40, 0x41, 0xdb, 0x93, 0x41, 0xbb, 0xa9, 0x02, 0x6d, 0xb6, 0xf1, 0x18, 0x69, 0xbb, 0x12, 0x69, + 0x37, 0x14, 0xa4, 0xcd, 0x9c, 0x50, 0xd4, 0xf6, 0x64, 0xd4, 0x6e, 0xaa, 0x50, 0x9b, 0x39, 0x61, + 0xac, 0xfd, 0x4e, 0xc1, 0xda, 0xdd, 0x22, 0xd6, 0x66, 0x35, 0xcc, 0x60, 0xfb, 0xcd, 0x63, 0xd8, + 0xee, 0x14, 0xc0, 0x36, 0xab, 0x61, 0x4a, 0xdb, 0xb4, 0x8b, 0xbd, 0x5e, 0xa2, 0x17, 0xc5, 0x37, + 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x22, 0x29, 0x56, 0xfd, 0x1a, 0x00, 0x00, +} diff --git a/pkg/sentry/socket/socket.go b/pkg/sentry/socket/socket.go new file mode 100644 index 000000000..9393acd28 --- /dev/null +++ b/pkg/sentry/socket/socket.go @@ -0,0 +1,336 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package socket provides the interfaces that need to be provided by socket +// implementations and providers, as well as per family demultiplexing of socket +// creation. +package socket + +import ( + "fmt" + "sync/atomic" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/device" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip" +) + +// ControlMessages represents the union of unix control messages and tcpip +// control messages. +type ControlMessages struct { + Unix transport.ControlMessages + IP tcpip.ControlMessages +} + +// Socket is the interface containing socket syscalls used by the syscall layer +// to redirect them to the appropriate implementation. +type Socket interface { + fs.FileOperations + + // Connect implements the connect(2) linux syscall. + Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error + + // Accept implements the accept4(2) linux syscall. + // Returns fd, real peer address length and error. Real peer address + // length is only set if len(peer) > 0. + Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) + + // Bind implements the bind(2) linux syscall. + Bind(t *kernel.Task, sockaddr []byte) *syserr.Error + + // Listen implements the listen(2) linux syscall. + Listen(t *kernel.Task, backlog int) *syserr.Error + + // Shutdown implements the shutdown(2) linux syscall. + Shutdown(t *kernel.Task, how int) *syserr.Error + + // GetSockOpt implements the getsockopt(2) linux syscall. + GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) + + // SetSockOpt implements the setsockopt(2) linux syscall. + SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error + + // GetSockName implements the getsockname(2) linux syscall. + // + // addrLen is the address length to be returned to the application, not + // necessarily the actual length of the address. + GetSockName(t *kernel.Task) (addr interface{}, addrLen uint32, err *syserr.Error) + + // GetPeerName implements the getpeername(2) linux syscall. + // + // addrLen is the address length to be returned to the application, not + // necessarily the actual length of the address. + GetPeerName(t *kernel.Task) (addr interface{}, addrLen uint32, err *syserr.Error) + + // RecvMsg implements the recvmsg(2) linux syscall. + // + // senderAddrLen is the address length to be returned to the application, + // not necessarily the actual length of the address. + // + // flags control how RecvMsg should be completed. msgFlags indicate how + // the RecvMsg call was completed. Note that control message truncation + // may still be required even if the MSG_CTRUNC bit is not set in + // msgFlags. In that case, the caller should set MSG_CTRUNC appropriately. + // + // If err != nil, the recv was not successful. + RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (n int, msgFlags int, senderAddr interface{}, senderAddrLen uint32, controlMessages ControlMessages, err *syserr.Error) + + // SendMsg implements the sendmsg(2) linux syscall. SendMsg does not take + // ownership of the ControlMessage on error. + // + // If n > 0, err will either be nil or an error from t.Block. + SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages ControlMessages) (n int, err *syserr.Error) + + // SetRecvTimeout sets the timeout (in ns) for recv operations. Zero means + // no timeout, and negative means DONTWAIT. + SetRecvTimeout(nanoseconds int64) + + // RecvTimeout gets the current timeout (in ns) for recv operations. Zero + // means no timeout, and negative means DONTWAIT. + RecvTimeout() int64 + + // SetSendTimeout sets the timeout (in ns) for send operations. Zero means + // no timeout, and negative means DONTWAIT. + SetSendTimeout(nanoseconds int64) + + // SendTimeout gets the current timeout (in ns) for send operations. Zero + // means no timeout, and negative means DONTWAIT. + SendTimeout() int64 +} + +// Provider is the interface implemented by providers of sockets for specific +// address families (e.g., AF_INET). +type Provider interface { + // Socket creates a new socket. + // + // If a nil Socket _and_ a nil error is returned, it means that the + // protocol is not supported. A non-nil error should only be returned + // if the protocol is supported, but an error occurs during creation. + Socket(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *syserr.Error) + + // Pair creates a pair of connected sockets. + // + // See Socket for error information. + Pair(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) +} + +// families holds a map of all known address families and their providers. +var families = make(map[int][]Provider) + +// RegisterProvider registers the provider of a given address family so that +// sockets of that type can be created via socket() and/or socketpair() +// syscalls. +func RegisterProvider(family int, provider Provider) { + families[family] = append(families[family], provider) +} + +// New creates a new socket with the given family, type and protocol. +func New(t *kernel.Task, family int, stype transport.SockType, protocol int) (*fs.File, *syserr.Error) { + for _, p := range families[family] { + s, err := p.Socket(t, stype, protocol) + if err != nil { + return nil, err + } + if s != nil { + t.Kernel().RecordSocket(s, family) + return s, nil + } + } + + return nil, syserr.ErrAddressFamilyNotSupported +} + +// Pair creates a new connected socket pair with the given family, type and +// protocol. +func Pair(t *kernel.Task, family int, stype transport.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) { + providers, ok := families[family] + if !ok { + return nil, nil, syserr.ErrAddressFamilyNotSupported + } + + for _, p := range providers { + s1, s2, err := p.Pair(t, stype, protocol) + if err != nil { + return nil, nil, err + } + if s1 != nil && s2 != nil { + k := t.Kernel() + k.RecordSocket(s1, family) + k.RecordSocket(s2, family) + return s1, s2, nil + } + } + + return nil, nil, syserr.ErrSocketNotSupported +} + +// NewDirent returns a sockfs fs.Dirent that resides on device d. +func NewDirent(ctx context.Context, d *device.Device) *fs.Dirent { + ino := d.NextIno() + iops := &fsutil.SimpleFileInode{ + InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, fs.FileOwnerFromContext(ctx), fs.FilePermissions{ + User: fs.PermMask{Read: true, Write: true}, + }, linux.SOCKFS_MAGIC), + } + inode := fs.NewInode(iops, fs.NewPseudoMountSource(), fs.StableAttr{ + Type: fs.Socket, + DeviceID: d.DeviceID(), + InodeID: ino, + BlockSize: usermem.PageSize, + }) + + // Dirent name matches net/socket.c:sockfs_dname. + return fs.NewDirent(inode, fmt.Sprintf("socket:[%d]", ino)) +} + +// SendReceiveTimeout stores timeouts for send and receive calls. +// +// It is meant to be embedded into Socket implementations to help satisfy the +// interface. +// +// Care must be taken when copying SendReceiveTimeout as it contains atomic +// variables. +// +// +stateify savable +type SendReceiveTimeout struct { + // send is length of the send timeout in nanoseconds. + // + // send must be accessed atomically. + send int64 + + // recv is length of the receive timeout in nanoseconds. + // + // recv must be accessed atomically. + recv int64 +} + +// SetRecvTimeout implements Socket.SetRecvTimeout. +func (to *SendReceiveTimeout) SetRecvTimeout(nanoseconds int64) { + atomic.StoreInt64(&to.recv, nanoseconds) +} + +// RecvTimeout implements Socket.RecvTimeout. +func (to *SendReceiveTimeout) RecvTimeout() int64 { + return atomic.LoadInt64(&to.recv) +} + +// SetSendTimeout implements Socket.SetSendTimeout. +func (to *SendReceiveTimeout) SetSendTimeout(nanoseconds int64) { + atomic.StoreInt64(&to.send, nanoseconds) +} + +// SendTimeout implements Socket.SendTimeout. +func (to *SendReceiveTimeout) SendTimeout() int64 { + return atomic.LoadInt64(&to.send) +} + +// GetSockOptEmitUnimplementedEvent emits unimplemented event if name is valid. +// It contains names that are valid for GetSockOpt when level is SOL_SOCKET. +func GetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) { + switch name { + case linux.SO_ACCEPTCONN, + linux.SO_BPF_EXTENSIONS, + linux.SO_COOKIE, + linux.SO_DOMAIN, + linux.SO_ERROR, + linux.SO_GET_FILTER, + linux.SO_INCOMING_NAPI_ID, + linux.SO_MEMINFO, + linux.SO_PEERCRED, + linux.SO_PEERGROUPS, + linux.SO_PEERNAME, + linux.SO_PEERSEC, + linux.SO_PROTOCOL, + linux.SO_SNDLOWAT, + linux.SO_TYPE: + + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEvent(t, name) + } +} + +// SetSockOptEmitUnimplementedEvent emits unimplemented event if name is valid. +// It contains names that are valid for SetSockOpt when level is SOL_SOCKET. +func SetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) { + switch name { + case linux.SO_ATTACH_BPF, + linux.SO_ATTACH_FILTER, + linux.SO_ATTACH_REUSEPORT_CBPF, + linux.SO_ATTACH_REUSEPORT_EBPF, + linux.SO_CNX_ADVICE, + linux.SO_DETACH_FILTER, + linux.SO_RCVBUFFORCE, + linux.SO_SNDBUFFORCE: + + t.Kernel().EmitUnimplementedEvent(t) + + default: + emitUnimplementedEvent(t, name) + } +} + +// emitUnimplementedEvent emits unimplemented event if name is valid. It +// contains names that are common between Get and SetSocketOpt when level is +// SOL_SOCKET. +func emitUnimplementedEvent(t *kernel.Task, name int) { + switch name { + case linux.SO_BINDTODEVICE, + linux.SO_BROADCAST, + linux.SO_BSDCOMPAT, + linux.SO_BUSY_POLL, + linux.SO_DEBUG, + linux.SO_DONTROUTE, + linux.SO_INCOMING_CPU, + linux.SO_KEEPALIVE, + linux.SO_LINGER, + linux.SO_LOCK_FILTER, + linux.SO_MARK, + linux.SO_MAX_PACING_RATE, + linux.SO_NOFCS, + linux.SO_NO_CHECK, + linux.SO_OOBINLINE, + linux.SO_PASSCRED, + linux.SO_PASSSEC, + linux.SO_PEEK_OFF, + linux.SO_PRIORITY, + linux.SO_RCVBUF, + linux.SO_RCVLOWAT, + linux.SO_RCVTIMEO, + linux.SO_REUSEADDR, + linux.SO_REUSEPORT, + linux.SO_RXQ_OVFL, + linux.SO_SELECT_ERR_QUEUE, + linux.SO_SNDBUF, + linux.SO_SNDTIMEO, + linux.SO_TIMESTAMP, + linux.SO_TIMESTAMPING, + linux.SO_TIMESTAMPNS, + linux.SO_TXTIME, + linux.SO_WIFI_STATUS, + linux.SO_ZEROCOPY: + + t.Kernel().EmitUnimplementedEvent(t) + } +} diff --git a/pkg/sentry/socket/socket_state_autogen.go b/pkg/sentry/socket/socket_state_autogen.go new file mode 100755 index 000000000..f3c899200 --- /dev/null +++ b/pkg/sentry/socket/socket_state_autogen.go @@ -0,0 +1,24 @@ +// automatically generated by stateify. + +package socket + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *SendReceiveTimeout) beforeSave() {} +func (x *SendReceiveTimeout) save(m state.Map) { + x.beforeSave() + m.Save("send", &x.send) + m.Save("recv", &x.recv) +} + +func (x *SendReceiveTimeout) afterLoad() {} +func (x *SendReceiveTimeout) load(m state.Map) { + m.Load("send", &x.send) + m.Load("recv", &x.recv) +} + +func init() { + state.Register("socket.SendReceiveTimeout", (*SendReceiveTimeout)(nil), state.Fns{Save: (*SendReceiveTimeout).save, Load: (*SendReceiveTimeout).load}) +} diff --git a/pkg/sentry/socket/unix/device.go b/pkg/sentry/socket/unix/device.go new file mode 100644 index 000000000..734d39ee6 --- /dev/null +++ b/pkg/sentry/socket/unix/device.go @@ -0,0 +1,20 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unix + +import "gvisor.googlesource.com/gvisor/pkg/sentry/device" + +// unixSocketDevice is the unix socket virtual device. +var unixSocketDevice = device.NewAnonDevice() diff --git a/pkg/sentry/socket/unix/io.go b/pkg/sentry/socket/unix/io.go new file mode 100644 index 000000000..5a1475ec2 --- /dev/null +++ b/pkg/sentry/socket/unix/io.go @@ -0,0 +1,93 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unix + +import ( + "gvisor.googlesource.com/gvisor/pkg/sentry/safemem" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/tcpip" +) + +// EndpointWriter implements safemem.Writer that writes to a transport.Endpoint. +// +// EndpointWriter is not thread-safe. +type EndpointWriter struct { + // Endpoint is the transport.Endpoint to write to. + Endpoint transport.Endpoint + + // Control is the control messages to send. + Control transport.ControlMessages + + // To is the endpoint to send to. May be nil. + To transport.BoundEndpoint +} + +// WriteFromBlocks implements safemem.Writer.WriteFromBlocks. +func (w *EndpointWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) { + return safemem.FromVecWriterFunc{func(bufs [][]byte) (int64, error) { + n, err := w.Endpoint.SendMsg(bufs, w.Control, w.To) + if err != nil { + return int64(n), err.ToError() + } + return int64(n), nil + }}.WriteFromBlocks(srcs) +} + +// EndpointReader implements safemem.Reader that reads from a +// transport.Endpoint. +// +// EndpointReader is not thread-safe. +type EndpointReader struct { + // Endpoint is the transport.Endpoint to read from. + Endpoint transport.Endpoint + + // Creds indicates if credential control messages are requested. + Creds bool + + // NumRights is the number of SCM_RIGHTS FDs requested. + NumRights uintptr + + // Peek indicates that the data should not be consumed from the + // endpoint. + Peek bool + + // MsgSize is the size of the message that was read from. For stream + // sockets, it is the amount read. + MsgSize uintptr + + // From, if not nil, will be set with the address read from. + From *tcpip.FullAddress + + // Control contains the received control messages. + Control transport.ControlMessages + + // ControlTrunc indicates that SCM_RIGHTS FDs were discarded based on + // the value of NumRights. + ControlTrunc bool +} + +// ReadToBlocks implements safemem.Reader.ReadToBlocks. +func (r *EndpointReader) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { + return safemem.FromVecReaderFunc{func(bufs [][]byte) (int64, error) { + n, ms, c, ct, err := r.Endpoint.RecvMsg(bufs, r.Creds, r.NumRights, r.Peek, r.From) + r.Control = c + r.ControlTrunc = ct + r.MsgSize = ms + if err != nil { + return int64(n), err.ToError() + } + return int64(n), nil + }}.ReadToBlocks(dsts) +} diff --git a/pkg/sentry/socket/unix/transport/connectioned.go b/pkg/sentry/socket/unix/transport/connectioned.go new file mode 100644 index 000000000..18e492862 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/connectioned.go @@ -0,0 +1,460 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package transport + +import ( + "sync" + + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// UniqueIDProvider generates a sequence of unique identifiers useful for, +// among other things, lock ordering. +type UniqueIDProvider interface { + // UniqueID returns a new unique identifier. + UniqueID() uint64 +} + +// A ConnectingEndpoint is a connectioned unix endpoint that is attempting to +// establish a bidirectional connection with a BoundEndpoint. +type ConnectingEndpoint interface { + // ID returns the endpoint's globally unique identifier. This identifier + // must be used to determine locking order if more than one endpoint is + // to be locked in the same codepath. The endpoint with the smaller + // identifier must be locked before endpoints with larger identifiers. + ID() uint64 + + // Passcred implements socket.Credentialer.Passcred. + Passcred() bool + + // Type returns the socket type, typically either SockStream or + // SockSeqpacket. The connection attempt must be aborted if this + // value doesn't match the ConnectableEndpoint's type. + Type() SockType + + // GetLocalAddress returns the bound path. + GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) + + // Locker protects the following methods. While locked, only the holder of + // the lock can change the return value of the protected methods. + sync.Locker + + // Connected returns true iff the ConnectingEndpoint is in the connected + // state. ConnectingEndpoints can only be connected to a single endpoint, + // so the connection attempt must be aborted if this returns true. + Connected() bool + + // Listening returns true iff the ConnectingEndpoint is in the listening + // state. ConnectingEndpoints cannot make connections while listening, so + // the connection attempt must be aborted if this returns true. + Listening() bool + + // WaiterQueue returns a pointer to the endpoint's waiter queue. + WaiterQueue() *waiter.Queue +} + +// connectionedEndpoint is a Unix-domain connected or connectable endpoint and implements +// ConnectingEndpoint, ConnectableEndpoint and tcpip.Endpoint. +// +// connectionedEndpoints must be in connected state in order to transfer data. +// +// This implementation includes STREAM and SEQPACKET Unix sockets created with +// socket(2), accept(2) or socketpair(2) and dgram unix sockets created with +// socketpair(2). See unix_connectionless.go for the implementation of DGRAM +// Unix sockets created with socket(2). +// +// The state is much simpler than a TCP endpoint, so it is not encoded +// explicitly. Instead we enforce the following invariants: +// +// receiver != nil, connected != nil => connected. +// path != "" && acceptedChan == nil => bound, not listening. +// path != "" && acceptedChan != nil => bound and listening. +// +// Only one of these will be true at any moment. +// +// +stateify savable +type connectionedEndpoint struct { + baseEndpoint + + // id is the unique endpoint identifier. This is used exclusively for + // lock ordering within connect. + id uint64 + + // idGenerator is used to generate new unique endpoint identifiers. + idGenerator UniqueIDProvider + + // stype is used by connecting sockets to ensure that they are the + // same type. The value is typically either tcpip.SockSeqpacket or + // tcpip.SockStream. + stype SockType + + // acceptedChan is per the TCP endpoint implementation. Note that the + // sockets in this channel are _already in the connected state_, and + // have another associated connectionedEndpoint. + // + // If nil, then no listen call has been made. + acceptedChan chan *connectionedEndpoint `state:".([]*connectionedEndpoint)"` +} + +// NewConnectioned creates a new unbound connectionedEndpoint. +func NewConnectioned(stype SockType, uid UniqueIDProvider) Endpoint { + return &connectionedEndpoint{ + baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}}, + id: uid.UniqueID(), + idGenerator: uid, + stype: stype, + } +} + +// NewPair allocates a new pair of connected unix-domain connectionedEndpoints. +func NewPair(stype SockType, uid UniqueIDProvider) (Endpoint, Endpoint) { + a := &connectionedEndpoint{ + baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}}, + id: uid.UniqueID(), + idGenerator: uid, + stype: stype, + } + b := &connectionedEndpoint{ + baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}}, + id: uid.UniqueID(), + idGenerator: uid, + stype: stype, + } + + q1 := &queue{ReaderQueue: a.Queue, WriterQueue: b.Queue, limit: initialLimit} + q2 := &queue{ReaderQueue: b.Queue, WriterQueue: a.Queue, limit: initialLimit} + + if stype == SockStream { + a.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{q1}} + b.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{q2}} + } else { + a.receiver = &queueReceiver{q1} + b.receiver = &queueReceiver{q2} + } + + q2.IncRef() + a.connected = &connectedEndpoint{ + endpoint: b, + writeQueue: q2, + } + q1.IncRef() + b.connected = &connectedEndpoint{ + endpoint: a, + writeQueue: q1, + } + + return a, b +} + +// NewExternal creates a new externally backed Endpoint. It behaves like a +// socketpair. +func NewExternal(stype SockType, uid UniqueIDProvider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint { + return &connectionedEndpoint{ + baseEndpoint: baseEndpoint{Queue: queue, receiver: receiver, connected: connected}, + id: uid.UniqueID(), + idGenerator: uid, + stype: stype, + } +} + +// ID implements ConnectingEndpoint.ID. +func (e *connectionedEndpoint) ID() uint64 { + return e.id +} + +// Type implements ConnectingEndpoint.Type and Endpoint.Type. +func (e *connectionedEndpoint) Type() SockType { + return e.stype +} + +// WaiterQueue implements ConnectingEndpoint.WaiterQueue. +func (e *connectionedEndpoint) WaiterQueue() *waiter.Queue { + return e.Queue +} + +// isBound returns true iff the connectionedEndpoint is bound (but not +// listening). +func (e *connectionedEndpoint) isBound() bool { + return e.path != "" && e.acceptedChan == nil +} + +// Listening implements ConnectingEndpoint.Listening. +func (e *connectionedEndpoint) Listening() bool { + return e.acceptedChan != nil +} + +// Close puts the connectionedEndpoint in a closed state and frees all +// resources associated with it. +// +// The socket will be a fresh state after a call to close and may be reused. +// That is, close may be used to "unbind" or "disconnect" the socket in error +// paths. +func (e *connectionedEndpoint) Close() { + e.Lock() + var c ConnectedEndpoint + var r Receiver + switch { + case e.Connected(): + e.connected.CloseSend() + e.receiver.CloseRecv() + c = e.connected + r = e.receiver + e.connected = nil + e.receiver = nil + case e.isBound(): + e.path = "" + case e.Listening(): + close(e.acceptedChan) + for n := range e.acceptedChan { + n.Close() + } + e.acceptedChan = nil + e.path = "" + } + e.Unlock() + if c != nil { + c.CloseNotify() + c.Release() + } + if r != nil { + r.CloseNotify() + r.Release() + } +} + +// BidirectionalConnect implements BoundEndpoint.BidirectionalConnect. +func (e *connectionedEndpoint) BidirectionalConnect(ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error { + if ce.Type() != e.stype { + return syserr.ErrConnectionRefused + } + + // Check if ce is e to avoid a deadlock. + if ce, ok := ce.(*connectionedEndpoint); ok && ce == e { + return syserr.ErrInvalidEndpointState + } + + // Do a dance to safely acquire locks on both endpoints. + if e.id < ce.ID() { + e.Lock() + ce.Lock() + } else { + ce.Lock() + e.Lock() + } + + // Check connecting state. + if ce.Connected() { + e.Unlock() + ce.Unlock() + return syserr.ErrAlreadyConnected + } + if ce.Listening() { + e.Unlock() + ce.Unlock() + return syserr.ErrInvalidEndpointState + } + + // Check bound state. + if !e.Listening() { + e.Unlock() + ce.Unlock() + return syserr.ErrConnectionRefused + } + + // Create a newly bound connectionedEndpoint. + ne := &connectionedEndpoint{ + baseEndpoint: baseEndpoint{ + path: e.path, + Queue: &waiter.Queue{}, + }, + id: e.idGenerator.UniqueID(), + idGenerator: e.idGenerator, + stype: e.stype, + } + + readQueue := &queue{ReaderQueue: ce.WaiterQueue(), WriterQueue: ne.Queue, limit: initialLimit} + ne.connected = &connectedEndpoint{ + endpoint: ce, + writeQueue: readQueue, + } + + writeQueue := &queue{ReaderQueue: ne.Queue, WriterQueue: ce.WaiterQueue(), limit: initialLimit} + if e.stype == SockStream { + ne.receiver = &streamQueueReceiver{queueReceiver: queueReceiver{readQueue: writeQueue}} + } else { + ne.receiver = &queueReceiver{readQueue: writeQueue} + } + + select { + case e.acceptedChan <- ne: + // Commit state. + writeQueue.IncRef() + connected := &connectedEndpoint{ + endpoint: ne, + writeQueue: writeQueue, + } + readQueue.IncRef() + if e.stype == SockStream { + returnConnect(&streamQueueReceiver{queueReceiver: queueReceiver{readQueue: readQueue}}, connected) + } else { + returnConnect(&queueReceiver{readQueue: readQueue}, connected) + } + + // Notify can deadlock if we are holding these locks. + e.Unlock() + ce.Unlock() + + // Notify on both ends. + e.Notify(waiter.EventIn) + ce.WaiterQueue().Notify(waiter.EventOut) + + return nil + default: + // Busy; return ECONNREFUSED per spec. + ne.Close() + e.Unlock() + ce.Unlock() + return syserr.ErrConnectionRefused + } +} + +// UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect. +func (e *connectionedEndpoint) UnidirectionalConnect() (ConnectedEndpoint, *syserr.Error) { + return nil, syserr.ErrConnectionRefused +} + +// Connect attempts to directly connect to another Endpoint. +// Implements Endpoint.Connect. +func (e *connectionedEndpoint) Connect(server BoundEndpoint) *syserr.Error { + returnConnect := func(r Receiver, ce ConnectedEndpoint) { + e.receiver = r + e.connected = ce + } + + return server.BidirectionalConnect(e, returnConnect) +} + +// Listen starts listening on the connection. +func (e *connectionedEndpoint) Listen(backlog int) *syserr.Error { + e.Lock() + defer e.Unlock() + if e.Listening() { + // Adjust the size of the channel iff we can fix existing + // pending connections into the new one. + if len(e.acceptedChan) > backlog { + return syserr.ErrInvalidEndpointState + } + origChan := e.acceptedChan + e.acceptedChan = make(chan *connectionedEndpoint, backlog) + close(origChan) + for ep := range origChan { + e.acceptedChan <- ep + } + return nil + } + if !e.isBound() { + return syserr.ErrInvalidEndpointState + } + + // Normal case. + e.acceptedChan = make(chan *connectionedEndpoint, backlog) + return nil +} + +// Accept accepts a new connection. +func (e *connectionedEndpoint) Accept() (Endpoint, *syserr.Error) { + e.Lock() + defer e.Unlock() + + if !e.Listening() { + return nil, syserr.ErrInvalidEndpointState + } + + select { + case ne := <-e.acceptedChan: + return ne, nil + + default: + // Nothing left. + return nil, syserr.ErrWouldBlock + } +} + +// Bind binds the connection. +// +// For Unix connectionedEndpoints, this _only sets the address associated with +// the socket_. Work associated with sockets in the filesystem or finding those +// sockets must be done by a higher level. +// +// Bind will fail only if the socket is connected, bound or the passed address +// is invalid (the empty string). +func (e *connectionedEndpoint) Bind(addr tcpip.FullAddress, commit func() *syserr.Error) *syserr.Error { + e.Lock() + defer e.Unlock() + if e.isBound() || e.Listening() { + return syserr.ErrAlreadyBound + } + if addr.Addr == "" { + // The empty string is not permitted. + return syserr.ErrBadLocalAddress + } + if commit != nil { + if err := commit(); err != nil { + return err + } + } + + // Save the bound address. + e.path = string(addr.Addr) + return nil +} + +// SendMsg writes data and a control message to the endpoint's peer. +// This method does not block if the data cannot be written. +func (e *connectionedEndpoint) SendMsg(data [][]byte, c ControlMessages, to BoundEndpoint) (uintptr, *syserr.Error) { + // Stream sockets do not support specifying the endpoint. Seqpacket + // sockets ignore the passed endpoint. + if e.stype == SockStream && to != nil { + return 0, syserr.ErrNotSupported + } + return e.baseEndpoint.SendMsg(data, c, to) +} + +// Readiness returns the current readiness of the connectionedEndpoint. For +// example, if waiter.EventIn is set, the connectionedEndpoint is immediately +// readable. +func (e *connectionedEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask { + e.Lock() + defer e.Unlock() + + ready := waiter.EventMask(0) + switch { + case e.Connected(): + if mask&waiter.EventIn != 0 && e.receiver.Readable() { + ready |= waiter.EventIn + } + if mask&waiter.EventOut != 0 && e.connected.Writable() { + ready |= waiter.EventOut + } + case e.Listening(): + if mask&waiter.EventIn != 0 && len(e.acceptedChan) > 0 { + ready |= waiter.EventIn + } + } + + return ready +} diff --git a/pkg/sentry/socket/unix/transport/connectioned_state.go b/pkg/sentry/socket/unix/transport/connectioned_state.go new file mode 100644 index 000000000..7e02a5db8 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/connectioned_state.go @@ -0,0 +1,53 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package transport + +// saveAcceptedChan is invoked by stateify. +func (e *connectionedEndpoint) saveAcceptedChan() []*connectionedEndpoint { + // If acceptedChan is nil (i.e. we are not listening) then we will save nil. + // Otherwise we create a (possibly empty) slice of the values in acceptedChan and + // save that. + var acceptedSlice []*connectionedEndpoint + if e.acceptedChan != nil { + // Swap out acceptedChan with a new empty channel of the same capacity. + saveChan := e.acceptedChan + e.acceptedChan = make(chan *connectionedEndpoint, cap(saveChan)) + + // Create a new slice with the same len and capacity as the channel. + acceptedSlice = make([]*connectionedEndpoint, len(saveChan), cap(saveChan)) + // Drain acceptedChan into saveSlice, and fill up the new acceptChan at the + // same time. + for i := range acceptedSlice { + ep := <-saveChan + acceptedSlice[i] = ep + e.acceptedChan <- ep + } + close(saveChan) + } + return acceptedSlice +} + +// loadAcceptedChan is invoked by stateify. +func (e *connectionedEndpoint) loadAcceptedChan(acceptedSlice []*connectionedEndpoint) { + // If acceptedSlice is nil, then acceptedChan should also be nil. + if acceptedSlice != nil { + // Otherwise, create a new channel with the same capacity as acceptedSlice. + e.acceptedChan = make(chan *connectionedEndpoint, cap(acceptedSlice)) + // Seed the channel with values from acceptedSlice. + for _, ep := range acceptedSlice { + e.acceptedChan <- ep + } + } +} diff --git a/pkg/sentry/socket/unix/transport/connectionless.go b/pkg/sentry/socket/unix/transport/connectionless.go new file mode 100644 index 000000000..43ff875e4 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/connectionless.go @@ -0,0 +1,196 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package transport + +import ( + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// connectionlessEndpoint is a unix endpoint for unix sockets that support operating in +// a conectionless fashon. +// +// Specifically, this means datagram unix sockets not created with +// socketpair(2). +// +// +stateify savable +type connectionlessEndpoint struct { + baseEndpoint +} + +// NewConnectionless creates a new unbound dgram endpoint. +func NewConnectionless() Endpoint { + ep := &connectionlessEndpoint{baseEndpoint{Queue: &waiter.Queue{}}} + ep.receiver = &queueReceiver{readQueue: &queue{ReaderQueue: ep.Queue, WriterQueue: &waiter.Queue{}, limit: initialLimit}} + return ep +} + +// isBound returns true iff the endpoint is bound. +func (e *connectionlessEndpoint) isBound() bool { + return e.path != "" +} + +// Close puts the endpoint in a closed state and frees all resources associated +// with it. +// +// The socket will be a fresh state after a call to close and may be reused. +// That is, close may be used to "unbind" or "disconnect" the socket in error +// paths. +func (e *connectionlessEndpoint) Close() { + e.Lock() + var r Receiver + if e.Connected() { + e.receiver.CloseRecv() + r = e.receiver + e.receiver = nil + + e.connected.Release() + e.connected = nil + } + if e.isBound() { + e.path = "" + } + e.Unlock() + if r != nil { + r.CloseNotify() + r.Release() + } +} + +// BidirectionalConnect implements BoundEndpoint.BidirectionalConnect. +func (e *connectionlessEndpoint) BidirectionalConnect(ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error { + return syserr.ErrConnectionRefused +} + +// UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect. +func (e *connectionlessEndpoint) UnidirectionalConnect() (ConnectedEndpoint, *syserr.Error) { + e.Lock() + r := e.receiver + e.Unlock() + if r == nil { + return nil, syserr.ErrConnectionRefused + } + q := r.(*queueReceiver).readQueue + if !q.TryIncRef() { + return nil, syserr.ErrConnectionRefused + } + return &connectedEndpoint{ + endpoint: e, + writeQueue: q, + }, nil +} + +// SendMsg writes data and a control message to the specified endpoint. +// This method does not block if the data cannot be written. +func (e *connectionlessEndpoint) SendMsg(data [][]byte, c ControlMessages, to BoundEndpoint) (uintptr, *syserr.Error) { + if to == nil { + return e.baseEndpoint.SendMsg(data, c, nil) + } + + connected, err := to.UnidirectionalConnect() + if err != nil { + return 0, syserr.ErrInvalidEndpointState + } + defer connected.Release() + + e.Lock() + n, notify, err := connected.Send(data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)}) + e.Unlock() + + if notify { + connected.SendNotify() + } + + return n, err +} + +// Type implements Endpoint.Type. +func (e *connectionlessEndpoint) Type() SockType { + return SockDgram +} + +// Connect attempts to connect directly to server. +func (e *connectionlessEndpoint) Connect(server BoundEndpoint) *syserr.Error { + connected, err := server.UnidirectionalConnect() + if err != nil { + return err + } + + e.Lock() + e.connected = connected + e.Unlock() + + return nil +} + +// Listen starts listening on the connection. +func (e *connectionlessEndpoint) Listen(int) *syserr.Error { + return syserr.ErrNotSupported +} + +// Accept accepts a new connection. +func (e *connectionlessEndpoint) Accept() (Endpoint, *syserr.Error) { + return nil, syserr.ErrNotSupported +} + +// Bind binds the connection. +// +// For Unix endpoints, this _only sets the address associated with the socket_. +// Work associated with sockets in the filesystem or finding those sockets must +// be done by a higher level. +// +// Bind will fail only if the socket is connected, bound or the passed address +// is invalid (the empty string). +func (e *connectionlessEndpoint) Bind(addr tcpip.FullAddress, commit func() *syserr.Error) *syserr.Error { + e.Lock() + defer e.Unlock() + if e.isBound() { + return syserr.ErrAlreadyBound + } + if addr.Addr == "" { + // The empty string is not permitted. + return syserr.ErrBadLocalAddress + } + if commit != nil { + if err := commit(); err != nil { + return err + } + } + + // Save the bound address. + e.path = string(addr.Addr) + return nil +} + +// Readiness returns the current readiness of the endpoint. For example, if +// waiter.EventIn is set, the endpoint is immediately readable. +func (e *connectionlessEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask { + e.Lock() + defer e.Unlock() + + ready := waiter.EventMask(0) + if mask&waiter.EventIn != 0 && e.receiver.Readable() { + ready |= waiter.EventIn + } + + if e.Connected() { + if mask&waiter.EventOut != 0 && e.connected.Writable() { + ready |= waiter.EventOut + } + } + + return ready +} diff --git a/pkg/sentry/socket/unix/transport/queue.go b/pkg/sentry/socket/unix/transport/queue.go new file mode 100644 index 000000000..b650caae7 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/queue.go @@ -0,0 +1,210 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package transport + +import ( + "sync" + + "gvisor.googlesource.com/gvisor/pkg/refs" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// queue is a buffer queue. +// +// +stateify savable +type queue struct { + refs.AtomicRefCount + + ReaderQueue *waiter.Queue + WriterQueue *waiter.Queue + + mu sync.Mutex `state:"nosave"` + closed bool + used int64 + limit int64 + dataList messageList +} + +// Close closes q for reading and writing. It is immediately not writable and +// will become unreadable when no more data is pending. +// +// Both the read and write queues must be notified after closing: +// q.ReaderQueue.Notify(waiter.EventIn) +// q.WriterQueue.Notify(waiter.EventOut) +func (q *queue) Close() { + q.mu.Lock() + q.closed = true + q.mu.Unlock() +} + +// Reset empties the queue and Releases all of the Entries. +// +// Both the read and write queues must be notified after resetting: +// q.ReaderQueue.Notify(waiter.EventIn) +// q.WriterQueue.Notify(waiter.EventOut) +func (q *queue) Reset() { + q.mu.Lock() + for cur := q.dataList.Front(); cur != nil; cur = cur.Next() { + cur.Release() + } + q.dataList.Reset() + q.used = 0 + q.mu.Unlock() +} + +// DecRef implements RefCounter.DecRef with destructor q.Reset. +func (q *queue) DecRef() { + q.DecRefWithDestructor(q.Reset) + // We don't need to notify after resetting because no one cares about + // this queue after all references have been dropped. +} + +// IsReadable determines if q is currently readable. +func (q *queue) IsReadable() bool { + q.mu.Lock() + defer q.mu.Unlock() + + return q.closed || q.dataList.Front() != nil +} + +// bufWritable returns true if there is space for writing. +// +// N.B. Linux only considers a unix socket "writable" if >75% of the buffer is +// free. +// +// See net/unix/af_unix.c:unix_writeable. +func (q *queue) bufWritable() bool { + return 4*q.used < q.limit +} + +// IsWritable determines if q is currently writable. +func (q *queue) IsWritable() bool { + q.mu.Lock() + defer q.mu.Unlock() + + return q.closed || q.bufWritable() +} + +// Enqueue adds an entry to the data queue if room is available. +// +// If truncate is true, Enqueue may truncate the message beforing enqueuing it. +// Otherwise, the entire message must fit. If n < e.Length(), err indicates why. +// +// If notify is true, ReaderQueue.Notify must be called: +// q.ReaderQueue.Notify(waiter.EventIn) +func (q *queue) Enqueue(e *message, truncate bool) (l int64, notify bool, err *syserr.Error) { + q.mu.Lock() + + if q.closed { + q.mu.Unlock() + return 0, false, syserr.ErrClosedForSend + } + + free := q.limit - q.used + + l = e.Length() + + if l > free && truncate { + if free == 0 { + // Message can't fit right now. + q.mu.Unlock() + return 0, false, syserr.ErrWouldBlock + } + + e.Truncate(free) + l = e.Length() + err = syserr.ErrWouldBlock + } + + if l > q.limit { + // Message is too big to ever fit. + q.mu.Unlock() + return 0, false, syserr.ErrMessageTooLong + } + + if l > free { + // Message can't fit right now. + q.mu.Unlock() + return 0, false, syserr.ErrWouldBlock + } + + notify = q.dataList.Front() == nil + q.used += l + q.dataList.PushBack(e) + + q.mu.Unlock() + + return l, notify, err +} + +// Dequeue removes the first entry in the data queue, if one exists. +// +// If notify is true, WriterQueue.Notify must be called: +// q.WriterQueue.Notify(waiter.EventOut) +func (q *queue) Dequeue() (e *message, notify bool, err *syserr.Error) { + q.mu.Lock() + + if q.dataList.Front() == nil { + err := syserr.ErrWouldBlock + if q.closed { + err = syserr.ErrClosedForReceive + } + q.mu.Unlock() + + return nil, false, err + } + + notify = !q.bufWritable() + + e = q.dataList.Front() + q.dataList.Remove(e) + q.used -= e.Length() + + notify = notify && q.bufWritable() + + q.mu.Unlock() + + return e, notify, nil +} + +// Peek returns the first entry in the data queue, if one exists. +func (q *queue) Peek() (*message, *syserr.Error) { + q.mu.Lock() + defer q.mu.Unlock() + + if q.dataList.Front() == nil { + err := syserr.ErrWouldBlock + if q.closed { + err = syserr.ErrClosedForReceive + } + return nil, err + } + + return q.dataList.Front().Peek(), nil +} + +// QueuedSize returns the number of bytes currently in the queue, that is, the +// number of readable bytes. +func (q *queue) QueuedSize() int64 { + q.mu.Lock() + defer q.mu.Unlock() + return q.used +} + +// MaxQueueSize returns the maximum number of bytes storable in the queue. +func (q *queue) MaxQueueSize() int64 { + return q.limit +} diff --git a/pkg/sentry/socket/unix/transport/transport_message_list.go b/pkg/sentry/socket/unix/transport/transport_message_list.go new file mode 100755 index 000000000..6d394860c --- /dev/null +++ b/pkg/sentry/socket/unix/transport/transport_message_list.go @@ -0,0 +1,173 @@ +package transport + +// ElementMapper provides an identity mapping by default. +// +// This can be replaced to provide a struct that maps elements to linker +// objects, if they are not the same. An ElementMapper is not typically +// required if: Linker is left as is, Element is left as is, or Linker and +// Element are the same type. +type messageElementMapper struct{} + +// linkerFor maps an Element to a Linker. +// +// This default implementation should be inlined. +// +//go:nosplit +func (messageElementMapper) linkerFor(elem *message) *message { return elem } + +// List is an intrusive list. Entries can be added to or removed from the list +// in O(1) time and with no additional memory allocations. +// +// The zero value for List is an empty list ready to use. +// +// To iterate over a list (where l is a List): +// for e := l.Front(); e != nil; e = e.Next() { +// // do something with e. +// } +// +// +stateify savable +type messageList struct { + head *message + tail *message +} + +// Reset resets list l to the empty state. +func (l *messageList) Reset() { + l.head = nil + l.tail = nil +} + +// Empty returns true iff the list is empty. +func (l *messageList) Empty() bool { + return l.head == nil +} + +// Front returns the first element of list l or nil. +func (l *messageList) Front() *message { + return l.head +} + +// Back returns the last element of list l or nil. +func (l *messageList) Back() *message { + return l.tail +} + +// PushFront inserts the element e at the front of list l. +func (l *messageList) PushFront(e *message) { + messageElementMapper{}.linkerFor(e).SetNext(l.head) + messageElementMapper{}.linkerFor(e).SetPrev(nil) + + if l.head != nil { + messageElementMapper{}.linkerFor(l.head).SetPrev(e) + } else { + l.tail = e + } + + l.head = e +} + +// PushBack inserts the element e at the back of list l. +func (l *messageList) PushBack(e *message) { + messageElementMapper{}.linkerFor(e).SetNext(nil) + messageElementMapper{}.linkerFor(e).SetPrev(l.tail) + + if l.tail != nil { + messageElementMapper{}.linkerFor(l.tail).SetNext(e) + } else { + l.head = e + } + + l.tail = e +} + +// PushBackList inserts list m at the end of list l, emptying m. +func (l *messageList) PushBackList(m *messageList) { + if l.head == nil { + l.head = m.head + l.tail = m.tail + } else if m.head != nil { + messageElementMapper{}.linkerFor(l.tail).SetNext(m.head) + messageElementMapper{}.linkerFor(m.head).SetPrev(l.tail) + + l.tail = m.tail + } + + m.head = nil + m.tail = nil +} + +// InsertAfter inserts e after b. +func (l *messageList) InsertAfter(b, e *message) { + a := messageElementMapper{}.linkerFor(b).Next() + messageElementMapper{}.linkerFor(e).SetNext(a) + messageElementMapper{}.linkerFor(e).SetPrev(b) + messageElementMapper{}.linkerFor(b).SetNext(e) + + if a != nil { + messageElementMapper{}.linkerFor(a).SetPrev(e) + } else { + l.tail = e + } +} + +// InsertBefore inserts e before a. +func (l *messageList) InsertBefore(a, e *message) { + b := messageElementMapper{}.linkerFor(a).Prev() + messageElementMapper{}.linkerFor(e).SetNext(a) + messageElementMapper{}.linkerFor(e).SetPrev(b) + messageElementMapper{}.linkerFor(a).SetPrev(e) + + if b != nil { + messageElementMapper{}.linkerFor(b).SetNext(e) + } else { + l.head = e + } +} + +// Remove removes e from l. +func (l *messageList) Remove(e *message) { + prev := messageElementMapper{}.linkerFor(e).Prev() + next := messageElementMapper{}.linkerFor(e).Next() + + if prev != nil { + messageElementMapper{}.linkerFor(prev).SetNext(next) + } else { + l.head = next + } + + if next != nil { + messageElementMapper{}.linkerFor(next).SetPrev(prev) + } else { + l.tail = prev + } +} + +// Entry is a default implementation of Linker. Users can add anonymous fields +// of this type to their structs to make them automatically implement the +// methods needed by List. +// +// +stateify savable +type messageEntry struct { + next *message + prev *message +} + +// Next returns the entry that follows e in the list. +func (e *messageEntry) Next() *message { + return e.next +} + +// Prev returns the entry that precedes e in the list. +func (e *messageEntry) Prev() *message { + return e.prev +} + +// SetNext assigns 'entry' as the entry that follows e in the list. +func (e *messageEntry) SetNext(elem *message) { + e.next = elem +} + +// SetPrev assigns 'entry' as the entry that precedes e in the list. +func (e *messageEntry) SetPrev(elem *message) { + e.prev = elem +} diff --git a/pkg/sentry/socket/unix/transport/transport_state_autogen.go b/pkg/sentry/socket/unix/transport/transport_state_autogen.go new file mode 100755 index 000000000..8bb1b5b31 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/transport_state_autogen.go @@ -0,0 +1,191 @@ +// automatically generated by stateify. + +package transport + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *connectionedEndpoint) beforeSave() {} +func (x *connectionedEndpoint) save(m state.Map) { + x.beforeSave() + var acceptedChan []*connectionedEndpoint = x.saveAcceptedChan() + m.SaveValue("acceptedChan", acceptedChan) + m.Save("baseEndpoint", &x.baseEndpoint) + m.Save("id", &x.id) + m.Save("idGenerator", &x.idGenerator) + m.Save("stype", &x.stype) +} + +func (x *connectionedEndpoint) afterLoad() {} +func (x *connectionedEndpoint) load(m state.Map) { + m.Load("baseEndpoint", &x.baseEndpoint) + m.Load("id", &x.id) + m.Load("idGenerator", &x.idGenerator) + m.Load("stype", &x.stype) + m.LoadValue("acceptedChan", new([]*connectionedEndpoint), func(y interface{}) { x.loadAcceptedChan(y.([]*connectionedEndpoint)) }) +} + +func (x *connectionlessEndpoint) beforeSave() {} +func (x *connectionlessEndpoint) save(m state.Map) { + x.beforeSave() + m.Save("baseEndpoint", &x.baseEndpoint) +} + +func (x *connectionlessEndpoint) afterLoad() {} +func (x *connectionlessEndpoint) load(m state.Map) { + m.Load("baseEndpoint", &x.baseEndpoint) +} + +func (x *queue) beforeSave() {} +func (x *queue) save(m state.Map) { + x.beforeSave() + m.Save("AtomicRefCount", &x.AtomicRefCount) + m.Save("ReaderQueue", &x.ReaderQueue) + m.Save("WriterQueue", &x.WriterQueue) + m.Save("closed", &x.closed) + m.Save("used", &x.used) + m.Save("limit", &x.limit) + m.Save("dataList", &x.dataList) +} + +func (x *queue) afterLoad() {} +func (x *queue) load(m state.Map) { + m.Load("AtomicRefCount", &x.AtomicRefCount) + m.Load("ReaderQueue", &x.ReaderQueue) + m.Load("WriterQueue", &x.WriterQueue) + m.Load("closed", &x.closed) + m.Load("used", &x.used) + m.Load("limit", &x.limit) + m.Load("dataList", &x.dataList) +} + +func (x *messageList) beforeSave() {} +func (x *messageList) save(m state.Map) { + x.beforeSave() + m.Save("head", &x.head) + m.Save("tail", &x.tail) +} + +func (x *messageList) afterLoad() {} +func (x *messageList) load(m state.Map) { + m.Load("head", &x.head) + m.Load("tail", &x.tail) +} + +func (x *messageEntry) beforeSave() {} +func (x *messageEntry) save(m state.Map) { + x.beforeSave() + m.Save("next", &x.next) + m.Save("prev", &x.prev) +} + +func (x *messageEntry) afterLoad() {} +func (x *messageEntry) load(m state.Map) { + m.Load("next", &x.next) + m.Load("prev", &x.prev) +} + +func (x *ControlMessages) beforeSave() {} +func (x *ControlMessages) save(m state.Map) { + x.beforeSave() + m.Save("Rights", &x.Rights) + m.Save("Credentials", &x.Credentials) +} + +func (x *ControlMessages) afterLoad() {} +func (x *ControlMessages) load(m state.Map) { + m.Load("Rights", &x.Rights) + m.Load("Credentials", &x.Credentials) +} + +func (x *message) beforeSave() {} +func (x *message) save(m state.Map) { + x.beforeSave() + m.Save("messageEntry", &x.messageEntry) + m.Save("Data", &x.Data) + m.Save("Control", &x.Control) + m.Save("Address", &x.Address) +} + +func (x *message) afterLoad() {} +func (x *message) load(m state.Map) { + m.Load("messageEntry", &x.messageEntry) + m.Load("Data", &x.Data) + m.Load("Control", &x.Control) + m.Load("Address", &x.Address) +} + +func (x *queueReceiver) beforeSave() {} +func (x *queueReceiver) save(m state.Map) { + x.beforeSave() + m.Save("readQueue", &x.readQueue) +} + +func (x *queueReceiver) afterLoad() {} +func (x *queueReceiver) load(m state.Map) { + m.Load("readQueue", &x.readQueue) +} + +func (x *streamQueueReceiver) beforeSave() {} +func (x *streamQueueReceiver) save(m state.Map) { + x.beforeSave() + m.Save("queueReceiver", &x.queueReceiver) + m.Save("buffer", &x.buffer) + m.Save("control", &x.control) + m.Save("addr", &x.addr) +} + +func (x *streamQueueReceiver) afterLoad() {} +func (x *streamQueueReceiver) load(m state.Map) { + m.Load("queueReceiver", &x.queueReceiver) + m.Load("buffer", &x.buffer) + m.Load("control", &x.control) + m.Load("addr", &x.addr) +} + +func (x *connectedEndpoint) beforeSave() {} +func (x *connectedEndpoint) save(m state.Map) { + x.beforeSave() + m.Save("endpoint", &x.endpoint) + m.Save("writeQueue", &x.writeQueue) +} + +func (x *connectedEndpoint) afterLoad() {} +func (x *connectedEndpoint) load(m state.Map) { + m.Load("endpoint", &x.endpoint) + m.Load("writeQueue", &x.writeQueue) +} + +func (x *baseEndpoint) beforeSave() {} +func (x *baseEndpoint) save(m state.Map) { + x.beforeSave() + m.Save("Queue", &x.Queue) + m.Save("passcred", &x.passcred) + m.Save("receiver", &x.receiver) + m.Save("connected", &x.connected) + m.Save("path", &x.path) +} + +func (x *baseEndpoint) afterLoad() {} +func (x *baseEndpoint) load(m state.Map) { + m.Load("Queue", &x.Queue) + m.Load("passcred", &x.passcred) + m.Load("receiver", &x.receiver) + m.Load("connected", &x.connected) + m.Load("path", &x.path) +} + +func init() { + state.Register("transport.connectionedEndpoint", (*connectionedEndpoint)(nil), state.Fns{Save: (*connectionedEndpoint).save, Load: (*connectionedEndpoint).load}) + state.Register("transport.connectionlessEndpoint", (*connectionlessEndpoint)(nil), state.Fns{Save: (*connectionlessEndpoint).save, Load: (*connectionlessEndpoint).load}) + state.Register("transport.queue", (*queue)(nil), state.Fns{Save: (*queue).save, Load: (*queue).load}) + state.Register("transport.messageList", (*messageList)(nil), state.Fns{Save: (*messageList).save, Load: (*messageList).load}) + state.Register("transport.messageEntry", (*messageEntry)(nil), state.Fns{Save: (*messageEntry).save, Load: (*messageEntry).load}) + state.Register("transport.ControlMessages", (*ControlMessages)(nil), state.Fns{Save: (*ControlMessages).save, Load: (*ControlMessages).load}) + state.Register("transport.message", (*message)(nil), state.Fns{Save: (*message).save, Load: (*message).load}) + state.Register("transport.queueReceiver", (*queueReceiver)(nil), state.Fns{Save: (*queueReceiver).save, Load: (*queueReceiver).load}) + state.Register("transport.streamQueueReceiver", (*streamQueueReceiver)(nil), state.Fns{Save: (*streamQueueReceiver).save, Load: (*streamQueueReceiver).load}) + state.Register("transport.connectedEndpoint", (*connectedEndpoint)(nil), state.Fns{Save: (*connectedEndpoint).save, Load: (*connectedEndpoint).load}) + state.Register("transport.baseEndpoint", (*baseEndpoint)(nil), state.Fns{Save: (*baseEndpoint).save, Load: (*baseEndpoint).load}) +} diff --git a/pkg/sentry/socket/unix/transport/unix.go b/pkg/sentry/socket/unix/transport/unix.go new file mode 100644 index 000000000..b734b4c20 --- /dev/null +++ b/pkg/sentry/socket/unix/transport/unix.go @@ -0,0 +1,973 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package transport contains the implementation of Unix endpoints. +package transport + +import ( + "sync" + "sync/atomic" + + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// initialLimit is the starting limit for the socket buffers. +const initialLimit = 16 * 1024 + +// A SockType is a type (as opposed to family) of sockets. These are enumerated +// in the syscall package as syscall.SOCK_* constants. +type SockType int + +const ( + // SockStream corresponds to syscall.SOCK_STREAM. + SockStream SockType = 1 + // SockDgram corresponds to syscall.SOCK_DGRAM. + SockDgram SockType = 2 + // SockRaw corresponds to syscall.SOCK_RAW. + SockRaw SockType = 3 + // SockSeqpacket corresponds to syscall.SOCK_SEQPACKET. + SockSeqpacket SockType = 5 +) + +// A RightsControlMessage is a control message containing FDs. +type RightsControlMessage interface { + // Clone returns a copy of the RightsControlMessage. + Clone() RightsControlMessage + + // Release releases any resources owned by the RightsControlMessage. + Release() +} + +// A CredentialsControlMessage is a control message containing Unix credentials. +type CredentialsControlMessage interface { + // Equals returns true iff the two messages are equal. + Equals(CredentialsControlMessage) bool +} + +// A ControlMessages represents a collection of socket control messages. +// +// +stateify savable +type ControlMessages struct { + // Rights is a control message containing FDs. + Rights RightsControlMessage + + // Credentials is a control message containing Unix credentials. + Credentials CredentialsControlMessage +} + +// Empty returns true iff the ControlMessages does not contain either +// credentials or rights. +func (c *ControlMessages) Empty() bool { + return c.Rights == nil && c.Credentials == nil +} + +// Clone clones both the credentials and the rights. +func (c *ControlMessages) Clone() ControlMessages { + cm := ControlMessages{} + if c.Rights != nil { + cm.Rights = c.Rights.Clone() + } + cm.Credentials = c.Credentials + return cm +} + +// Release releases both the credentials and the rights. +func (c *ControlMessages) Release() { + if c.Rights != nil { + c.Rights.Release() + } + *c = ControlMessages{} +} + +// Endpoint is the interface implemented by Unix transport protocol +// implementations that expose functionality like sendmsg, recvmsg, connect, +// etc. to Unix socket implementations. +type Endpoint interface { + Credentialer + waiter.Waitable + + // Close puts the endpoint in a closed state and frees all resources + // associated with it. + Close() + + // RecvMsg reads data and a control message from the endpoint. This method + // does not block if there is no data pending. + // + // creds indicates if credential control messages are requested by the + // caller. This is useful for determining if control messages can be + // coalesced. creds is a hint and can be safely ignored by the + // implementation if no coalescing is possible. It is fine to return + // credential control messages when none were requested or to not return + // credential control messages when they were requested. + // + // numRights is the number of SCM_RIGHTS FDs requested by the caller. This + // is useful if one must allocate a buffer to receive a SCM_RIGHTS message + // or determine if control messages can be coalesced. numRights is a hint + // and can be safely ignored by the implementation if the number of + // available SCM_RIGHTS FDs is known and no coalescing is possible. It is + // fine for the returned number of SCM_RIGHTS FDs to be either higher or + // lower than the requested number. + // + // If peek is true, no data should be consumed from the Endpoint. Any and + // all data returned from a peek should be available in the next call to + // RecvMsg. + // + // recvLen is the number of bytes copied into data. + // + // msgLen is the length of the read message consumed for datagram Endpoints. + // msgLen is always the same as recvLen for stream Endpoints. + // + // CMTruncated indicates that the numRights hint was used to receive fewer + // than the total available SCM_RIGHTS FDs. Additional truncation may be + // required by the caller. + RecvMsg(data [][]byte, creds bool, numRights uintptr, peek bool, addr *tcpip.FullAddress) (recvLen, msgLen uintptr, cm ControlMessages, CMTruncated bool, err *syserr.Error) + + // SendMsg writes data and a control message to the endpoint's peer. + // This method does not block if the data cannot be written. + // + // SendMsg does not take ownership of any of its arguments on error. + SendMsg([][]byte, ControlMessages, BoundEndpoint) (uintptr, *syserr.Error) + + // Connect connects this endpoint directly to another. + // + // This should be called on the client endpoint, and the (bound) + // endpoint passed in as a parameter. + // + // The error codes are the same as Connect. + Connect(server BoundEndpoint) *syserr.Error + + // Shutdown closes the read and/or write end of the endpoint connection + // to its peer. + Shutdown(flags tcpip.ShutdownFlags) *syserr.Error + + // Listen puts the endpoint in "listen" mode, which allows it to accept + // new connections. + Listen(backlog int) *syserr.Error + + // Accept returns a new endpoint if a peer has established a connection + // to an endpoint previously set to listen mode. This method does not + // block if no new connections are available. + // + // The returned Queue is the wait queue for the newly created endpoint. + Accept() (Endpoint, *syserr.Error) + + // Bind binds the endpoint to a specific local address and port. + // Specifying a NIC is optional. + // + // An optional commit function will be executed atomically with respect + // to binding the endpoint. If this returns an error, the bind will not + // occur and the error will be propagated back to the caller. + Bind(address tcpip.FullAddress, commit func() *syserr.Error) *syserr.Error + + // Type return the socket type, typically either SockStream, SockDgram + // or SockSeqpacket. + Type() SockType + + // GetLocalAddress returns the address to which the endpoint is bound. + GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) + + // GetRemoteAddress returns the address to which the endpoint is + // connected. + GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) + + // SetSockOpt sets a socket option. opt should be one of the tcpip.*Option + // types. + SetSockOpt(opt interface{}) *tcpip.Error + + // GetSockOpt gets a socket option. opt should be a pointer to one of the + // tcpip.*Option types. + GetSockOpt(opt interface{}) *tcpip.Error +} + +// A Credentialer is a socket or endpoint that supports the SO_PASSCRED socket +// option. +type Credentialer interface { + // Passcred returns whether or not the SO_PASSCRED socket option is + // enabled on this end. + Passcred() bool + + // ConnectedPasscred returns whether or not the SO_PASSCRED socket option + // is enabled on the connected end. + ConnectedPasscred() bool +} + +// A BoundEndpoint is a unix endpoint that can be connected to. +type BoundEndpoint interface { + // BidirectionalConnect establishes a bi-directional connection between two + // unix endpoints in an all-or-nothing manner. If an error occurs during + // connecting, the state of neither endpoint should be modified. + // + // In order for an endpoint to establish such a bidirectional connection + // with a BoundEndpoint, the endpoint calls the BidirectionalConnect method + // on the BoundEndpoint and sends a representation of itself (the + // ConnectingEndpoint) and a callback (returnConnect) to receive the + // connection information (Receiver and ConnectedEndpoint) upon a + // successful connect. The callback should only be called on a successful + // connect. + // + // For a connection attempt to be successful, the ConnectingEndpoint must + // be unconnected and not listening and the BoundEndpoint whose + // BidirectionalConnect method is being called must be listening. + // + // This method will return syserr.ErrConnectionRefused on endpoints with a + // type that isn't SockStream or SockSeqpacket. + BidirectionalConnect(ep ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error + + // UnidirectionalConnect establishes a write-only connection to a unix + // endpoint. + // + // An endpoint which calls UnidirectionalConnect and supports it itself must + // not hold its own lock when calling UnidirectionalConnect. + // + // This method will return syserr.ErrConnectionRefused on a non-SockDgram + // endpoint. + UnidirectionalConnect() (ConnectedEndpoint, *syserr.Error) + + // Release releases any resources held by the BoundEndpoint. It must be + // called before dropping all references to a BoundEndpoint returned by a + // function. + Release() +} + +// message represents a message passed over a Unix domain socket. +// +// +stateify savable +type message struct { + messageEntry + + // Data is the Message payload. + Data buffer.View + + // Control is auxiliary control message data that goes along with the + // data. + Control ControlMessages + + // Address is the bound address of the endpoint that sent the message. + // + // If the endpoint that sent the message is not bound, the Address is + // the empty string. + Address tcpip.FullAddress +} + +// Length returns number of bytes stored in the message. +func (m *message) Length() int64 { + return int64(len(m.Data)) +} + +// Release releases any resources held by the message. +func (m *message) Release() { + m.Control.Release() +} + +// Peek returns a copy of the message. +func (m *message) Peek() *message { + return &message{Data: m.Data, Control: m.Control.Clone(), Address: m.Address} +} + +// Truncate reduces the length of the message payload to n bytes. +// +// Preconditions: n <= m.Length(). +func (m *message) Truncate(n int64) { + m.Data.CapLength(int(n)) +} + +// A Receiver can be used to receive Messages. +type Receiver interface { + // Recv receives a single message. This method does not block. + // + // See Endpoint.RecvMsg for documentation on shared arguments. + // + // notify indicates if RecvNotify should be called. + Recv(data [][]byte, creds bool, numRights uintptr, peek bool) (recvLen, msgLen uintptr, cm ControlMessages, CMTruncated bool, source tcpip.FullAddress, notify bool, err *syserr.Error) + + // RecvNotify notifies the Receiver of a successful Recv. This must not be + // called while holding any endpoint locks. + RecvNotify() + + // CloseRecv prevents the receiving of additional Messages. + // + // After CloseRecv is called, CloseNotify must also be called. + CloseRecv() + + // CloseNotify notifies the Receiver of recv being closed. This must not be + // called while holding any endpoint locks. + CloseNotify() + + // Readable returns if messages should be attempted to be received. This + // includes when read has been shutdown. + Readable() bool + + // RecvQueuedSize returns the total amount of data currently receivable. + // RecvQueuedSize should return -1 if the operation isn't supported. + RecvQueuedSize() int64 + + // RecvMaxQueueSize returns maximum value for RecvQueuedSize. + // RecvMaxQueueSize should return -1 if the operation isn't supported. + RecvMaxQueueSize() int64 + + // Release releases any resources owned by the Receiver. It should be + // called before droping all references to a Receiver. + Release() +} + +// queueReceiver implements Receiver for datagram sockets. +// +// +stateify savable +type queueReceiver struct { + readQueue *queue +} + +// Recv implements Receiver.Recv. +func (q *queueReceiver) Recv(data [][]byte, creds bool, numRights uintptr, peek bool) (uintptr, uintptr, ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) { + var m *message + var notify bool + var err *syserr.Error + if peek { + m, err = q.readQueue.Peek() + } else { + m, notify, err = q.readQueue.Dequeue() + } + if err != nil { + return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, err + } + src := []byte(m.Data) + var copied uintptr + for i := 0; i < len(data) && len(src) > 0; i++ { + n := copy(data[i], src) + copied += uintptr(n) + src = src[n:] + } + return copied, uintptr(len(m.Data)), m.Control, false, m.Address, notify, nil +} + +// RecvNotify implements Receiver.RecvNotify. +func (q *queueReceiver) RecvNotify() { + q.readQueue.WriterQueue.Notify(waiter.EventOut) +} + +// CloseNotify implements Receiver.CloseNotify. +func (q *queueReceiver) CloseNotify() { + q.readQueue.ReaderQueue.Notify(waiter.EventIn) + q.readQueue.WriterQueue.Notify(waiter.EventOut) +} + +// CloseRecv implements Receiver.CloseRecv. +func (q *queueReceiver) CloseRecv() { + q.readQueue.Close() +} + +// Readable implements Receiver.Readable. +func (q *queueReceiver) Readable() bool { + return q.readQueue.IsReadable() +} + +// RecvQueuedSize implements Receiver.RecvQueuedSize. +func (q *queueReceiver) RecvQueuedSize() int64 { + return q.readQueue.QueuedSize() +} + +// RecvMaxQueueSize implements Receiver.RecvMaxQueueSize. +func (q *queueReceiver) RecvMaxQueueSize() int64 { + return q.readQueue.MaxQueueSize() +} + +// Release implements Receiver.Release. +func (q *queueReceiver) Release() { + q.readQueue.DecRef() +} + +// streamQueueReceiver implements Receiver for stream sockets. +// +// +stateify savable +type streamQueueReceiver struct { + queueReceiver + + mu sync.Mutex `state:"nosave"` + buffer []byte + control ControlMessages + addr tcpip.FullAddress +} + +func vecCopy(data [][]byte, buf []byte) (uintptr, [][]byte, []byte) { + var copied uintptr + for len(data) > 0 && len(buf) > 0 { + n := copy(data[0], buf) + copied += uintptr(n) + buf = buf[n:] + data[0] = data[0][n:] + if len(data[0]) == 0 { + data = data[1:] + } + } + return copied, data, buf +} + +// Readable implements Receiver.Readable. +func (q *streamQueueReceiver) Readable() bool { + q.mu.Lock() + bl := len(q.buffer) + r := q.readQueue.IsReadable() + q.mu.Unlock() + // We're readable if we have data in our buffer or if the queue receiver is + // readable. + return bl > 0 || r +} + +// RecvQueuedSize implements Receiver.RecvQueuedSize. +func (q *streamQueueReceiver) RecvQueuedSize() int64 { + q.mu.Lock() + bl := len(q.buffer) + qs := q.readQueue.QueuedSize() + q.mu.Unlock() + return int64(bl) + qs +} + +// RecvMaxQueueSize implements Receiver.RecvMaxQueueSize. +func (q *streamQueueReceiver) RecvMaxQueueSize() int64 { + // The RecvMaxQueueSize() is the readQueue's MaxQueueSize() plus the largest + // message we can buffer which is also the largest message we can receive. + return 2 * q.readQueue.MaxQueueSize() +} + +// Recv implements Receiver.Recv. +func (q *streamQueueReceiver) Recv(data [][]byte, wantCreds bool, numRights uintptr, peek bool) (uintptr, uintptr, ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) { + q.mu.Lock() + defer q.mu.Unlock() + + var notify bool + + // If we have no data in the endpoint, we need to get some. + if len(q.buffer) == 0 { + // Load the next message into a buffer, even if we are peeking. Peeking + // won't consume the message, so it will be still available to be read + // the next time Recv() is called. + m, n, err := q.readQueue.Dequeue() + if err != nil { + return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, err + } + notify = n + q.buffer = []byte(m.Data) + q.control = m.Control + q.addr = m.Address + } + + var copied uintptr + if peek { + // Don't consume control message if we are peeking. + c := q.control.Clone() + + // Don't consume data since we are peeking. + copied, data, _ = vecCopy(data, q.buffer) + + return copied, copied, c, false, q.addr, notify, nil + } + + // Consume data and control message since we are not peeking. + copied, data, q.buffer = vecCopy(data, q.buffer) + + // Save the original state of q.control. + c := q.control + + // Remove rights from q.control and leave behind just the creds. + q.control.Rights = nil + if !wantCreds { + c.Credentials = nil + } + + var cmTruncated bool + if c.Rights != nil && numRights == 0 { + c.Rights.Release() + c.Rights = nil + cmTruncated = true + } + + haveRights := c.Rights != nil + + // If we have more capacity for data and haven't received any usable + // rights. + // + // Linux never coalesces rights control messages. + for !haveRights && len(data) > 0 { + // Get a message from the readQueue. + m, n, err := q.readQueue.Dequeue() + if err != nil { + // We already got some data, so ignore this error. This will + // manifest as a short read to the user, which is what Linux + // does. + break + } + notify = notify || n + q.buffer = []byte(m.Data) + q.control = m.Control + q.addr = m.Address + + if wantCreds { + if (q.control.Credentials == nil) != (c.Credentials == nil) { + // One message has credentials, the other does not. + break + } + + if q.control.Credentials != nil && c.Credentials != nil && !q.control.Credentials.Equals(c.Credentials) { + // Both messages have credentials, but they don't match. + break + } + } + + if numRights != 0 && c.Rights != nil && q.control.Rights != nil { + // Both messages have rights. + break + } + + var cpd uintptr + cpd, data, q.buffer = vecCopy(data, q.buffer) + copied += cpd + + if cpd == 0 { + // data was actually full. + break + } + + if q.control.Rights != nil { + // Consume rights. + if numRights == 0 { + cmTruncated = true + q.control.Rights.Release() + } else { + c.Rights = q.control.Rights + haveRights = true + } + q.control.Rights = nil + } + } + return copied, copied, c, cmTruncated, q.addr, notify, nil +} + +// A ConnectedEndpoint is an Endpoint that can be used to send Messages. +type ConnectedEndpoint interface { + // Passcred implements Endpoint.Passcred. + Passcred() bool + + // GetLocalAddress implements Endpoint.GetLocalAddress. + GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) + + // Send sends a single message. This method does not block. + // + // notify indicates if SendNotify should be called. + // + // syserr.ErrWouldBlock can be returned along with a partial write if + // the caller should block to send the rest of the data. + Send(data [][]byte, controlMessages ControlMessages, from tcpip.FullAddress) (n uintptr, notify bool, err *syserr.Error) + + // SendNotify notifies the ConnectedEndpoint of a successful Send. This + // must not be called while holding any endpoint locks. + SendNotify() + + // CloseSend prevents the sending of additional Messages. + // + // After CloseSend is call, CloseNotify must also be called. + CloseSend() + + // CloseNotify notifies the ConnectedEndpoint of send being closed. This + // must not be called while holding any endpoint locks. + CloseNotify() + + // Writable returns if messages should be attempted to be sent. This + // includes when write has been shutdown. + Writable() bool + + // EventUpdate lets the ConnectedEndpoint know that event registrations + // have changed. + EventUpdate() + + // SendQueuedSize returns the total amount of data currently queued for + // sending. SendQueuedSize should return -1 if the operation isn't + // supported. + SendQueuedSize() int64 + + // SendMaxQueueSize returns maximum value for SendQueuedSize. + // SendMaxQueueSize should return -1 if the operation isn't supported. + SendMaxQueueSize() int64 + + // Release releases any resources owned by the ConnectedEndpoint. It should + // be called before droping all references to a ConnectedEndpoint. + Release() +} + +// +stateify savable +type connectedEndpoint struct { + // endpoint represents the subset of the Endpoint functionality needed by + // the connectedEndpoint. It is implemented by both connectionedEndpoint + // and connectionlessEndpoint and allows the use of types which don't + // fully implement Endpoint. + endpoint interface { + // Passcred implements Endpoint.Passcred. + Passcred() bool + + // GetLocalAddress implements Endpoint.GetLocalAddress. + GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) + + // Type implements Endpoint.Type. + Type() SockType + } + + writeQueue *queue +} + +// Passcred implements ConnectedEndpoint.Passcred. +func (e *connectedEndpoint) Passcred() bool { + return e.endpoint.Passcred() +} + +// GetLocalAddress implements ConnectedEndpoint.GetLocalAddress. +func (e *connectedEndpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) { + return e.endpoint.GetLocalAddress() +} + +// Send implements ConnectedEndpoint.Send. +func (e *connectedEndpoint) Send(data [][]byte, controlMessages ControlMessages, from tcpip.FullAddress) (uintptr, bool, *syserr.Error) { + var l int64 + for _, d := range data { + l += int64(len(d)) + } + + truncate := false + if e.endpoint.Type() == SockStream { + // Since stream sockets don't preserve message boundaries, we + // can write only as much of the message as fits in the queue. + truncate = true + + // Discard empty stream packets. Since stream sockets don't + // preserve message boundaries, sending zero bytes is a no-op. + // In Linux, the receiver actually uses a zero-length receive + // as an indication that the stream was closed. + if l == 0 { + controlMessages.Release() + return 0, false, nil + } + } + + v := make([]byte, 0, l) + for _, d := range data { + v = append(v, d...) + } + + l, notify, err := e.writeQueue.Enqueue(&message{Data: buffer.View(v), Control: controlMessages, Address: from}, truncate) + return uintptr(l), notify, err +} + +// SendNotify implements ConnectedEndpoint.SendNotify. +func (e *connectedEndpoint) SendNotify() { + e.writeQueue.ReaderQueue.Notify(waiter.EventIn) +} + +// CloseNotify implements ConnectedEndpoint.CloseNotify. +func (e *connectedEndpoint) CloseNotify() { + e.writeQueue.ReaderQueue.Notify(waiter.EventIn) + e.writeQueue.WriterQueue.Notify(waiter.EventOut) +} + +// CloseSend implements ConnectedEndpoint.CloseSend. +func (e *connectedEndpoint) CloseSend() { + e.writeQueue.Close() +} + +// Writable implements ConnectedEndpoint.Writable. +func (e *connectedEndpoint) Writable() bool { + return e.writeQueue.IsWritable() +} + +// EventUpdate implements ConnectedEndpoint.EventUpdate. +func (*connectedEndpoint) EventUpdate() {} + +// SendQueuedSize implements ConnectedEndpoint.SendQueuedSize. +func (e *connectedEndpoint) SendQueuedSize() int64 { + return e.writeQueue.QueuedSize() +} + +// SendMaxQueueSize implements ConnectedEndpoint.SendMaxQueueSize. +func (e *connectedEndpoint) SendMaxQueueSize() int64 { + return e.writeQueue.MaxQueueSize() +} + +// Release implements ConnectedEndpoint.Release. +func (e *connectedEndpoint) Release() { + e.writeQueue.DecRef() +} + +// baseEndpoint is an embeddable unix endpoint base used in both the connected and connectionless +// unix domain socket Endpoint implementations. +// +// Not to be used on its own. +// +// +stateify savable +type baseEndpoint struct { + *waiter.Queue + + // passcred specifies whether SCM_CREDENTIALS socket control messages are + // enabled on this endpoint. Must be accessed atomically. + passcred int32 + + // Mutex protects the below fields. + sync.Mutex `state:"nosave"` + + // receiver allows Messages to be received. + receiver Receiver + + // connected allows messages to be sent and state information about the + // connected endpoint to be read. + connected ConnectedEndpoint + + // path is not empty if the endpoint has been bound, + // or may be used if the endpoint is connected. + path string +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (e *baseEndpoint) EventRegister(we *waiter.Entry, mask waiter.EventMask) { + e.Queue.EventRegister(we, mask) + e.Lock() + if e.connected != nil { + e.connected.EventUpdate() + } + e.Unlock() +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (e *baseEndpoint) EventUnregister(we *waiter.Entry) { + e.Queue.EventUnregister(we) + e.Lock() + if e.connected != nil { + e.connected.EventUpdate() + } + e.Unlock() +} + +// Passcred implements Credentialer.Passcred. +func (e *baseEndpoint) Passcred() bool { + return atomic.LoadInt32(&e.passcred) != 0 +} + +// ConnectedPasscred implements Credentialer.ConnectedPasscred. +func (e *baseEndpoint) ConnectedPasscred() bool { + e.Lock() + defer e.Unlock() + return e.connected != nil && e.connected.Passcred() +} + +func (e *baseEndpoint) setPasscred(pc bool) { + if pc { + atomic.StoreInt32(&e.passcred, 1) + } else { + atomic.StoreInt32(&e.passcred, 0) + } +} + +// Connected implements ConnectingEndpoint.Connected. +func (e *baseEndpoint) Connected() bool { + return e.receiver != nil && e.connected != nil +} + +// RecvMsg reads data and a control message from the endpoint. +func (e *baseEndpoint) RecvMsg(data [][]byte, creds bool, numRights uintptr, peek bool, addr *tcpip.FullAddress) (uintptr, uintptr, ControlMessages, bool, *syserr.Error) { + e.Lock() + + if e.receiver == nil { + e.Unlock() + return 0, 0, ControlMessages{}, false, syserr.ErrNotConnected + } + + recvLen, msgLen, cms, cmt, a, notify, err := e.receiver.Recv(data, creds, numRights, peek) + e.Unlock() + if err != nil { + return 0, 0, ControlMessages{}, false, err + } + + if notify { + e.receiver.RecvNotify() + } + + if addr != nil { + *addr = a + } + return recvLen, msgLen, cms, cmt, nil +} + +// SendMsg writes data and a control message to the endpoint's peer. +// This method does not block if the data cannot be written. +func (e *baseEndpoint) SendMsg(data [][]byte, c ControlMessages, to BoundEndpoint) (uintptr, *syserr.Error) { + e.Lock() + if !e.Connected() { + e.Unlock() + return 0, syserr.ErrNotConnected + } + if to != nil { + e.Unlock() + return 0, syserr.ErrAlreadyConnected + } + + n, notify, err := e.connected.Send(data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)}) + e.Unlock() + + if notify { + e.connected.SendNotify() + } + + return n, err +} + +// SetSockOpt sets a socket option. Currently not supported. +func (e *baseEndpoint) SetSockOpt(opt interface{}) *tcpip.Error { + switch v := opt.(type) { + case tcpip.PasscredOption: + e.setPasscred(v != 0) + return nil + } + return nil +} + +// GetSockOpt implements tcpip.Endpoint.GetSockOpt. +func (e *baseEndpoint) GetSockOpt(opt interface{}) *tcpip.Error { + switch o := opt.(type) { + case tcpip.ErrorOption: + return nil + + case *tcpip.SendQueueSizeOption: + e.Lock() + if !e.Connected() { + e.Unlock() + return tcpip.ErrNotConnected + } + qs := tcpip.SendQueueSizeOption(e.connected.SendQueuedSize()) + e.Unlock() + if qs < 0 { + return tcpip.ErrQueueSizeNotSupported + } + *o = qs + return nil + + case *tcpip.ReceiveQueueSizeOption: + e.Lock() + if !e.Connected() { + e.Unlock() + return tcpip.ErrNotConnected + } + qs := tcpip.ReceiveQueueSizeOption(e.receiver.RecvQueuedSize()) + e.Unlock() + if qs < 0 { + return tcpip.ErrQueueSizeNotSupported + } + *o = qs + return nil + + case *tcpip.PasscredOption: + if e.Passcred() { + *o = tcpip.PasscredOption(1) + } else { + *o = tcpip.PasscredOption(0) + } + return nil + + case *tcpip.SendBufferSizeOption: + e.Lock() + if !e.Connected() { + e.Unlock() + return tcpip.ErrNotConnected + } + qs := tcpip.SendBufferSizeOption(e.connected.SendMaxQueueSize()) + e.Unlock() + if qs < 0 { + return tcpip.ErrQueueSizeNotSupported + } + *o = qs + return nil + + case *tcpip.ReceiveBufferSizeOption: + e.Lock() + if e.receiver == nil { + e.Unlock() + return tcpip.ErrNotConnected + } + qs := tcpip.ReceiveBufferSizeOption(e.receiver.RecvMaxQueueSize()) + e.Unlock() + if qs < 0 { + return tcpip.ErrQueueSizeNotSupported + } + *o = qs + return nil + + case *tcpip.KeepaliveEnabledOption: + *o = 0 + return nil + + default: + return tcpip.ErrUnknownProtocolOption + } +} + +// Shutdown closes the read and/or write end of the endpoint connection to its +// peer. +func (e *baseEndpoint) Shutdown(flags tcpip.ShutdownFlags) *syserr.Error { + e.Lock() + if !e.Connected() { + e.Unlock() + return syserr.ErrNotConnected + } + + if flags&tcpip.ShutdownRead != 0 { + e.receiver.CloseRecv() + } + + if flags&tcpip.ShutdownWrite != 0 { + e.connected.CloseSend() + } + + e.Unlock() + + if flags&tcpip.ShutdownRead != 0 { + e.receiver.CloseNotify() + } + + if flags&tcpip.ShutdownWrite != 0 { + e.connected.CloseNotify() + } + + return nil +} + +// GetLocalAddress returns the bound path. +func (e *baseEndpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) { + e.Lock() + defer e.Unlock() + return tcpip.FullAddress{Addr: tcpip.Address(e.path)}, nil +} + +// GetRemoteAddress returns the local address of the connected endpoint (if +// available). +func (e *baseEndpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) { + e.Lock() + c := e.connected + e.Unlock() + if c != nil { + return c.GetLocalAddress() + } + return tcpip.FullAddress{}, tcpip.ErrNotConnected +} + +// Release implements BoundEndpoint.Release. +func (*baseEndpoint) Release() { + // Binding a baseEndpoint doesn't take a reference. +} diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go new file mode 100644 index 000000000..1414be0c6 --- /dev/null +++ b/pkg/sentry/socket/unix/unix.go @@ -0,0 +1,650 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package unix provides an implementation of the socket.Socket interface for +// the AF_UNIX protocol family. +package unix + +import ( + "strings" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/refs" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/control" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/epsocket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserr" + "gvisor.googlesource.com/gvisor/pkg/syserror" + "gvisor.googlesource.com/gvisor/pkg/tcpip" + "gvisor.googlesource.com/gvisor/pkg/waiter" +) + +// SocketOperations is a Unix socket. It is similar to an epsocket, except it +// is backed by a transport.Endpoint instead of a tcpip.Endpoint. +// +// +stateify savable +type SocketOperations struct { + fsutil.FilePipeSeek `state:"nosave"` + fsutil.FileNotDirReaddir `state:"nosave"` + fsutil.FileNoFsync `state:"nosave"` + fsutil.FileNoMMap `state:"nosave"` + fsutil.FileNoSplice `state:"nosave"` + fsutil.FileNoopFlush `state:"nosave"` + fsutil.FileUseInodeUnstableAttr `state:"nosave"` + refs.AtomicRefCount + socket.SendReceiveTimeout + + ep transport.Endpoint + isPacket bool +} + +// New creates a new unix socket. +func New(ctx context.Context, endpoint transport.Endpoint, isPacket bool) *fs.File { + dirent := socket.NewDirent(ctx, unixSocketDevice) + defer dirent.DecRef() + return NewWithDirent(ctx, dirent, endpoint, isPacket, fs.FileFlags{Read: true, Write: true}) +} + +// NewWithDirent creates a new unix socket using an existing dirent. +func NewWithDirent(ctx context.Context, d *fs.Dirent, ep transport.Endpoint, isPacket bool, flags fs.FileFlags) *fs.File { + return fs.NewFile(ctx, d, flags, &SocketOperations{ + ep: ep, + isPacket: isPacket, + }) +} + +// DecRef implements RefCounter.DecRef. +func (s *SocketOperations) DecRef() { + s.DecRefWithDestructor(func() { + s.ep.Close() + }) +} + +// Release implemements fs.FileOperations.Release. +func (s *SocketOperations) Release() { + // Release only decrements a reference on s because s may be referenced in + // the abstract socket namespace. + s.DecRef() +} + +// Endpoint extracts the transport.Endpoint. +func (s *SocketOperations) Endpoint() transport.Endpoint { + return s.ep +} + +// extractPath extracts and validates the address. +func extractPath(sockaddr []byte) (string, *syserr.Error) { + addr, err := epsocket.GetAddress(linux.AF_UNIX, sockaddr) + if err != nil { + return "", err + } + + // The address is trimmed by GetAddress. + p := string(addr.Addr) + if p == "" { + // Not allowed. + return "", syserr.ErrInvalidArgument + } + if p[len(p)-1] == '/' { + // Weird, they tried to bind '/a/b/c/'? + return "", syserr.ErrIsDir + } + + return p, nil +} + +// GetPeerName implements the linux syscall getpeername(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) GetPeerName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr, err := s.ep.GetRemoteAddress() + if err != nil { + return nil, 0, syserr.TranslateNetstackError(err) + } + + a, l := epsocket.ConvertAddress(linux.AF_UNIX, addr) + return a, l, nil +} + +// GetSockName implements the linux syscall getsockname(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) GetSockName(t *kernel.Task) (interface{}, uint32, *syserr.Error) { + addr, err := s.ep.GetLocalAddress() + if err != nil { + return nil, 0, syserr.TranslateNetstackError(err) + } + + a, l := epsocket.ConvertAddress(linux.AF_UNIX, addr) + return a, l, nil +} + +// Ioctl implements fs.FileOperations.Ioctl. +func (s *SocketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { + return epsocket.Ioctl(ctx, s.ep, io, args) +} + +// GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name, outLen int) (interface{}, *syserr.Error) { + return epsocket.GetSockOpt(t, s, s.ep, linux.AF_UNIX, s.ep.Type(), level, name, outLen) +} + +// Listen implements the linux syscall listen(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) Listen(t *kernel.Task, backlog int) *syserr.Error { + return s.ep.Listen(backlog) +} + +// blockingAccept implements a blocking version of accept(2), that is, if no +// connections are ready to be accept, it will block until one becomes ready. +func (s *SocketOperations) blockingAccept(t *kernel.Task) (transport.Endpoint, *syserr.Error) { + // Register for notifications. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + // Try to accept the connection; if it fails, then wait until we get a + // notification. + for { + if ep, err := s.ep.Accept(); err != syserr.ErrWouldBlock { + return ep, err + } + + if err := t.Block(ch); err != nil { + return nil, syserr.FromError(err) + } + } +} + +// Accept implements the linux syscall accept(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) Accept(t *kernel.Task, peerRequested bool, flags int, blocking bool) (kdefs.FD, interface{}, uint32, *syserr.Error) { + // Issue the accept request to get the new endpoint. + ep, err := s.ep.Accept() + if err != nil { + if err != syserr.ErrWouldBlock || !blocking { + return 0, nil, 0, err + } + + var err *syserr.Error + ep, err = s.blockingAccept(t) + if err != nil { + return 0, nil, 0, err + } + } + + ns := New(t, ep, s.isPacket) + defer ns.DecRef() + + if flags&linux.SOCK_NONBLOCK != 0 { + flags := ns.Flags() + flags.NonBlocking = true + ns.SetFlags(flags.Settable()) + } + + var addr interface{} + var addrLen uint32 + if peerRequested { + // Get address of the peer. + var err *syserr.Error + addr, addrLen, err = ns.FileOperations.(*SocketOperations).GetPeerName(t) + if err != nil { + return 0, nil, 0, err + } + } + + fdFlags := kernel.FDFlags{ + CloseOnExec: flags&linux.SOCK_CLOEXEC != 0, + } + fd, e := t.FDMap().NewFDFrom(0, ns, fdFlags, t.ThreadGroup().Limits()) + if e != nil { + return 0, nil, 0, syserr.FromError(e) + } + + t.Kernel().RecordSocket(ns, linux.AF_UNIX) + + return fd, addr, addrLen, nil +} + +// Bind implements the linux syscall bind(2) for unix sockets. +func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { + p, e := extractPath(sockaddr) + if e != nil { + return e + } + + bep, ok := s.ep.(transport.BoundEndpoint) + if !ok { + // This socket can't be bound. + return syserr.ErrInvalidArgument + } + + return s.ep.Bind(tcpip.FullAddress{Addr: tcpip.Address(p)}, func() *syserr.Error { + // Is it abstract? + if p[0] == 0 { + if t.IsNetworkNamespaced() { + return syserr.ErrInvalidEndpointState + } + if err := t.AbstractSockets().Bind(p[1:], bep, s); err != nil { + // syserr.ErrPortInUse corresponds to EADDRINUSE. + return syserr.ErrPortInUse + } + } else { + // The parent and name. + var d *fs.Dirent + var name string + + cwd := t.FSContext().WorkingDirectory() + defer cwd.DecRef() + + // Is there no slash at all? + if !strings.Contains(p, "/") { + d = cwd + name = p + } else { + root := t.FSContext().RootDirectory() + defer root.DecRef() + // Find the last path component, we know that something follows + // that final slash, otherwise extractPath() would have failed. + lastSlash := strings.LastIndex(p, "/") + subPath := p[:lastSlash] + if subPath == "" { + // Fix up subpath in case file is in root. + subPath = "/" + } + var err error + remainingTraversals := uint(fs.DefaultTraversalLimit) + d, err = t.MountNamespace().FindInode(t, root, cwd, subPath, &remainingTraversals) + if err != nil { + // No path available. + return syserr.ErrNoSuchFile + } + defer d.DecRef() + name = p[lastSlash+1:] + } + + // Create the socket. + childDir, err := d.Bind(t, t.FSContext().RootDirectory(), name, bep, fs.FilePermissions{User: fs.PermMask{Read: true}}) + if err != nil { + return syserr.ErrPortInUse + } + childDir.DecRef() + } + + return nil + }) +} + +// extractEndpoint retrieves the transport.BoundEndpoint associated with a Unix +// socket path. The Release must be called on the transport.BoundEndpoint when +// the caller is done with it. +func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, *syserr.Error) { + path, err := extractPath(sockaddr) + if err != nil { + return nil, err + } + + // Is it abstract? + if path[0] == 0 { + if t.IsNetworkNamespaced() { + return nil, syserr.ErrInvalidArgument + } + + ep := t.AbstractSockets().BoundEndpoint(path[1:]) + if ep == nil { + // No socket found. + return nil, syserr.ErrConnectionRefused + } + + return ep, nil + } + + // Find the node in the filesystem. + root := t.FSContext().RootDirectory() + cwd := t.FSContext().WorkingDirectory() + remainingTraversals := uint(fs.DefaultTraversalLimit) + d, e := t.MountNamespace().FindInode(t, root, cwd, path, &remainingTraversals) + cwd.DecRef() + root.DecRef() + if e != nil { + return nil, syserr.FromError(e) + } + + // Extract the endpoint if one is there. + ep := d.Inode.BoundEndpoint(path) + d.DecRef() + if ep == nil { + // No socket! + return nil, syserr.ErrConnectionRefused + } + + return ep, nil +} + +// Connect implements the linux syscall connect(2) for unix sockets. +func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error { + ep, err := extractEndpoint(t, sockaddr) + if err != nil { + return err + } + defer ep.Release() + + // Connect the server endpoint. + return s.ep.Connect(ep) +} + +// Writev implements fs.FileOperations.Write. +func (s *SocketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { + t := kernel.TaskFromContext(ctx) + ctrl := control.New(t, s.ep, nil) + + if src.NumBytes() == 0 { + nInt, err := s.ep.SendMsg([][]byte{}, ctrl, nil) + return int64(nInt), err.ToError() + } + + return src.CopyInTo(ctx, &EndpointWriter{ + Endpoint: s.ep, + Control: ctrl, + To: nil, + }) +} + +// SendMsg implements the linux syscall sendmsg(2) for unix sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags int, haveDeadline bool, deadline ktime.Time, controlMessages socket.ControlMessages) (int, *syserr.Error) { + w := EndpointWriter{ + Endpoint: s.ep, + Control: controlMessages.Unix, + To: nil, + } + if len(to) > 0 { + ep, err := extractEndpoint(t, to) + if err != nil { + return 0, err + } + defer ep.Release() + w.To = ep + } + + n, err := src.CopyInTo(t, &w) + if err != syserror.ErrWouldBlock || flags&linux.MSG_DONTWAIT != 0 { + return int(n), syserr.FromError(err) + } + + // We'll have to block. Register for notification and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventOut) + defer s.EventUnregister(&e) + + total := n + for { + // Shorten src to reflect bytes previously written. + src = src.DropFirst64(n) + + n, err = src.CopyInTo(t, &w) + total += n + if err != syserror.ErrWouldBlock { + break + } + + if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if err == syserror.ETIMEDOUT { + err = syserror.ErrWouldBlock + } + break + } + } + + return int(total), syserr.FromError(err) +} + +// Passcred implements transport.Credentialer.Passcred. +func (s *SocketOperations) Passcred() bool { + return s.ep.Passcred() +} + +// ConnectedPasscred implements transport.Credentialer.ConnectedPasscred. +func (s *SocketOperations) ConnectedPasscred() bool { + return s.ep.ConnectedPasscred() +} + +// Readiness implements waiter.Waitable.Readiness. +func (s *SocketOperations) Readiness(mask waiter.EventMask) waiter.EventMask { + return s.ep.Readiness(mask) +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (s *SocketOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { + s.ep.EventRegister(e, mask) +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (s *SocketOperations) EventUnregister(e *waiter.Entry) { + s.ep.EventUnregister(e) +} + +// SetSockOpt implements the linux syscall setsockopt(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVal []byte) *syserr.Error { + return epsocket.SetSockOpt(t, s, s.ep, level, name, optVal) +} + +// Shutdown implements the linux syscall shutdown(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) Shutdown(t *kernel.Task, how int) *syserr.Error { + f, err := epsocket.ConvertShutdown(how) + if err != nil { + return err + } + + // Issue shutdown request. + return s.ep.Shutdown(f) +} + +// Read implements fs.FileOperations.Read. +func (s *SocketOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { + if dst.NumBytes() == 0 { + return 0, nil + } + return dst.CopyOutFrom(ctx, &EndpointReader{ + Endpoint: s.ep, + NumRights: 0, + Peek: false, + From: nil, + }) +} + +// RecvMsg implements the linux syscall recvmsg(2) for sockets backed by +// a transport.Endpoint. +func (s *SocketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags int, haveDeadline bool, deadline ktime.Time, senderRequested bool, controlDataLen uint64) (n int, msgFlags int, senderAddr interface{}, senderAddrLen uint32, controlMessages socket.ControlMessages, err *syserr.Error) { + trunc := flags&linux.MSG_TRUNC != 0 + peek := flags&linux.MSG_PEEK != 0 + dontWait := flags&linux.MSG_DONTWAIT != 0 + waitAll := flags&linux.MSG_WAITALL != 0 + + // Calculate the number of FDs for which we have space and if we are + // requesting credentials. + var wantCreds bool + rightsLen := int(controlDataLen) - syscall.SizeofCmsghdr + if s.Passcred() { + // Credentials take priority if they are enabled and there is space. + wantCreds = rightsLen > 0 + if !wantCreds { + msgFlags |= linux.MSG_CTRUNC + } + credLen := syscall.CmsgSpace(syscall.SizeofUcred) + rightsLen -= credLen + } + // FDs are 32 bit (4 byte) ints. + numRights := rightsLen / 4 + if numRights < 0 { + numRights = 0 + } + + r := EndpointReader{ + Endpoint: s.ep, + Creds: wantCreds, + NumRights: uintptr(numRights), + Peek: peek, + } + if senderRequested { + r.From = &tcpip.FullAddress{} + } + var total int64 + if n, err := dst.CopyOutFrom(t, &r); err != syserror.ErrWouldBlock || dontWait { + var from interface{} + var fromLen uint32 + if r.From != nil { + from, fromLen = epsocket.ConvertAddress(linux.AF_UNIX, *r.From) + } + + if r.ControlTrunc { + msgFlags |= linux.MSG_CTRUNC + } + + if err != nil || dontWait || !waitAll || s.isPacket || n >= dst.NumBytes() { + if s.isPacket && n < int64(r.MsgSize) { + msgFlags |= linux.MSG_TRUNC + } + + if trunc { + n = int64(r.MsgSize) + } + + return int(n), msgFlags, from, fromLen, socket.ControlMessages{Unix: r.Control}, syserr.FromError(err) + } + + // Don't overwrite any data we received. + dst = dst.DropFirst64(n) + total += n + } + + // We'll have to block. Register for notification and keep trying to + // send all the data. + e, ch := waiter.NewChannelEntry(nil) + s.EventRegister(&e, waiter.EventIn) + defer s.EventUnregister(&e) + + for { + if n, err := dst.CopyOutFrom(t, &r); err != syserror.ErrWouldBlock { + var from interface{} + var fromLen uint32 + if r.From != nil { + from, fromLen = epsocket.ConvertAddress(linux.AF_UNIX, *r.From) + } + + if r.ControlTrunc { + msgFlags |= linux.MSG_CTRUNC + } + + if trunc { + // n and r.MsgSize are the same for streams. + total += int64(r.MsgSize) + } else { + total += n + } + + if err != nil || !waitAll || s.isPacket || n >= dst.NumBytes() { + if total > 0 { + err = nil + } + if s.isPacket && n < int64(r.MsgSize) { + msgFlags |= linux.MSG_TRUNC + } + return int(total), msgFlags, from, fromLen, socket.ControlMessages{Unix: r.Control}, syserr.FromError(err) + } + + // Don't overwrite any data we received. + dst = dst.DropFirst64(n) + } + + if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { + if total > 0 { + err = nil + } + if err == syserror.ETIMEDOUT { + return int(total), msgFlags, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain + } + return int(total), msgFlags, nil, 0, socket.ControlMessages{}, syserr.FromError(err) + } + } +} + +// provider is a unix domain socket provider. +type provider struct{} + +// Socket returns a new unix domain socket. +func (*provider) Socket(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *syserr.Error) { + // Check arguments. + if protocol != 0 && protocol != linux.AF_UNIX /* PF_UNIX */ { + return nil, syserr.ErrProtocolNotSupported + } + + // Create the endpoint and socket. + var ep transport.Endpoint + var isPacket bool + switch stype { + case linux.SOCK_DGRAM: + isPacket = true + ep = transport.NewConnectionless() + case linux.SOCK_SEQPACKET: + isPacket = true + fallthrough + case linux.SOCK_STREAM: + ep = transport.NewConnectioned(stype, t.Kernel()) + default: + return nil, syserr.ErrInvalidArgument + } + + return New(t, ep, isPacket), nil +} + +// Pair creates a new pair of AF_UNIX connected sockets. +func (*provider) Pair(t *kernel.Task, stype transport.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) { + // Check arguments. + if protocol != 0 && protocol != linux.AF_UNIX /* PF_UNIX */ { + return nil, nil, syserr.ErrProtocolNotSupported + } + + var isPacket bool + switch stype { + case linux.SOCK_STREAM: + case linux.SOCK_DGRAM, linux.SOCK_SEQPACKET: + isPacket = true + default: + return nil, nil, syserr.ErrInvalidArgument + } + + // Create the endpoints and sockets. + ep1, ep2 := transport.NewPair(stype, t.Kernel()) + s1 := New(t, ep1, isPacket) + s2 := New(t, ep2, isPacket) + + return s1, s2, nil +} + +func init() { + socket.RegisterProvider(linux.AF_UNIX, &provider{}) +} diff --git a/pkg/sentry/socket/unix/unix_state_autogen.go b/pkg/sentry/socket/unix/unix_state_autogen.go new file mode 100755 index 000000000..6f8d24b44 --- /dev/null +++ b/pkg/sentry/socket/unix/unix_state_autogen.go @@ -0,0 +1,28 @@ +// automatically generated by stateify. + +package unix + +import ( + "gvisor.googlesource.com/gvisor/pkg/state" +) + +func (x *SocketOperations) beforeSave() {} +func (x *SocketOperations) save(m state.Map) { + x.beforeSave() + m.Save("AtomicRefCount", &x.AtomicRefCount) + m.Save("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Save("ep", &x.ep) + m.Save("isPacket", &x.isPacket) +} + +func (x *SocketOperations) afterLoad() {} +func (x *SocketOperations) load(m state.Map) { + m.Load("AtomicRefCount", &x.AtomicRefCount) + m.Load("SendReceiveTimeout", &x.SendReceiveTimeout) + m.Load("ep", &x.ep) + m.Load("isPacket", &x.isPacket) +} + +func init() { + state.Register("unix.SocketOperations", (*SocketOperations)(nil), state.Fns{Save: (*SocketOperations).save, Load: (*SocketOperations).load}) +} |