summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
authorIan Gudger <igudger@google.com>2019-02-07 23:14:06 -0800
committerShentubot <shentubot@google.com>2019-02-07 23:15:23 -0800
commit80f901b16b8bb8fe397cc44578035173f5155b24 (patch)
tree91707e2f2b424f71f7bac661c05a830b56244255 /pkg
parentfda4d1f4f11201c34bd15d41ba4c94279d135d95 (diff)
Plumb IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP to netstack.
Also includes a few fixes for IPv4 multicast support. IPv6 support is coming in a followup CL. PiperOrigin-RevId: 233008638 Change-Id: If7dae6222fef43fda48033f0292af77832d95e82
Diffstat (limited to 'pkg')
-rw-r--r--pkg/abi/linux/socket.go21
-rw-r--r--pkg/sentry/socket/epsocket/epsocket.go46
-rw-r--r--pkg/tcpip/stack/stack.go7
-rw-r--r--pkg/tcpip/transport/udp/endpoint.go16
-rw-r--r--pkg/tcpip/transport/udp/endpoint_state.go6
5 files changed, 86 insertions, 10 deletions
diff --git a/pkg/abi/linux/socket.go b/pkg/abi/linux/socket.go
index a5f78506a..906776525 100644
--- a/pkg/abi/linux/socket.go
+++ b/pkg/abi/linux/socket.go
@@ -204,15 +204,30 @@ const (
// uapi/linux/socket.h.
const SockAddrMax = 128
-// SockAddrInt is struct sockaddr_in, from uapi/linux/in.h.
+// InetAddr is struct in_addr, from uapi/linux/in.h.
+type InetAddr [4]byte
+
+// SockAddrInet is struct sockaddr_in, from uapi/linux/in.h.
type SockAddrInet struct {
Family uint16
Port uint16
- Addr [4]byte
+ Addr InetAddr
Zero [8]uint8 // pad to sizeof(struct sockaddr).
}
-// SockAddrInt6 is struct sockaddr_in6, from uapi/linux/in6.h.
+// InetMulticastRequest is struct ip_mreq, from uapi/linux/in.h.
+type InetMulticastRequest struct {
+ MulticastAddr InetAddr
+ InterfaceAddr InetAddr
+}
+
+// InetMulticastRequestWithNIC is struct ip_mreqn, from uapi/linux/in.h.
+type InetMulticastRequestWithNIC struct {
+ InetMulticastRequest
+ InterfaceIndex int32
+}
+
+// SockAddrInet6 is struct sockaddr_in6, from uapi/linux/in6.h.
type SockAddrInet6 struct {
Family uint16
Port uint16
diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go
index ca865b111..16720456a 100644
--- a/pkg/sentry/socket/epsocket/epsocket.go
+++ b/pkg/sentry/socket/epsocket/epsocket.go
@@ -1078,6 +1078,25 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte)
return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{}))
}
+var (
+ inetMulticastRequestSize = int(binary.Size(linux.InetMulticastRequest{}))
+ inetMulticastRequestWithNICSize = int(binary.Size(linux.InetMulticastRequestWithNIC{}))
+)
+
+func copyInMulticastRequest(optVal []byte) (linux.InetMulticastRequestWithNIC, *syserr.Error) {
+ if len(optVal) < inetMulticastRequestSize {
+ return linux.InetMulticastRequestWithNIC{}, syserr.ErrInvalidArgument
+ }
+
+ var req linux.InetMulticastRequestWithNIC
+ if len(optVal) >= inetMulticastRequestWithNICSize {
+ binary.Unmarshal(optVal[:inetMulticastRequestWithNICSize], usermem.ByteOrder, &req)
+ } else {
+ binary.Unmarshal(optVal[:inetMulticastRequestSize], usermem.ByteOrder, &req.InetMulticastRequest)
+ }
+ return req, nil
+}
+
// setSockOptIP implements SetSockOpt when level is SOL_IP.
func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
switch name {
@@ -1096,7 +1115,31 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
}
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastTTLOption(v)))
- case linux.IP_ADD_MEMBERSHIP, linux.MCAST_JOIN_GROUP, linux.IP_MULTICAST_IF:
+ case linux.IP_ADD_MEMBERSHIP:
+ req, err := copyInMulticastRequest(optVal)
+ if err != nil {
+ return err
+ }
+
+ return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.AddMembershipOption{
+ NIC: tcpip.NICID(req.InterfaceIndex),
+ InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
+ MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
+ }))
+
+ case linux.IP_DROP_MEMBERSHIP:
+ req, err := copyInMulticastRequest(optVal)
+ if err != nil {
+ return err
+ }
+
+ return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.RemoveMembershipOption{
+ NIC: tcpip.NICID(req.InterfaceIndex),
+ InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
+ MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
+ }))
+
+ case linux.MCAST_JOIN_GROUP, linux.IP_MULTICAST_IF:
// FIXME: Disallow IP-level multicast group options by
// default. These will need to be supported by appropriately plumbing
// the level through to the network stack (if at all). However, we
@@ -1108,7 +1151,6 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
linux.IP_BIND_ADDRESS_NO_PORT,
linux.IP_BLOCK_SOURCE,
linux.IP_CHECKSUM,
- linux.IP_DROP_MEMBERSHIP,
linux.IP_DROP_SOURCE_MEMBERSHIP,
linux.IP_FREEBIND,
linux.IP_HDRINCL,
diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go
index 7aa9dbd46..854ebe1bb 100644
--- a/pkg/tcpip/stack/stack.go
+++ b/pkg/tcpip/stack/stack.go
@@ -742,6 +742,9 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
return Route{}, tcpip.ErrNoRoute
}
+ // TODO: Route multicast packets with no specified local
+ // address or NIC.
+
for i := range s.routeTable {
if (id != 0 && id != s.routeTable[i].NIC) || (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
continue
@@ -768,6 +771,10 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
return r, nil
}
+ if isMulticast {
+ return Route{}, tcpip.ErrNetworkUnreachable
+ }
+
return Route{}, tcpip.ErrNoRoute
}
diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go
index b2a27a7cb..d46bf0ade 100644
--- a/pkg/tcpip/transport/udp/endpoint.go
+++ b/pkg/tcpip/transport/udp/endpoint.go
@@ -99,6 +99,7 @@ type endpoint struct {
effectiveNetProtos []tcpip.NetworkProtocolNumber
}
+// +stateify savable
type multicastMembership struct {
nicID tcpip.NICID
multicastAddr tcpip.Address
@@ -412,6 +413,8 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
}
if nicID == 0 {
+ // TODO: Allow adding memberships without
+ // specifing an interface.
return tcpip.ErrNoRoute
}
@@ -766,9 +769,11 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
}
}
+ nicid := addr.NIC
if len(addr.Addr) != 0 {
// A local address was specified, verify that it's valid.
- if e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr) == 0 {
+ nicid = e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr)
+ if nicid == 0 {
return tcpip.ErrBadLocalAddress
}
}
@@ -777,21 +782,21 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
LocalPort: addr.Port,
LocalAddress: addr.Addr,
}
- id, err = e.registerWithStack(addr.NIC, netProtos, id)
+ id, err = e.registerWithStack(nicid, netProtos, id)
if err != nil {
return err
}
if commit != nil {
if err := commit(); err != nil {
// Unregister, the commit failed.
- e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber, id, e)
+ e.stack.UnregisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
return err
}
}
e.id = id
- e.regNICID = addr.NIC
+ e.regNICID = nicid
e.effectiveNetProtos = netProtos
// Mark endpoint as bound.
@@ -815,7 +820,8 @@ func (e *endpoint) Bind(addr tcpip.FullAddress, commit func() *tcpip.Error) *tcp
return err
}
- e.bindNICID = addr.NIC
+ // Save the effective NICID generated by bindLocked.
+ e.bindNICID = e.regNICID
return nil
}
diff --git a/pkg/tcpip/transport/udp/endpoint_state.go b/pkg/tcpip/transport/udp/endpoint_state.go
index db1e281ad..4d8210294 100644
--- a/pkg/tcpip/transport/udp/endpoint_state.go
+++ b/pkg/tcpip/transport/udp/endpoint_state.go
@@ -103,4 +103,10 @@ func (e *endpoint) afterLoad() {
if err != nil {
panic(*err)
}
+
+ for _, m := range e.multicastMemberships {
+ if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
+ panic(err)
+ }
+ }
}