github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/socket/hostinet/netlink.go (about)

     1  // Copyright 2023 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 hostinet
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"syscall"
    21  
    22  	"github.com/ttpreport/gvisor-ligolo/pkg/abi/linux"
    23  	"github.com/ttpreport/gvisor-ligolo/pkg/binary"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    25  	"github.com/ttpreport/gvisor-ligolo/pkg/hostarch"
    26  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal"
    27  	"github.com/ttpreport/gvisor-ligolo/pkg/marshal/primitive"
    28  	"github.com/ttpreport/gvisor-ligolo/pkg/sentry/inet"
    29  	"github.com/ttpreport/gvisor-ligolo/pkg/tcpip"
    30  	"golang.org/x/sys/unix"
    31  )
    32  
    33  func getInterfaces() (map[int32]inet.Interface, error) {
    34  	data, err := syscall.NetlinkRIB(unix.RTM_GETLINK, syscall.AF_UNSPEC)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	msgs, err := syscall.ParseNetlinkMessage(data)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	ifs := make(map[int32]inet.Interface, len(msgs))
    43  	for _, msg := range msgs {
    44  		if msg.Header.Type != unix.RTM_NEWLINK {
    45  			continue
    46  		}
    47  		if len(msg.Data) < unix.SizeofIfInfomsg {
    48  			return nil, fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid data length (%d bytes, expected at least %d bytes)", len(msg.Data), unix.SizeofIfInfomsg)
    49  		}
    50  		var ifinfo linux.InterfaceInfoMessage
    51  		ifinfo.UnmarshalUnsafe(msg.Data)
    52  		inetIF := inet.Interface{
    53  			DeviceType: ifinfo.Type,
    54  			Flags:      ifinfo.Flags,
    55  		}
    56  		// Not clearly documented: syscall.ParseNetlinkRouteAttr will check the
    57  		// syscall.NetlinkMessage.Header.Type and skip the struct ifinfomsg
    58  		// accordingly.
    59  		attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
    60  		if err != nil {
    61  			return nil, fmt.Errorf("RTM_GETLINK returned RTM_NEWLINK message with invalid rtattrs: %v", err)
    62  		}
    63  		for _, attr := range attrs {
    64  			switch attr.Attr.Type {
    65  			case unix.IFLA_ADDRESS:
    66  				inetIF.Addr = attr.Value
    67  			case unix.IFLA_IFNAME:
    68  				inetIF.Name = string(attr.Value[:len(attr.Value)-1])
    69  			}
    70  		}
    71  		ifs[ifinfo.Index] = inetIF
    72  	}
    73  	return ifs, nil
    74  }
    75  
    76  func getInterfaceAddrs() (map[int32][]inet.InterfaceAddr, error) {
    77  	data, err := syscall.NetlinkRIB(unix.RTM_GETADDR, syscall.AF_UNSPEC)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	msgs, err := syscall.ParseNetlinkMessage(data)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	addrs := make(map[int32][]inet.InterfaceAddr, len(msgs))
    86  	for _, msg := range msgs {
    87  		if msg.Header.Type != unix.RTM_NEWADDR {
    88  			continue
    89  		}
    90  		if len(msg.Data) < unix.SizeofIfAddrmsg {
    91  			return nil, fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid data length (%d bytes, expected at least %d bytes)", len(msg.Data), unix.SizeofIfAddrmsg)
    92  		}
    93  		var ifaddr linux.InterfaceAddrMessage
    94  		ifaddr.UnmarshalUnsafe(msg.Data)
    95  		inetAddr := inet.InterfaceAddr{
    96  			Family:    ifaddr.Family,
    97  			PrefixLen: ifaddr.PrefixLen,
    98  			Flags:     ifaddr.Flags,
    99  		}
   100  		attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
   101  		if err != nil {
   102  			return nil, fmt.Errorf("RTM_GETADDR returned RTM_NEWADDR message with invalid rtattrs: %v", err)
   103  		}
   104  		for _, attr := range attrs {
   105  			switch attr.Attr.Type {
   106  			case unix.IFA_ADDRESS:
   107  				inetAddr.Addr = attr.Value
   108  			}
   109  		}
   110  		addrs[int32(ifaddr.Index)] = append(addrs[int32(ifaddr.Index)], inetAddr)
   111  
   112  	}
   113  	return addrs, nil
   114  }
   115  
   116  func getRoutes() ([]inet.Route, error) {
   117  	data, err := syscall.NetlinkRIB(unix.RTM_GETROUTE, syscall.AF_UNSPEC)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	msgs, err := syscall.ParseNetlinkMessage(data)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	routes, err := extractRoutes(msgs)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return routes, nil
   131  }
   132  
   133  // extractRoutes populates the given routes slice with the data from the host
   134  // route table.
   135  func extractRoutes(routeMsgs []syscall.NetlinkMessage) ([]inet.Route, error) {
   136  	var routes []inet.Route
   137  	for _, routeMsg := range routeMsgs {
   138  		if routeMsg.Header.Type != unix.RTM_NEWROUTE {
   139  			continue
   140  		}
   141  
   142  		var ifRoute linux.RouteMessage
   143  		ifRoute.UnmarshalUnsafe(routeMsg.Data)
   144  		inetRoute := inet.Route{
   145  			Family:   ifRoute.Family,
   146  			DstLen:   ifRoute.DstLen,
   147  			SrcLen:   ifRoute.SrcLen,
   148  			TOS:      ifRoute.TOS,
   149  			Table:    ifRoute.Table,
   150  			Protocol: ifRoute.Protocol,
   151  			Scope:    ifRoute.Scope,
   152  			Type:     ifRoute.Type,
   153  			Flags:    ifRoute.Flags,
   154  		}
   155  
   156  		// Not clearly documented: syscall.ParseNetlinkRouteAttr will check the
   157  		// syscall.NetlinkMessage.Header.Type and skip the struct rtmsg
   158  		// accordingly.
   159  		attrs, err := syscall.ParseNetlinkRouteAttr(&routeMsg)
   160  		if err != nil {
   161  			return nil, fmt.Errorf("RTM_GETROUTE returned RTM_NEWROUTE message with invalid rtattrs: %v", err)
   162  		}
   163  
   164  		for _, attr := range attrs {
   165  			switch attr.Attr.Type {
   166  			case unix.RTA_DST:
   167  				inetRoute.DstAddr = attr.Value
   168  			case unix.RTA_SRC:
   169  				inetRoute.SrcAddr = attr.Value
   170  			case unix.RTA_GATEWAY:
   171  				inetRoute.GatewayAddr = attr.Value
   172  			case unix.RTA_OIF:
   173  				expected := int(binary.Size(inetRoute.OutputInterface))
   174  				if len(attr.Value) != expected {
   175  					return nil, fmt.Errorf("RTM_GETROUTE returned RTM_NEWROUTE message with invalid attribute data length (%d bytes, expected %d bytes)", len(attr.Value), expected)
   176  				}
   177  				var outputIF primitive.Int32
   178  				outputIF.UnmarshalUnsafe(attr.Value)
   179  				inetRoute.OutputInterface = int32(outputIF)
   180  			}
   181  		}
   182  
   183  		routes = append(routes, inetRoute)
   184  	}
   185  
   186  	return routes, nil
   187  }
   188  
   189  // doNetlinkRouteRequest is a more general form of syscall.NetlinkRIB that
   190  // allows sending arbitrary (marshallable) structs to the netlink socket.
   191  func doNetlinkRouteRequest(msgs []marshal.Marshallable) error {
   192  	s, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.NETLINK_ROUTE)
   193  	if err != nil {
   194  		return err
   195  	}
   196  	defer syscall.Close(s)
   197  	sa := syscall.SockaddrNetlink{Family: unix.AF_NETLINK}
   198  	if err := syscall.Bind(s, &sa); err != nil {
   199  		return err
   200  	}
   201  
   202  	b := marshal.MarshalAll(msgs)
   203  	if err := syscall.Sendto(s, b, 0, &sa); err != nil {
   204  		return err
   205  	}
   206  
   207  	lsa, err := syscall.Getsockname(s)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	lsanl, ok := lsa.(*syscall.SockaddrNetlink)
   212  	if !ok {
   213  		return linuxerr.EINVAL
   214  	}
   215  	rbNew := make([]byte, hostarch.PageSize)
   216  done:
   217  	for {
   218  		rb := rbNew
   219  		nr, _, err := syscall.Recvfrom(s, rb, 0)
   220  		if err != nil {
   221  			return err
   222  		}
   223  		if nr < linux.NetlinkMessageHeaderSize {
   224  			return linuxerr.EINVAL
   225  		}
   226  		rb = rb[:nr]
   227  		msgs, err := syscall.ParseNetlinkMessage(rb)
   228  		if err != nil {
   229  			return err
   230  		}
   231  		for _, m := range msgs {
   232  			if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid {
   233  				return linuxerr.EINVAL
   234  			}
   235  			if m.Header.Type == linux.NLMSG_DONE {
   236  				break done
   237  			}
   238  			if m.Header.Type == linux.NLMSG_ERROR {
   239  				errno, err := binary.ReadUint32(bytes.NewReader(m.Data[0:4]), hostarch.ByteOrder)
   240  				if err != nil {
   241  					return err
   242  				}
   243  				if errno == 0 {
   244  					break done
   245  				}
   246  				return linuxerr.ErrorFromUnix(unix.Errno(-errno))
   247  			}
   248  		}
   249  	}
   250  	return nil
   251  }
   252  
   253  func removeInterface(idx int32) error {
   254  	// [ NetlinkMessageHeader | InterfaceInfoMessage ]
   255  	hdr := linux.NetlinkMessageHeader{
   256  		Type:  linux.RTM_DELLINK,
   257  		Flags: linux.NLM_F_REQUEST | linux.NLM_F_ACK,
   258  		Seq:   1,
   259  	}
   260  	infoMsg := linux.InterfaceInfoMessage{
   261  		Family: linux.AF_UNSPEC,
   262  		Index:  idx,
   263  	}
   264  
   265  	msgs := []marshal.Marshallable{
   266  		&hdr,
   267  		&infoMsg,
   268  	}
   269  	hdr.Length = uint32(marshal.TotalSize(msgs))
   270  	return doNetlinkRouteRequest(msgs)
   271  }
   272  
   273  func doNetlinkInterfaceRequest(typ, flags uint16, idx uint32, addr inet.InterfaceAddr) error {
   274  	// [ NetlinkMessageHeader | InterfaceAddrMessage | RtAttr | localAddr | RtAttr | peerAddr ]
   275  	hdr := linux.NetlinkMessageHeader{
   276  		Type:  typ,
   277  		Flags: flags | linux.NLM_F_REQUEST | linux.NLM_F_ACK,
   278  		Seq:   1,
   279  	}
   280  	infoMsg := linux.InterfaceAddrMessage{
   281  		Family:    addr.Family,
   282  		Index:     idx,
   283  		PrefixLen: addr.PrefixLen,
   284  		Flags:     addr.Flags,
   285  	}
   286  	// Local address.
   287  	localAddr := tcpip.AddrFromSlice(addr.Addr)
   288  	if addr.Family == linux.AF_INET {
   289  		localAddr = localAddr.To4()
   290  	}
   291  	rtLocal := linux.RtAttr{
   292  		Len:  linux.SizeOfRtAttr + uint16(localAddr.Len()),
   293  		Type: linux.IFA_LOCAL,
   294  	}
   295  	localAddrBs := primitive.ByteSlice(localAddr.AsSlice())
   296  	// Peer is always the local address for us.
   297  	rtPeer := linux.RtAttr{
   298  		Len:  linux.SizeOfRtAttr + uint16(localAddr.Len()),
   299  		Type: linux.IFA_ADDRESS,
   300  	}
   301  	peerAddrBs := primitive.ByteSlice(localAddr.AsSlice())
   302  
   303  	msgs := []marshal.Marshallable{
   304  		&hdr,
   305  		&infoMsg,
   306  		&rtLocal,
   307  		&localAddrBs,
   308  		&rtPeer,
   309  		&peerAddrBs,
   310  	}
   311  	hdr.Length = uint32(marshal.TotalSize(msgs))
   312  	return doNetlinkRouteRequest(msgs)
   313  }
   314  
   315  func addInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
   316  	return doNetlinkInterfaceRequest(linux.RTM_NEWADDR, linux.NLM_F_CREATE, uint32(idx), addr)
   317  }
   318  
   319  func removeInterfaceAddr(idx int32, addr inet.InterfaceAddr) error {
   320  	return doNetlinkInterfaceRequest(linux.RTM_DELADDR, 0, uint32(idx), addr)
   321  }