From ea1a15131f2cb264c4c4a1102b0f80202e95f971 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Fri, 5 Apr 2024 14:46:22 -0700 Subject: [PATCH] Move helpers to parse netlink message to the separate module It will be used in following changes. PiperOrigin-RevId: 622287107 --- pkg/sentry/socket/netlink/BUILD | 19 +------- pkg/sentry/socket/netlink/nlmsg/BUILD | 35 +++++++++++++++ .../socket/netlink/{ => nlmsg}/message.go | 43 ++++++++++++++++++- .../netlink/{ => nlmsg}/message_test.go | 6 +-- pkg/sentry/socket/netlink/provider.go | 3 +- pkg/sentry/socket/netlink/route/BUILD | 1 + pkg/sentry/socket/netlink/route/protocol.go | 23 +++++----- pkg/sentry/socket/netlink/socket.go | 15 ++++--- pkg/sentry/socket/netlink/uevent/BUILD | 1 + pkg/sentry/socket/netlink/uevent/protocol.go | 3 +- 10 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 pkg/sentry/socket/netlink/nlmsg/BUILD rename pkg/sentry/socket/netlink/{ => nlmsg}/message.go (88%) rename pkg/sentry/socket/netlink/{ => nlmsg}/message_test.go (98%) diff --git a/pkg/sentry/socket/netlink/BUILD b/pkg/sentry/socket/netlink/BUILD index 4d87f53f4..4bbdca48d 100644 --- a/pkg/sentry/socket/netlink/BUILD +++ b/pkg/sentry/socket/netlink/BUILD @@ -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", - ], -) diff --git a/pkg/sentry/socket/netlink/nlmsg/BUILD b/pkg/sentry/socket/netlink/nlmsg/BUILD new file mode 100644 index 000000000..e7b82a85d --- /dev/null +++ b/pkg/sentry/socket/netlink/nlmsg/BUILD @@ -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", + ], +) diff --git a/pkg/sentry/socket/netlink/message.go b/pkg/sentry/socket/netlink/nlmsg/message.go similarity index 88% rename from pkg/sentry/socket/netlink/message.go rename to pkg/sentry/socket/netlink/nlmsg/message.go index 5e159f959..fa1ec6f5e 100644 --- a/pkg/sentry/socket/netlink/message.go +++ b/pkg/sentry/socket/netlink/nlmsg/message.go @@ -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 +} diff --git a/pkg/sentry/socket/netlink/message_test.go b/pkg/sentry/socket/netlink/nlmsg/message_test.go similarity index 98% rename from pkg/sentry/socket/netlink/message_test.go rename to pkg/sentry/socket/netlink/nlmsg/message_test.go index 1604b2792..cc1cffe00 100644 --- a/pkg/sentry/socket/netlink/message_test.go +++ b/pkg/sentry/socket/netlink/nlmsg/message_test.go @@ -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() diff --git a/pkg/sentry/socket/netlink/provider.go b/pkg/sentry/socket/netlink/provider.go index 0c01180e6..fe431755d 100644 --- a/pkg/sentry/socket/netlink/provider.go +++ b/pkg/sentry/socket/netlink/provider.go @@ -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 diff --git a/pkg/sentry/socket/netlink/route/BUILD b/pkg/sentry/socket/netlink/route/BUILD index 7b3e8d13e..bdede27f4 100644 --- a/pkg/sentry/socket/netlink/route/BUILD +++ b/pkg/sentry/socket/netlink/route/BUILD @@ -20,6 +20,7 @@ go_library( "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/socket/netlink", + "//pkg/sentry/socket/netlink/nlmsg", "//pkg/syserr", ], ) diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index 3b8fd1a4e..2469cdde6 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -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. diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index 9aea8bccd..aa7c9f4bf 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -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 { diff --git a/pkg/sentry/socket/netlink/uevent/BUILD b/pkg/sentry/socket/netlink/uevent/BUILD index eed8f8a28..cbe235941 100644 --- a/pkg/sentry/socket/netlink/uevent/BUILD +++ b/pkg/sentry/socket/netlink/uevent/BUILD @@ -14,6 +14,7 @@ go_library( "//pkg/context", "//pkg/sentry/kernel", "//pkg/sentry/socket/netlink", + "//pkg/sentry/socket/netlink/nlmsg", "//pkg/syserr", ], ) diff --git a/pkg/sentry/socket/netlink/uevent/protocol.go b/pkg/sentry/socket/netlink/uevent/protocol.go index 029ba21b5..07780ca98 100644 --- a/pkg/sentry/socket/netlink/uevent/protocol.go +++ b/pkg/sentry/socket/netlink/uevent/protocol.go @@ -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 }