github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/socket/netlink/message.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package netlink
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  
    21  	"github.com/metacubex/gvisor/pkg/abi/linux"
    22  	"github.com/metacubex/gvisor/pkg/bits"
    23  	"github.com/metacubex/gvisor/pkg/hostarch"
    24  	"github.com/metacubex/gvisor/pkg/marshal"
    25  	"github.com/metacubex/gvisor/pkg/marshal/primitive"
    26  )
    27  
    28  // alignPad returns the length of padding required for alignment.
    29  //
    30  // Preconditions: align is a power of two.
    31  func alignPad(length int, align uint) int {
    32  	return bits.AlignUp(length, align) - length
    33  }
    34  
    35  // Message contains a complete serialized netlink message.
    36  type Message struct {
    37  	hdr linux.NetlinkMessageHeader
    38  	buf []byte
    39  }
    40  
    41  // NewMessage creates a new Message containing the passed header.
    42  //
    43  // The header length will be updated by Finalize.
    44  func NewMessage(hdr linux.NetlinkMessageHeader) *Message {
    45  	return &Message{
    46  		hdr: hdr,
    47  		buf: marshal.Marshal(&hdr),
    48  	}
    49  }
    50  
    51  // ParseMessage parses the first message seen at buf, returning the rest of the
    52  // buffer. If message is malformed, ok of false is returned. For last message,
    53  // padding check is loose, if there isn't enough padding, whole buf is consumed
    54  // and ok is set to true.
    55  func ParseMessage(buf []byte) (msg *Message, rest []byte, ok bool) {
    56  	b := BytesView(buf)
    57  
    58  	hdrBytes, ok := b.Extract(linux.NetlinkMessageHeaderSize)
    59  	if !ok {
    60  		return
    61  	}
    62  	var hdr linux.NetlinkMessageHeader
    63  	hdr.UnmarshalUnsafe(hdrBytes)
    64  
    65  	// Msg portion.
    66  	totalMsgLen := int(hdr.Length)
    67  	_, ok = b.Extract(totalMsgLen - linux.NetlinkMessageHeaderSize)
    68  	if !ok {
    69  		return
    70  	}
    71  
    72  	// Padding.
    73  	numPad := alignPad(totalMsgLen, linux.NLMSG_ALIGNTO)
    74  	// Linux permits the last message not being aligned, just consume all of it.
    75  	// Ref: net/netlink/af_netlink.c:netlink_rcv_skb
    76  	if numPad > len(b) {
    77  		numPad = len(b)
    78  	}
    79  	_, ok = b.Extract(numPad)
    80  	if !ok {
    81  		return
    82  	}
    83  
    84  	return &Message{
    85  		hdr: hdr,
    86  		buf: buf[:totalMsgLen],
    87  	}, []byte(b), true
    88  }
    89  
    90  // Header returns the header of this message.
    91  func (m *Message) Header() linux.NetlinkMessageHeader {
    92  	return m.hdr
    93  }
    94  
    95  // GetData unmarshals the payload message header from this netlink message, and
    96  // returns the attributes portion.
    97  func (m *Message) GetData(msg marshal.Marshallable) (AttrsView, bool) {
    98  	b := BytesView(m.buf)
    99  
   100  	_, ok := b.Extract(linux.NetlinkMessageHeaderSize)
   101  	if !ok {
   102  		return nil, false
   103  	}
   104  
   105  	size := msg.SizeBytes()
   106  	msgBytes, ok := b.Extract(size)
   107  	if !ok {
   108  		return nil, false
   109  	}
   110  	msg.UnmarshalUnsafe(msgBytes)
   111  
   112  	numPad := alignPad(linux.NetlinkMessageHeaderSize+size, linux.NLMSG_ALIGNTO)
   113  	// Linux permits the last message not being aligned, just consume all of it.
   114  	// Ref: net/netlink/af_netlink.c:netlink_rcv_skb
   115  	if numPad > len(b) {
   116  		numPad = len(b)
   117  	}
   118  	_, ok = b.Extract(numPad)
   119  	if !ok {
   120  		return nil, false
   121  	}
   122  
   123  	return AttrsView(b), true
   124  }
   125  
   126  // Finalize returns the []byte containing the entire message, with the total
   127  // length set in the message header. The Message must not be modified after
   128  // calling Finalize.
   129  func (m *Message) Finalize() []byte {
   130  	// Update length, which is the first 4 bytes of the header.
   131  	hostarch.ByteOrder.PutUint32(m.buf, uint32(len(m.buf)))
   132  
   133  	// Align the message. Note that the message length in the header (set
   134  	// above) is the useful length of the message, not the total aligned
   135  	// length. See net/netlink/af_netlink.c:__nlmsg_put.
   136  	aligned := bits.AlignUp(len(m.buf), linux.NLMSG_ALIGNTO)
   137  	m.putZeros(aligned - len(m.buf))
   138  	return m.buf
   139  }
   140  
   141  // putZeros adds n zeros to the message.
   142  func (m *Message) putZeros(n int) {
   143  	for n > 0 {
   144  		m.buf = append(m.buf, 0)
   145  		n--
   146  	}
   147  }
   148  
   149  // Put serializes v into the message.
   150  func (m *Message) Put(v marshal.Marshallable) {
   151  	m.buf = append(m.buf, marshal.Marshal(v)...)
   152  }
   153  
   154  // PutAttr adds v to the message as a netlink attribute.
   155  //
   156  // Preconditions: The serialized attribute (linux.NetlinkAttrHeaderSize +
   157  // v.SizeBytes()) fits in math.MaxUint16 bytes.
   158  func (m *Message) PutAttr(atype uint16, v marshal.Marshallable) {
   159  	l := linux.NetlinkAttrHeaderSize + v.SizeBytes()
   160  	if l > math.MaxUint16 {
   161  		panic(fmt.Sprintf("attribute too large: %d", l))
   162  	}
   163  
   164  	m.Put(&linux.NetlinkAttrHeader{
   165  		Type:   atype,
   166  		Length: uint16(l),
   167  	})
   168  	m.Put(v)
   169  
   170  	// Align the attribute.
   171  	aligned := bits.AlignUp(l, linux.NLA_ALIGNTO)
   172  	m.putZeros(aligned - l)
   173  }
   174  
   175  // PutAttrString adds s to the message as a netlink attribute.
   176  func (m *Message) PutAttrString(atype uint16, s string) {
   177  	l := linux.NetlinkAttrHeaderSize + len(s) + 1
   178  	m.Put(&linux.NetlinkAttrHeader{
   179  		Type:   atype,
   180  		Length: uint16(l),
   181  	})
   182  
   183  	// String + NUL-termination.
   184  	m.Put(primitive.AsByteSlice([]byte(s)))
   185  	m.putZeros(1)
   186  
   187  	// Align the attribute.
   188  	aligned := bits.AlignUp(l, linux.NLA_ALIGNTO)
   189  	m.putZeros(aligned - l)
   190  }
   191  
   192  // MessageSet contains a series of netlink messages.
   193  type MessageSet struct {
   194  	// Multi indicates that this a multi-part message, to be terminated by
   195  	// NLMSG_DONE. NLMSG_DONE is sent even if the set contains only one
   196  	// Message.
   197  	//
   198  	// If Multi is set, all added messages will have NLM_F_MULTI set.
   199  	Multi bool
   200  
   201  	// PortID is the destination port for all messages.
   202  	PortID int32
   203  
   204  	// Seq is the sequence counter for all messages in the set.
   205  	Seq uint32
   206  
   207  	// Messages contains the messages in the set.
   208  	Messages []*Message
   209  }
   210  
   211  // NewMessageSet creates a new MessageSet.
   212  //
   213  // portID is the destination port to set as PortID in all messages.
   214  //
   215  // seq is the sequence counter to set as seq in all messages in the set.
   216  func NewMessageSet(portID int32, seq uint32) *MessageSet {
   217  	return &MessageSet{
   218  		PortID: portID,
   219  		Seq:    seq,
   220  	}
   221  }
   222  
   223  // AddMessage adds a new message to the set and returns it for further
   224  // additions.
   225  //
   226  // The passed header will have Seq, PortID and the multi flag set
   227  // automatically.
   228  func (ms *MessageSet) AddMessage(hdr linux.NetlinkMessageHeader) *Message {
   229  	hdr.Seq = ms.Seq
   230  	hdr.PortID = uint32(ms.PortID)
   231  	if ms.Multi {
   232  		hdr.Flags |= linux.NLM_F_MULTI
   233  	}
   234  
   235  	m := NewMessage(hdr)
   236  	ms.Messages = append(ms.Messages, m)
   237  	return m
   238  }
   239  
   240  // AttrsView is a view into the attributes portion of a netlink message.
   241  type AttrsView []byte
   242  
   243  // Empty returns whether there is no attribute left in v.
   244  func (v AttrsView) Empty() bool {
   245  	return len(v) == 0
   246  }
   247  
   248  // ParseFirst parses first netlink attribute at the beginning of v.
   249  func (v AttrsView) ParseFirst() (hdr linux.NetlinkAttrHeader, value []byte, rest AttrsView, ok bool) {
   250  	b := BytesView(v)
   251  
   252  	hdrBytes, ok := b.Extract(linux.NetlinkAttrHeaderSize)
   253  	if !ok {
   254  		return
   255  	}
   256  	hdr.UnmarshalUnsafe(hdrBytes)
   257  
   258  	value, ok = b.Extract(int(hdr.Length) - linux.NetlinkAttrHeaderSize)
   259  	if !ok {
   260  		return
   261  	}
   262  
   263  	_, ok = b.Extract(alignPad(int(hdr.Length), linux.NLA_ALIGNTO))
   264  	if !ok {
   265  		return
   266  	}
   267  
   268  	return hdr, value, AttrsView(b), ok
   269  }
   270  
   271  // BytesView supports extracting data from a byte slice with bounds checking.
   272  type BytesView []byte
   273  
   274  // Extract removes the first n bytes from v and returns it. If n is out of
   275  // bounds, it returns false.
   276  func (v *BytesView) Extract(n int) ([]byte, bool) {
   277  	if n < 0 || n > len(*v) {
   278  		return nil, false
   279  	}
   280  	extracted := (*v)[:n]
   281  	*v = (*v)[n:]
   282  	return extracted, true
   283  }