Move helpers to parse netlink message to the separate module

It will be used in following changes.

PiperOrigin-RevId: 622287107
This commit is contained in:
Andrei Vagin
2024-04-05 14:49:39 -07:00
committed by gVisor bot
parent 52fc39e336
commit ea1a15131f
10 changed files with 108 additions and 41 deletions
+2 -17
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "go_library", "go_test")
load("//tools:defs.bzl", "go_library")
package(
default_applicable_licenses = ["//:license"],
@@ -8,7 +8,6 @@ package(
go_library(
name = "netlink",
srcs = [
"message.go",
"provider.go",
"socket.go",
],
@@ -16,7 +15,6 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/abi/linux/errno",
"//pkg/bits",
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/hostarch",
@@ -28,6 +26,7 @@ go_library(
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
"//pkg/sentry/socket",
"//pkg/sentry/socket/netlink/nlmsg",
"//pkg/sentry/socket/netlink/port",
"//pkg/sentry/socket/unix",
"//pkg/sentry/socket/unix/transport",
@@ -38,17 +37,3 @@ go_library(
"//pkg/waiter",
],
)
go_test(
name = "netlink_test",
size = "small",
srcs = [
"message_test.go",
],
deps = [
":netlink",
"//pkg/abi/linux",
"//pkg/marshal",
"//pkg/marshal/primitive",
],
)
+35
View File
@@ -0,0 +1,35 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
go_library(
name = "nlmsg",
srcs = [
"message.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/bits",
"//pkg/hostarch",
"//pkg/marshal",
"//pkg/marshal/primitive",
],
)
go_test(
name = "nlmsg_test",
size = "small",
srcs = [
"message_test.go",
],
deps = [
":nlmsg",
"//pkg/abi/linux",
"//pkg/marshal",
"//pkg/marshal/primitive",
],
)
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package netlink
// Package nlmsg provides helpers to parse and construct netlink messages.
package nlmsg
import (
"fmt"
@@ -268,6 +269,23 @@ func (v AttrsView) ParseFirst() (hdr linux.NetlinkAttrHeader, value []byte, rest
return hdr, value, AttrsView(b), ok
}
// Parse parses netlink attributes.
func (v AttrsView) Parse() (map[uint16]BytesView, bool) {
attrs := make(map[uint16]BytesView)
attrsView := v
for !attrsView.Empty() {
// The index is unspecified, search by the interface name.
ahdr, value, rest, ok := attrsView.ParseFirst()
if !ok {
return nil, false
}
attrsView = rest
attrs[ahdr.Type] = BytesView(value)
}
return attrs, true
}
// BytesView supports extracting data from a byte slice with bounds checking.
type BytesView []byte
@@ -281,3 +299,26 @@ func (v *BytesView) Extract(n int) ([]byte, bool) {
*v = (*v)[n:]
return extracted, true
}
// String converts the raw attribute value to string.
func (v *BytesView) String() string {
b := []byte(*v)
if len(b) == 0 {
return ""
}
if b[len(b)-1] == 0 {
b = b[:len(b)-1]
}
return string(b)
}
// Uint32 converts the raw attribute value to uint32.
func (v *BytesView) Uint32() (uint32, bool) {
attr := []byte(*v)
val := primitive.Uint32(0)
if len(attr) != val.SizeBytes() {
return 0, false
}
val.UnmarshalBytes(attr)
return uint32(val), true
}
@@ -22,7 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
)
func TestParseMessage(t *testing.T) {
@@ -160,7 +160,7 @@ func TestParseMessage(t *testing.T) {
},
}
for _, test := range tests {
msg, rest, ok := netlink.ParseMessage(test.input)
msg, rest, ok := nlmsg.ParseMessage(test.input)
if ok != test.ok {
t.Errorf("%v: got ok = %v, want = %v", test.desc, ok, test.ok)
continue
@@ -277,7 +277,7 @@ func TestAttrView(t *testing.T) {
},
}
for _, test := range tests {
attrs := netlink.AttrsView(test.input)
attrs := nlmsg.AttrsView(test.input)
// Test ParseFirst().
hdr, value, rest, ok := attrs.ParseFirst()
+2 -1
View File
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserr"
)
@@ -43,7 +44,7 @@ type Protocol interface {
// 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, msg *Message, ms *MessageSet) *syserr.Error
ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error
}
// Provider is a function that creates a new Protocol for a specific netlink
+1
View File
@@ -20,6 +20,7 @@ go_library(
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/socket/netlink",
"//pkg/sentry/socket/netlink/nlmsg",
"//pkg/syserr",
],
)
+12 -11
View File
@@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
"gvisor.dev/gvisor/pkg/syserr"
)
@@ -69,7 +70,7 @@ func (p *Protocol) CanSend() bool {
}
// dumpLinks handles RTM_GETLINK dump requests.
func (p *Protocol) dumpLinks(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) dumpLinks(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.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.
@@ -101,7 +102,7 @@ func (p *Protocol) dumpLinks(ctx context.Context, msg *netlink.Message, ms *netl
}
// getLinks handles RTM_GETLINK requests.
func (p *Protocol) getLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) getLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network devices.
@@ -161,7 +162,7 @@ func (p *Protocol) getLink(ctx context.Context, msg *netlink.Message, ms *netlin
return nil
}
func (p *Protocol) newLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) newLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network stack.
@@ -228,7 +229,7 @@ func (p *Protocol) newLink(ctx context.Context, msg *netlink.Message, ms *netlin
}
// delLink handles RTM_DELLINK requests.
func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) delLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network stack.
@@ -270,7 +271,7 @@ func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlin
// addNewLinkMessage appends RTM_NEWLINK message for the given interface into
// the message set.
func addNewLinkMessage(ms *netlink.MessageSet, idx int32, i inet.Interface) {
func addNewLinkMessage(ms *nlmsg.MessageSet, idx int32, i inet.Interface) {
m := ms.AddMessage(linux.NetlinkMessageHeader{
Type: linux.RTM_NEWLINK,
})
@@ -298,7 +299,7 @@ func addNewLinkMessage(ms *netlink.MessageSet, idx int32, i inet.Interface) {
}
// dumpAddrs handles RTM_GETADDR dump requests.
func (p *Protocol) dumpAddrs(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) dumpAddrs(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.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.
@@ -416,7 +417,7 @@ func fillRoute(routes []inet.Route, addr []byte) (inet.Route, *syserr.Error) {
}
// parseForDestination parses a message as format of RouteMessage-RtAttr-dst.
func parseForDestination(msg *netlink.Message) ([]byte, *syserr.Error) {
func parseForDestination(msg *nlmsg.Message) ([]byte, *syserr.Error) {
var rtMsg linux.RouteMessage
attrs, ok := msg.GetData(&rtMsg)
if !ok {
@@ -437,7 +438,7 @@ func parseForDestination(msg *netlink.Message) ([]byte, *syserr.Error) {
}
// dumpRoutes handles RTM_GETROUTE requests.
func (p *Protocol) dumpRoutes(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) dumpRoutes(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
// RTM_GETROUTE dump requests need not contain anything more than the
// netlink header and 1 byte protocol family common to all
// NETLINK_ROUTE requests.
@@ -512,7 +513,7 @@ func (p *Protocol) dumpRoutes(ctx context.Context, msg *netlink.Message, ms *net
}
// newAddr handles RTM_NEWADDR requests.
func (p *Protocol) newAddr(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) newAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network stack.
@@ -562,7 +563,7 @@ func (p *Protocol) newAddr(ctx context.Context, msg *netlink.Message, ms *netlin
}
// delAddr handles RTM_DELADDR requests.
func (p *Protocol) delAddr(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) delAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network stack.
@@ -608,7 +609,7 @@ func (p *Protocol) delAddr(ctx context.Context, msg *netlink.Message, ms *netlin
}
// ProcessMessage implements netlink.Protocol.ProcessMessage.
func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
hdr := msg.Header()
// All messages start with a 1 byte protocol family.
+8 -7
View File
@@ -32,6 +32,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/port"
"gvisor.dev/gvisor/pkg/sentry/socket/unix"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -650,7 +651,7 @@ func (kernelSCM) Credentials(*kernel.Task) (kernel.ThreadID, auth.UID, auth.GID)
var kernelCreds = &kernelSCM{}
// sendResponse sends the response messages in ms back to userspace.
func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error {
func (s *Socket) sendResponse(ctx context.Context, ms *nlmsg.MessageSet) *syserr.Error {
// Linux combines multiple netlink messages into a single datagram.
bufs := make([][]byte, 0, len(ms.Messages))
for _, m := range ms.Messages {
@@ -677,12 +678,12 @@ func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error
}
// N.B. multi-part messages should still send NLMSG_DONE even if
// MessageSet contains no messages.
// nlmsg.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{
m := nlmsg.NewMessage(linux.NetlinkMessageHeader{
Type: linux.NLMSG_DONE,
Flags: linux.NLM_F_MULTI,
Seq: ms.Seq,
@@ -704,7 +705,7 @@ func (s *Socket) sendResponse(ctx context.Context, ms *MessageSet) *syserr.Error
return nil
}
func dumpErrorMessage(hdr linux.NetlinkMessageHeader, ms *MessageSet, err *syserr.Error) {
func dumpErrorMessage(hdr linux.NetlinkMessageHeader, ms *nlmsg.MessageSet, err *syserr.Error) {
m := ms.AddMessage(linux.NetlinkMessageHeader{
Type: linux.NLMSG_ERROR,
})
@@ -714,7 +715,7 @@ func dumpErrorMessage(hdr linux.NetlinkMessageHeader, ms *MessageSet, err *syser
})
}
func dumpAckMessage(hdr linux.NetlinkMessageHeader, ms *MessageSet) {
func dumpAckMessage(hdr linux.NetlinkMessageHeader, ms *nlmsg.MessageSet) {
m := ms.AddMessage(linux.NetlinkMessageHeader{
Type: linux.NLMSG_ERROR,
})
@@ -728,7 +729,7 @@ func dumpAckMessage(hdr linux.NetlinkMessageHeader, ms *MessageSet) {
// handler for final handling.
func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error {
for len(buf) > 0 {
msg, rest, ok := ParseMessage(buf)
msg, rest, ok := nlmsg.ParseMessage(buf)
if !ok {
// Linux ignores messages that are too short. See
// net/netlink/af_netlink.c:netlink_rcv_skb.
@@ -742,7 +743,7 @@ func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error
continue
}
ms := NewMessageSet(s.portID, hdr.Seq)
ms := nlmsg.NewMessageSet(s.portID, hdr.Seq)
if err := s.protocol.ProcessMessage(ctx, msg, ms); err != nil {
dumpErrorMessage(hdr, ms, err)
} else if hdr.Flags&linux.NLM_F_ACK == linux.NLM_F_ACK {
+1
View File
@@ -14,6 +14,7 @@ go_library(
"//pkg/context",
"//pkg/sentry/kernel",
"//pkg/sentry/socket/netlink",
"//pkg/sentry/socket/netlink/nlmsg",
"//pkg/syserr",
],
)
+2 -1
View File
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink"
"gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg"
"gvisor.dev/gvisor/pkg/syserr"
)
@@ -49,7 +50,7 @@ func (p *Protocol) CanSend() bool {
}
// ProcessMessage implements netlink.Protocol.ProcessMessage.
func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
func (p *Protocol) ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error {
// Silently ignore all messages.
return nil
}