github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/boot/network.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 boot
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"runtime"
    21  	"strings"
    22  
    23  	"golang.org/x/sys/unix"
    24  	"github.com/SagerNet/gvisor/pkg/log"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip/link/fdbased"
    27  	"github.com/SagerNet/gvisor/pkg/tcpip/link/loopback"
    28  	"github.com/SagerNet/gvisor/pkg/tcpip/link/packetsocket"
    29  	"github.com/SagerNet/gvisor/pkg/tcpip/link/qdisc/fifo"
    30  	"github.com/SagerNet/gvisor/pkg/tcpip/link/sniffer"
    31  	"github.com/SagerNet/gvisor/pkg/tcpip/network/ipv4"
    32  	"github.com/SagerNet/gvisor/pkg/tcpip/network/ipv6"
    33  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    34  	"github.com/SagerNet/gvisor/pkg/urpc"
    35  	"github.com/SagerNet/gvisor/runsc/config"
    36  )
    37  
    38  var (
    39  	// DefaultLoopbackLink contains IP addresses and routes of "127.0.0.1/8" and
    40  	// "::1/8" on "lo" interface.
    41  	DefaultLoopbackLink = LoopbackLink{
    42  		Name: "lo",
    43  		Addresses: []IPWithPrefix{
    44  			{Address: net.IP("\x7f\x00\x00\x01"), PrefixLen: 8},
    45  			{Address: net.IPv6loopback, PrefixLen: 128},
    46  		},
    47  		Routes: []Route{
    48  			{
    49  				Destination: net.IPNet{
    50  					IP:   net.IPv4(0x7f, 0, 0, 0),
    51  					Mask: net.IPv4Mask(0xff, 0, 0, 0),
    52  				},
    53  			},
    54  			{
    55  				Destination: net.IPNet{
    56  					IP:   net.IPv6loopback,
    57  					Mask: net.IPMask(strings.Repeat("\xff", net.IPv6len)),
    58  				},
    59  			},
    60  		},
    61  	}
    62  )
    63  
    64  // Network exposes methods that can be used to configure a network stack.
    65  type Network struct {
    66  	Stack *stack.Stack
    67  }
    68  
    69  // Route represents a route in the network stack.
    70  type Route struct {
    71  	Destination net.IPNet
    72  	Gateway     net.IP
    73  }
    74  
    75  // DefaultRoute represents a catch all route to the default gateway.
    76  type DefaultRoute struct {
    77  	Route Route
    78  	Name  string
    79  }
    80  
    81  // FDBasedLink configures an fd-based link.
    82  type FDBasedLink struct {
    83  	Name               string
    84  	MTU                int
    85  	Addresses          []IPWithPrefix
    86  	Routes             []Route
    87  	GSOMaxSize         uint32
    88  	SoftwareGSOEnabled bool
    89  	TXChecksumOffload  bool
    90  	RXChecksumOffload  bool
    91  	LinkAddress        net.HardwareAddr
    92  	QDisc              config.QueueingDiscipline
    93  
    94  	// NumChannels controls how many underlying FD's are to be used to
    95  	// create this endpoint.
    96  	NumChannels int
    97  }
    98  
    99  // LoopbackLink configures a loopback li nk.
   100  type LoopbackLink struct {
   101  	Name      string
   102  	Addresses []IPWithPrefix
   103  	Routes    []Route
   104  }
   105  
   106  // CreateLinksAndRoutesArgs are arguments to CreateLinkAndRoutes.
   107  type CreateLinksAndRoutesArgs struct {
   108  	// FilePayload contains the fds associated with the FDBasedLinks. The
   109  	// number of fd's should match the sum of the NumChannels field of the
   110  	// FDBasedLink entries below.
   111  	urpc.FilePayload
   112  
   113  	LoopbackLinks []LoopbackLink
   114  	FDBasedLinks  []FDBasedLink
   115  
   116  	Defaultv4Gateway DefaultRoute
   117  	Defaultv6Gateway DefaultRoute
   118  }
   119  
   120  // IPWithPrefix is an address with its subnet prefix length.
   121  type IPWithPrefix struct {
   122  	// Address is a network address.
   123  	Address net.IP
   124  
   125  	// PrefixLen is the subnet prefix length.
   126  	PrefixLen int
   127  }
   128  
   129  func (ip IPWithPrefix) String() string {
   130  	return fmt.Sprintf("%s/%d", ip.Address, ip.PrefixLen)
   131  }
   132  
   133  // Empty returns true if route hasn't been set.
   134  func (r *Route) Empty() bool {
   135  	return r.Destination.IP == nil && r.Destination.Mask == nil && r.Gateway == nil
   136  }
   137  
   138  func (r *Route) toTcpipRoute(id tcpip.NICID) (tcpip.Route, error) {
   139  	subnet, err := tcpip.NewSubnet(ipToAddress(r.Destination.IP), ipMaskToAddressMask(r.Destination.Mask))
   140  	if err != nil {
   141  		return tcpip.Route{}, err
   142  	}
   143  	return tcpip.Route{
   144  		Destination: subnet,
   145  		Gateway:     ipToAddress(r.Gateway),
   146  		NIC:         id,
   147  	}, nil
   148  }
   149  
   150  // CreateLinksAndRoutes creates links and routes in a network stack.  It should
   151  // only be called once.
   152  func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct{}) error {
   153  	wantFDs := 0
   154  	for _, l := range args.FDBasedLinks {
   155  		wantFDs += l.NumChannels
   156  	}
   157  	if got := len(args.FilePayload.Files); got != wantFDs {
   158  		return fmt.Errorf("args.FilePayload.Files has %d FD's but we need %d entries based on FDBasedLinks", got, wantFDs)
   159  	}
   160  
   161  	var nicID tcpip.NICID
   162  	nicids := make(map[string]tcpip.NICID)
   163  
   164  	// Collect routes from all links.
   165  	var routes []tcpip.Route
   166  
   167  	// Loopback normally appear before other interfaces.
   168  	for _, link := range args.LoopbackLinks {
   169  		nicID++
   170  		nicids[link.Name] = nicID
   171  
   172  		linkEP := loopback.New()
   173  
   174  		log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses)
   175  		if err := n.createNICWithAddrs(nicID, link.Name, linkEP, link.Addresses); err != nil {
   176  			return err
   177  		}
   178  
   179  		// Collect the routes from this link.
   180  		for _, r := range link.Routes {
   181  			route, err := r.toTcpipRoute(nicID)
   182  			if err != nil {
   183  				return err
   184  			}
   185  			routes = append(routes, route)
   186  		}
   187  	}
   188  
   189  	fdOffset := 0
   190  	for _, link := range args.FDBasedLinks {
   191  		nicID++
   192  		nicids[link.Name] = nicID
   193  
   194  		FDs := []int{}
   195  		for j := 0; j < link.NumChannels; j++ {
   196  			// Copy the underlying FD.
   197  			oldFD := args.FilePayload.Files[fdOffset].Fd()
   198  			newFD, err := unix.Dup(int(oldFD))
   199  			if err != nil {
   200  				return fmt.Errorf("failed to dup FD %v: %v", oldFD, err)
   201  			}
   202  			FDs = append(FDs, newFD)
   203  			fdOffset++
   204  		}
   205  
   206  		mac := tcpip.LinkAddress(link.LinkAddress)
   207  		log.Infof("gso max size is: %d", link.GSOMaxSize)
   208  
   209  		linkEP, err := fdbased.New(&fdbased.Options{
   210  			FDs:                FDs,
   211  			MTU:                uint32(link.MTU),
   212  			EthernetHeader:     true,
   213  			Address:            mac,
   214  			PacketDispatchMode: fdbased.RecvMMsg,
   215  			GSOMaxSize:         link.GSOMaxSize,
   216  			SoftwareGSOEnabled: link.SoftwareGSOEnabled,
   217  			TXChecksumOffload:  link.TXChecksumOffload,
   218  			RXChecksumOffload:  link.RXChecksumOffload,
   219  		})
   220  		if err != nil {
   221  			return err
   222  		}
   223  
   224  		switch link.QDisc {
   225  		case config.QDiscNone:
   226  		case config.QDiscFIFO:
   227  			log.Infof("Enabling FIFO QDisc on %q", link.Name)
   228  			linkEP = fifo.New(linkEP, runtime.GOMAXPROCS(0), 1000)
   229  		}
   230  
   231  		// Enable support for AF_PACKET sockets to receive outgoing packets.
   232  		linkEP = packetsocket.New(linkEP)
   233  
   234  		log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels)
   235  		if err := n.createNICWithAddrs(nicID, link.Name, linkEP, link.Addresses); err != nil {
   236  			return err
   237  		}
   238  
   239  		// Collect the routes from this link.
   240  		for _, r := range link.Routes {
   241  			route, err := r.toTcpipRoute(nicID)
   242  			if err != nil {
   243  				return err
   244  			}
   245  			routes = append(routes, route)
   246  		}
   247  	}
   248  
   249  	if !args.Defaultv4Gateway.Route.Empty() {
   250  		nicID, ok := nicids[args.Defaultv4Gateway.Name]
   251  		if !ok {
   252  			return fmt.Errorf("invalid interface name %q for default route", args.Defaultv4Gateway.Name)
   253  		}
   254  		route, err := args.Defaultv4Gateway.Route.toTcpipRoute(nicID)
   255  		if err != nil {
   256  			return err
   257  		}
   258  		routes = append(routes, route)
   259  	}
   260  
   261  	if !args.Defaultv6Gateway.Route.Empty() {
   262  		nicID, ok := nicids[args.Defaultv6Gateway.Name]
   263  		if !ok {
   264  			return fmt.Errorf("invalid interface name %q for default route", args.Defaultv6Gateway.Name)
   265  		}
   266  		route, err := args.Defaultv6Gateway.Route.toTcpipRoute(nicID)
   267  		if err != nil {
   268  			return err
   269  		}
   270  		routes = append(routes, route)
   271  	}
   272  
   273  	log.Infof("Setting routes %+v", routes)
   274  	n.Stack.SetRouteTable(routes)
   275  	return nil
   276  }
   277  
   278  // createNICWithAddrs creates a NIC in the network stack and adds the given
   279  // addresses.
   280  func (n *Network) createNICWithAddrs(id tcpip.NICID, name string, ep stack.LinkEndpoint, addrs []IPWithPrefix) error {
   281  	opts := stack.NICOptions{Name: name}
   282  	if err := n.Stack.CreateNICWithOptions(id, sniffer.New(ep), opts); err != nil {
   283  		return fmt.Errorf("CreateNICWithOptions(%d, _, %+v) failed: %v", id, opts, err)
   284  	}
   285  
   286  	for _, addr := range addrs {
   287  		proto, tcpipAddr := ipToAddressAndProto(addr.Address)
   288  		ap := tcpip.AddressWithPrefix{
   289  			Address:   tcpipAddr,
   290  			PrefixLen: addr.PrefixLen,
   291  		}
   292  		if err := n.Stack.AddAddressWithPrefix(id, proto, ap); err != nil {
   293  			return fmt.Errorf("AddAddress(%v, %v, %v) failed: %v", id, proto, tcpipAddr, err)
   294  		}
   295  	}
   296  	return nil
   297  }
   298  
   299  // ipToAddressAndProto converts IP to tcpip.Address and a protocol number.
   300  //
   301  // Note: don't use 'len(ip)' to determine IP version because length is always 16.
   302  func ipToAddressAndProto(ip net.IP) (tcpip.NetworkProtocolNumber, tcpip.Address) {
   303  	if i4 := ip.To4(); i4 != nil {
   304  		return ipv4.ProtocolNumber, tcpip.Address(i4)
   305  	}
   306  	return ipv6.ProtocolNumber, tcpip.Address(ip)
   307  }
   308  
   309  // ipToAddress converts IP to tcpip.Address, ignoring the protocol.
   310  func ipToAddress(ip net.IP) tcpip.Address {
   311  	_, addr := ipToAddressAndProto(ip)
   312  	return addr
   313  }
   314  
   315  // ipMaskToAddressMask converts IPMask to tcpip.AddressMask, ignoring the
   316  // protocol.
   317  func ipMaskToAddressMask(ipMask net.IPMask) tcpip.AddressMask {
   318  	return tcpip.AddressMask(ipToAddress(net.IP(ipMask)))
   319  }