...

Source file src/github.com/Microsoft/hcsshim/internal/uvm/network.go

Documentation: github.com/Microsoft/hcsshim/internal/uvm

     1  //go:build windows
     2  
     3  package uvm
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os"
     9  
    10  	"github.com/Microsoft/go-winio"
    11  	"github.com/Microsoft/go-winio/pkg/guid"
    12  	"github.com/containerd/ttrpc"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  
    16  	"github.com/Microsoft/hcsshim/hcn"
    17  	"github.com/Microsoft/hcsshim/internal/hcs/resourcepaths"
    18  	hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
    19  	"github.com/Microsoft/hcsshim/internal/hns"
    20  	"github.com/Microsoft/hcsshim/internal/log"
    21  	"github.com/Microsoft/hcsshim/internal/ncproxyttrpc"
    22  	"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
    23  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    24  	"github.com/Microsoft/hcsshim/osversion"
    25  )
    26  
    27  var (
    28  	// ErrNetNSAlreadyAttached is an error indicating the guest UVM already has
    29  	// an endpoint by this id.
    30  	ErrNetNSAlreadyAttached = errors.New("network namespace already added")
    31  	// ErrNetNSNotFound is an error indicating the guest UVM does not have a
    32  	// network namespace by this id.
    33  	ErrNetNSNotFound = errors.New("network namespace not found")
    34  	// ErrNICNotFound is an error indicating that the guest UVM does not have a NIC
    35  	// by this id.
    36  	ErrNICNotFound = errors.New("NIC not found in network namespace")
    37  )
    38  
    39  // In this function we take the namespace ID of the namespace that was created for this
    40  // UVM. We hot add the namespace. We get the endpoints associated with this namespace
    41  // and then hot add those endpoints.
    42  func (uvm *UtilityVM) SetupNetworkNamespace(ctx context.Context, nsid string) error {
    43  	nsidInsideUVM := nsid
    44  
    45  	// Query endpoints with actual nsid
    46  	endpoints, err := GetNamespaceEndpoints(ctx, nsid)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	// Add the network namespace inside the UVM.
    52  	// Get the namespace struct from the actual nsid.
    53  	hcnNamespace, err := hcn.GetNamespaceByID(nsid)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	if err = uvm.AddNetNS(ctx, hcnNamespace); err != nil {
    59  		return err
    60  	}
    61  
    62  	if err = uvm.AddEndpointsToNS(ctx, nsidInsideUVM, endpoints); err != nil {
    63  		// Best effort clean up the NS
    64  		if removeErr := uvm.RemoveNetNS(ctx, nsidInsideUVM); removeErr != nil {
    65  			log.G(ctx).Warn(removeErr)
    66  		}
    67  		return err
    68  	}
    69  	return nil
    70  }
    71  
    72  // GetNamespaceEndpoints gets all endpoints in `netNS`
    73  func GetNamespaceEndpoints(ctx context.Context, netNS string) ([]*hns.HNSEndpoint, error) {
    74  	op := "uvm::GetNamespaceEndpoints"
    75  	l := log.G(ctx).WithField("netns-id", netNS)
    76  	l.Debug(op + " - Begin")
    77  	defer func() {
    78  		l.Debug(op + " - End")
    79  	}()
    80  
    81  	ids, err := hns.GetNamespaceEndpoints(netNS)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	var endpoints []*hns.HNSEndpoint
    86  	for _, id := range ids {
    87  		endpoint, err := hns.GetHNSEndpointByID(id)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  		endpoints = append(endpoints, endpoint)
    92  	}
    93  	return endpoints, nil
    94  }
    95  
    96  // NCProxyEnabled returns if there is a network configuration client.
    97  func (uvm *UtilityVM) NCProxyEnabled() bool {
    98  	return uvm.ncProxyClientAddress != ""
    99  }
   100  
   101  type ncproxyClient struct {
   102  	raw *ttrpc.Client
   103  	ncproxyttrpc.NetworkConfigProxyService
   104  }
   105  
   106  func (n *ncproxyClient) Close() error {
   107  	return n.raw.Close()
   108  }
   109  
   110  func (uvm *UtilityVM) GetNCProxyClient() (*ncproxyClient, error) {
   111  	conn, err := winio.DialPipe(uvm.ncProxyClientAddress, nil)
   112  	if err != nil {
   113  		return nil, errors.Wrap(err, "failed to connect to ncproxy service")
   114  	}
   115  	raw := ttrpc.NewClient(conn, ttrpc.WithOnClose(func() { conn.Close() }))
   116  	return &ncproxyClient{raw, ncproxyttrpc.NewNetworkConfigProxyClient(raw)}, nil
   117  }
   118  
   119  // NetworkConfigType specifies the action to be performed during network configuration.
   120  // For example: setup or teardown
   121  type NetworkConfigType uint8
   122  
   123  const (
   124  	NetworkRequestSetup NetworkConfigType = iota
   125  	NetworkRequestTearDown
   126  )
   127  
   128  var ErrNoNetworkSetup = errors.New("no network setup present for UVM")
   129  
   130  // CreateAndAssignNetworkSetup creates and assigns a new NetworkSetup interface to the Utility VM.
   131  // This can be used to configure the networking (setup and teardown) of the vm.
   132  //
   133  // `addr` is an optional parameter
   134  func (uvm *UtilityVM) CreateAndAssignNetworkSetup(ctx context.Context, addr, containerID string) (err error) {
   135  	if uvm.NCProxyEnabled() {
   136  		if addr == "" || containerID == "" {
   137  			return errors.New("received empty field(s) for external network setup")
   138  		}
   139  		setup, err := NewExternalNetworkSetup(ctx, uvm, addr, containerID)
   140  		if err != nil {
   141  			return err
   142  		}
   143  		uvm.networkSetup = setup
   144  	} else {
   145  		uvm.networkSetup = NewInternalNetworkSetup(uvm)
   146  	}
   147  	return nil
   148  }
   149  
   150  // ConfigureNetworking configures the utility VMs networking setup using the namespace ID
   151  // `nsid`.
   152  func (uvm *UtilityVM) ConfigureNetworking(ctx context.Context, nsid string) error {
   153  	if uvm.networkSetup != nil {
   154  		return uvm.networkSetup.ConfigureNetworking(ctx, nsid, NetworkRequestSetup)
   155  	}
   156  	return ErrNoNetworkSetup
   157  }
   158  
   159  // TearDownNetworking tears down the utility VMs networking setup using the namespace ID
   160  // `nsid`.
   161  func (uvm *UtilityVM) TearDownNetworking(ctx context.Context, nsid string) error {
   162  	if uvm.networkSetup != nil {
   163  		return uvm.networkSetup.ConfigureNetworking(ctx, nsid, NetworkRequestTearDown)
   164  	}
   165  	return ErrNoNetworkSetup
   166  }
   167  
   168  // NetworkSetup is used to abstract away the details of setting up networking
   169  // for a container.
   170  type NetworkSetup interface {
   171  	ConfigureNetworking(ctx context.Context, namespaceID string, configType NetworkConfigType) error
   172  }
   173  
   174  // LocalNetworkSetup implements the NetworkSetup interface for configuring container
   175  // networking.
   176  type internalNetworkSetup struct {
   177  	vm *UtilityVM
   178  }
   179  
   180  func NewInternalNetworkSetup(vm *UtilityVM) NetworkSetup {
   181  	return &internalNetworkSetup{vm}
   182  }
   183  
   184  func (i *internalNetworkSetup) ConfigureNetworking(ctx context.Context, namespaceID string, configType NetworkConfigType) error {
   185  	switch configType {
   186  	case NetworkRequestSetup:
   187  		if err := i.vm.SetupNetworkNamespace(ctx, namespaceID); err != nil {
   188  			return err
   189  		}
   190  	case NetworkRequestTearDown:
   191  		if err := i.vm.RemoveNetNS(ctx, namespaceID); err != nil {
   192  			return err
   193  		}
   194  	default:
   195  		return fmt.Errorf("network configuration type %d is not known", configType)
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  // ExternalNetworkSetup implements the NetworkSetup interface for configuring
   202  // container networking. It will try and communicate with an external network configuration
   203  // proxy service to setup networking.
   204  type externalNetworkSetup struct {
   205  	vm          *UtilityVM
   206  	caAddr      string
   207  	containerID string
   208  }
   209  
   210  // NewExternalNetworkSetup returns an object implementing the NetworkSetup interface to be
   211  // used for external network configuration.
   212  func NewExternalNetworkSetup(ctx context.Context, vm *UtilityVM, caAddr, containerID string) (NetworkSetup, error) {
   213  	if err := setupAndServe(ctx, caAddr, vm); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	return &externalNetworkSetup{
   218  		vm,
   219  		caAddr,
   220  		containerID,
   221  	}, nil
   222  }
   223  
   224  func (e *externalNetworkSetup) ConfigureNetworking(ctx context.Context, namespaceID string, configType NetworkConfigType) error {
   225  	client, err := e.vm.GetNCProxyClient()
   226  	if err != nil {
   227  		return errors.Wrapf(err, "no ncproxy client for UVM %q", e.vm.ID())
   228  	}
   229  	defer client.Close()
   230  
   231  	netReq := &ncproxyttrpc.ConfigureNetworkingInternalRequest{
   232  		ContainerID: e.containerID,
   233  	}
   234  
   235  	switch configType {
   236  	case NetworkRequestSetup:
   237  		if err := e.vm.AddNetNSByID(ctx, namespaceID); err != nil {
   238  			return err
   239  		}
   240  
   241  		registerReq := &ncproxyttrpc.RegisterComputeAgentRequest{
   242  			ContainerID:  e.containerID,
   243  			AgentAddress: e.caAddr,
   244  		}
   245  		if _, err := client.RegisterComputeAgent(ctx, registerReq); err != nil {
   246  			return err
   247  		}
   248  
   249  		netReq.RequestType = ncproxyttrpc.RequestTypeInternal_Setup
   250  		if _, err := client.ConfigureNetworking(ctx, netReq); err != nil {
   251  			return err
   252  		}
   253  	case NetworkRequestTearDown:
   254  		netReq.RequestType = ncproxyttrpc.RequestTypeInternal_Teardown
   255  		if _, err := client.ConfigureNetworking(ctx, netReq); err != nil {
   256  			return err
   257  		}
   258  		// unregister compute agent with ncproxy
   259  		unregisterReq := &ncproxyttrpc.UnregisterComputeAgentRequest{
   260  			ContainerID: e.containerID,
   261  		}
   262  		if _, err := client.UnregisterComputeAgent(ctx, unregisterReq); err != nil {
   263  			return err
   264  		}
   265  	default:
   266  		return fmt.Errorf("network configuration type %d is not known", configType)
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  // NetworkEndpoints is a struct containing all of the endpoint IDs of a network
   273  // namespace.
   274  type NetworkEndpoints struct {
   275  	EndpointIDs []string
   276  	// ID of the namespace the endpoints belong to
   277  	Namespace string
   278  }
   279  
   280  // Release releases the resources for all of the network endpoints in a namespace.
   281  func (endpoints *NetworkEndpoints) Release(ctx context.Context) error {
   282  	for _, endpoint := range endpoints.EndpointIDs {
   283  		err := hns.RemoveNamespaceEndpoint(endpoints.Namespace, endpoint)
   284  		if err != nil {
   285  			if !os.IsNotExist(err) {
   286  				return err
   287  			}
   288  			log.G(ctx).WithFields(logrus.Fields{
   289  				"endpointID": endpoint,
   290  				"netID":      endpoints.Namespace,
   291  			}).Warn("removing endpoint from namespace: does not exist")
   292  		}
   293  	}
   294  	endpoints.EndpointIDs = nil
   295  	err := hns.RemoveNamespace(endpoints.Namespace)
   296  	if err != nil && !os.IsNotExist(err) {
   297  		return err
   298  	}
   299  	return nil
   300  }
   301  
   302  // AddNetNS adds network namespace inside the guest without actually querying for the
   303  // namespace by its ID. It uses the given namespace struct as it is in the guest request.
   304  // This function is mostly used when we need to override the values inside the namespace
   305  // struct returned by the GetNamespaceByID. For most uses cases AddNetNSByID is more appropriate.
   306  //
   307  // If a namespace with the same id already exists this returns `ErrNetNSAlreadyAttached`.
   308  func (uvm *UtilityVM) AddNetNS(ctx context.Context, hcnNamespace *hcn.HostComputeNamespace) error {
   309  	uvm.m.Lock()
   310  	defer uvm.m.Unlock()
   311  	if _, ok := uvm.namespaces[hcnNamespace.Id]; ok {
   312  		return ErrNetNSAlreadyAttached
   313  	}
   314  
   315  	if uvm.isNetworkNamespaceSupported() {
   316  		// Add a Guest Network namespace. On LCOW we add the adapters
   317  		// dynamically.
   318  		if uvm.operatingSystem == "windows" {
   319  			guestNamespace := hcsschema.ModifySettingRequest{
   320  				GuestRequest: guestrequest.ModificationRequest{
   321  					ResourceType: guestresource.ResourceTypeNetworkNamespace,
   322  					RequestType:  guestrequest.RequestTypeAdd,
   323  					Settings:     hcnNamespace,
   324  				},
   325  			}
   326  			if err := uvm.modify(ctx, &guestNamespace); err != nil {
   327  				return err
   328  			}
   329  		}
   330  	}
   331  
   332  	if uvm.namespaces == nil {
   333  		uvm.namespaces = make(map[string]*namespaceInfo)
   334  	}
   335  	uvm.namespaces[hcnNamespace.Id] = &namespaceInfo{
   336  		nics: make(map[string]*nicInfo),
   337  	}
   338  	return nil
   339  }
   340  
   341  // AddNetNSByID adds finds the namespace with given `id` and adds that
   342  // network namespace inside the guest.
   343  //
   344  // If a namespace with `id` already exists returns `ErrNetNSAlreadyAttached`.
   345  func (uvm *UtilityVM) AddNetNSByID(ctx context.Context, id string) error {
   346  	hcnNamespace, err := hcn.GetNamespaceByID(id)
   347  	if err != nil {
   348  		return err
   349  	}
   350  
   351  	if err = uvm.AddNetNS(ctx, hcnNamespace); err != nil {
   352  		return err
   353  	}
   354  	return nil
   355  }
   356  
   357  // AddEndpointToNSWithID adds an endpoint to the network namespace with the specified
   358  // NIC ID. If nicID is an empty string, a GUID will be generated for the ID instead.
   359  //
   360  // If no network namespace matches `id` returns `ErrNetNSNotFound`.
   361  func (uvm *UtilityVM) AddEndpointToNSWithID(ctx context.Context, nsID, nicID string, endpoint *hns.HNSEndpoint) error {
   362  	uvm.m.Lock()
   363  	defer uvm.m.Unlock()
   364  	ns, ok := uvm.namespaces[nsID]
   365  	if !ok {
   366  		return ErrNetNSNotFound
   367  	}
   368  	if _, ok := ns.nics[endpoint.Id]; !ok {
   369  		if nicID == "" {
   370  			id, err := guid.NewV4()
   371  			if err != nil {
   372  				return err
   373  			}
   374  			nicID = id.String()
   375  		}
   376  		if err := uvm.addNIC(ctx, nicID, endpoint); err != nil {
   377  			return err
   378  		}
   379  		ns.nics[endpoint.Id] = &nicInfo{
   380  			ID:       nicID,
   381  			Endpoint: endpoint,
   382  		}
   383  	}
   384  	return nil
   385  }
   386  
   387  // AddEndpointsToNS adds all unique `endpoints` to the network namespace
   388  // matching `id`. On failure does not roll back any previously successfully
   389  // added endpoints.
   390  //
   391  // If no network namespace matches `id` returns `ErrNetNSNotFound`.
   392  func (uvm *UtilityVM) AddEndpointsToNS(ctx context.Context, id string, endpoints []*hns.HNSEndpoint) error {
   393  	uvm.m.Lock()
   394  	defer uvm.m.Unlock()
   395  
   396  	ns, ok := uvm.namespaces[id]
   397  	if !ok {
   398  		return ErrNetNSNotFound
   399  	}
   400  
   401  	for _, endpoint := range endpoints {
   402  		if _, ok := ns.nics[endpoint.Id]; !ok {
   403  			nicID, err := guid.NewV4()
   404  			if err != nil {
   405  				return err
   406  			}
   407  			if err := uvm.addNIC(ctx, nicID.String(), endpoint); err != nil {
   408  				return err
   409  			}
   410  			ns.nics[endpoint.Id] = &nicInfo{
   411  				ID:       nicID.String(),
   412  				Endpoint: endpoint,
   413  			}
   414  		}
   415  	}
   416  	return nil
   417  }
   418  
   419  // RemoveNetNS removes the namespace from the uvm and all remaining endpoints in
   420  // the namespace.
   421  //
   422  // If a namespace matching `id` is not found this command silently succeeds.
   423  func (uvm *UtilityVM) RemoveNetNS(ctx context.Context, id string) error {
   424  	uvm.m.Lock()
   425  	defer uvm.m.Unlock()
   426  	if ns, ok := uvm.namespaces[id]; ok {
   427  		for _, ninfo := range ns.nics {
   428  			if err := uvm.removeNIC(ctx, ninfo.ID, ninfo.Endpoint); err != nil {
   429  				return err
   430  			}
   431  			ns.nics[ninfo.Endpoint.Id] = nil
   432  		}
   433  		// Remove the Guest Network namespace
   434  		if uvm.isNetworkNamespaceSupported() {
   435  			if uvm.operatingSystem == "windows" {
   436  				hcnNamespace, err := hcn.GetNamespaceByID(id)
   437  				if err != nil {
   438  					return err
   439  				}
   440  				guestNamespace := hcsschema.ModifySettingRequest{
   441  					GuestRequest: guestrequest.ModificationRequest{
   442  						ResourceType: guestresource.ResourceTypeNetworkNamespace,
   443  						RequestType:  guestrequest.RequestTypeRemove,
   444  						Settings:     hcnNamespace,
   445  					},
   446  				}
   447  				if err := uvm.modify(ctx, &guestNamespace); err != nil {
   448  					return err
   449  				}
   450  			}
   451  		}
   452  		delete(uvm.namespaces, id)
   453  	}
   454  	return nil
   455  }
   456  
   457  // RemoveEndpointsFromNS removes all matching `endpoints` in the network
   458  // namespace matching `id`. If no endpoint matching `endpoint.Id` is found in
   459  // the network namespace this command silently succeeds.
   460  //
   461  // If no network namespace matches `id` returns `ErrNetNSNotFound`.
   462  func (uvm *UtilityVM) RemoveEndpointsFromNS(ctx context.Context, id string, endpoints []*hns.HNSEndpoint) error {
   463  	uvm.m.Lock()
   464  	defer uvm.m.Unlock()
   465  
   466  	ns, ok := uvm.namespaces[id]
   467  	if !ok {
   468  		return ErrNetNSNotFound
   469  	}
   470  
   471  	for _, endpoint := range endpoints {
   472  		if ninfo, ok := ns.nics[endpoint.Id]; ok && ninfo != nil {
   473  			if err := uvm.removeNIC(ctx, ninfo.ID, ninfo.Endpoint); err != nil {
   474  				return err
   475  			}
   476  			delete(ns.nics, endpoint.Id)
   477  		}
   478  	}
   479  	return nil
   480  }
   481  
   482  // RemoveEndpointFromNS removes `endpoint` in the network
   483  // namespace matching `id`. If no endpoint matching `endpoint.Id` is found in
   484  // the network namespace this command returns `ErrNICNotFound`.
   485  //
   486  // If no network namespace matches `id` this function returns `ErrNetNSNotFound`.
   487  func (uvm *UtilityVM) RemoveEndpointFromNS(ctx context.Context, id string, endpoint *hns.HNSEndpoint) error {
   488  	uvm.m.Lock()
   489  	defer uvm.m.Unlock()
   490  
   491  	ns, ok := uvm.namespaces[id]
   492  	if !ok {
   493  		return ErrNetNSNotFound
   494  	}
   495  
   496  	if ninfo, ok := ns.nics[endpoint.Id]; ok && ninfo != nil {
   497  		if err := uvm.removeNIC(ctx, ninfo.ID, ninfo.Endpoint); err != nil {
   498  			return err
   499  		}
   500  		delete(ns.nics, endpoint.Id)
   501  	} else {
   502  		return ErrNICNotFound
   503  	}
   504  	return nil
   505  }
   506  
   507  // IsNetworkNamespaceSupported returns bool value specifying if network namespace is supported inside the guest
   508  func (uvm *UtilityVM) isNetworkNamespaceSupported() bool {
   509  	return uvm.guestCaps.NamespaceAddRequestSupported
   510  }
   511  
   512  func getNetworkModifyRequest(adapterID string, requestType guestrequest.RequestType, settings interface{}) interface{} {
   513  	if osversion.Build() >= osversion.RS5 {
   514  		return guestrequest.NetworkModifyRequest{
   515  			AdapterId:   adapterID,
   516  			RequestType: requestType,
   517  			Settings:    settings,
   518  		}
   519  	}
   520  	return guestrequest.RS4NetworkModifyRequest{
   521  		AdapterInstanceId: adapterID,
   522  		RequestType:       requestType,
   523  		Settings:          settings,
   524  	}
   525  }
   526  
   527  // addNIC adds a nic to the Utility VM.
   528  func (uvm *UtilityVM) addNIC(ctx context.Context, id string, endpoint *hns.HNSEndpoint) error {
   529  	// First a pre-add. This is a guest-only request and is only done on Windows.
   530  	if uvm.operatingSystem == "windows" {
   531  		preAddRequest := hcsschema.ModifySettingRequest{
   532  			GuestRequest: guestrequest.ModificationRequest{
   533  				ResourceType: guestresource.ResourceTypeNetwork,
   534  				RequestType:  guestrequest.RequestTypeAdd,
   535  				Settings: getNetworkModifyRequest(
   536  					id,
   537  					guestrequest.RequestTypePreAdd,
   538  					endpoint),
   539  			},
   540  		}
   541  		if err := uvm.modify(ctx, &preAddRequest); err != nil {
   542  			return err
   543  		}
   544  	}
   545  
   546  	// Then the Add itself
   547  	request := hcsschema.ModifySettingRequest{
   548  		RequestType:  guestrequest.RequestTypeAdd,
   549  		ResourcePath: fmt.Sprintf(resourcepaths.NetworkResourceFormat, id),
   550  		Settings: hcsschema.NetworkAdapter{
   551  			EndpointId: endpoint.Id,
   552  			MacAddress: endpoint.MacAddress,
   553  		},
   554  	}
   555  
   556  	if uvm.operatingSystem == "windows" {
   557  		request.GuestRequest = guestrequest.ModificationRequest{
   558  			ResourceType: guestresource.ResourceTypeNetwork,
   559  			RequestType:  guestrequest.RequestTypeAdd,
   560  			Settings: getNetworkModifyRequest(
   561  				id,
   562  				guestrequest.RequestTypeAdd,
   563  				nil),
   564  		}
   565  	} else {
   566  		// Verify this version of LCOW supports Network HotAdd
   567  		s := &guestresource.LCOWNetworkAdapter{
   568  			NamespaceID:     endpoint.Namespace.ID,
   569  			ID:              id,
   570  			MacAddress:      endpoint.MacAddress,
   571  			IPAddress:       endpoint.IPAddress.String(),
   572  			PrefixLength:    endpoint.PrefixLength,
   573  			GatewayAddress:  endpoint.GatewayAddress,
   574  			DNSSuffix:       endpoint.DNSSuffix,
   575  			DNSServerList:   endpoint.DNSServerList,
   576  			EnableLowMetric: endpoint.EnableLowMetric,
   577  			EncapOverhead:   endpoint.EncapOverhead,
   578  		}
   579  		if v6 := endpoint.IPv6Address.To16(); v6 != nil && !v6.IsUnspecified() {
   580  			// if the address is empty or invalid, endpoint.IPv6Address.String will be "<nil>"
   581  			// since we should be guranteed an IPv4, but not a v6, skip invalid v6 address
   582  			s.IPv6Address = v6.String()
   583  			s.IPv6PrefixLength = endpoint.IPv6PrefixLength
   584  			s.IPv6GatewayAddress = endpoint.GatewayAddressV6
   585  			log.G(ctx).WithFields(logrus.Fields{
   586  				"ip":           s.IPAddress,
   587  				"prefixLength": s.IPv6PrefixLength,
   588  				"gateway":      s.IPv6GatewayAddress,
   589  			}).Debug("adding IPv6 settings")
   590  		}
   591  		if uvm.isNetworkNamespaceSupported() {
   592  			request.GuestRequest = guestrequest.ModificationRequest{
   593  				ResourceType: guestresource.ResourceTypeNetwork,
   594  				RequestType:  guestrequest.RequestTypeAdd,
   595  				Settings:     s,
   596  			}
   597  		}
   598  	}
   599  
   600  	if err := uvm.modify(ctx, &request); err != nil {
   601  		return err
   602  	}
   603  
   604  	return nil
   605  }
   606  
   607  func (uvm *UtilityVM) removeNIC(ctx context.Context, id string, endpoint *hns.HNSEndpoint) error {
   608  	request := hcsschema.ModifySettingRequest{
   609  		RequestType:  guestrequest.RequestTypeRemove,
   610  		ResourcePath: fmt.Sprintf(resourcepaths.NetworkResourceFormat, id),
   611  		Settings: hcsschema.NetworkAdapter{
   612  			EndpointId: endpoint.Id,
   613  			MacAddress: endpoint.MacAddress,
   614  		},
   615  	}
   616  
   617  	if uvm.operatingSystem == "windows" {
   618  		request.GuestRequest = hcsschema.ModifySettingRequest{
   619  			RequestType: guestrequest.RequestTypeRemove,
   620  			Settings: getNetworkModifyRequest(
   621  				id,
   622  				guestrequest.RequestTypeRemove,
   623  				nil),
   624  		}
   625  	} else {
   626  		// Verify this version of LCOW supports Network HotRemove
   627  		if uvm.isNetworkNamespaceSupported() {
   628  			request.GuestRequest = guestrequest.ModificationRequest{
   629  				ResourceType: guestresource.ResourceTypeNetwork,
   630  				RequestType:  guestrequest.RequestTypeRemove,
   631  				Settings: &guestresource.LCOWNetworkAdapter{
   632  					NamespaceID: endpoint.Namespace.ID,
   633  					ID:          endpoint.Id,
   634  				},
   635  			}
   636  		}
   637  	}
   638  
   639  	if err := uvm.modify(ctx, &request); err != nil {
   640  		return err
   641  	}
   642  	return nil
   643  }
   644  
   645  // Removes all NICs added to this uvm.
   646  func (uvm *UtilityVM) RemoveAllNICs(ctx context.Context) error {
   647  	for _, ns := range uvm.namespaces {
   648  		for _, ninfo := range ns.nics {
   649  			if err := uvm.removeNIC(ctx, ninfo.ID, ninfo.Endpoint); err != nil {
   650  				return err
   651  			}
   652  		}
   653  	}
   654  	return nil
   655  }
   656  
   657  // UpdateNIC updates a UVM's network adapter.
   658  func (uvm *UtilityVM) UpdateNIC(ctx context.Context, id string, settings *hcsschema.NetworkAdapter) error {
   659  	req := &hcsschema.ModifySettingRequest{
   660  		RequestType:  guestrequest.RequestTypeUpdate,
   661  		ResourcePath: fmt.Sprintf(resourcepaths.NetworkResourceFormat, id),
   662  		Settings:     settings,
   663  	}
   664  	return uvm.modify(ctx, req)
   665  }
   666  
   667  // AddNICInGuest makes a request to setup a network adapter's interface inside the lcow guest.
   668  // This is primarily used for adding NICs in the guest that have been VPCI assigned.
   669  func (uvm *UtilityVM) AddNICInGuest(ctx context.Context, cfg *guestresource.LCOWNetworkAdapter) error {
   670  	if !uvm.isNetworkNamespaceSupported() {
   671  		return fmt.Errorf("guest does not support network namespaces and cannot add VF NIC %+v", cfg)
   672  	}
   673  	request := hcsschema.ModifySettingRequest{}
   674  	request.GuestRequest = guestrequest.ModificationRequest{
   675  		ResourceType: guestresource.ResourceTypeNetwork,
   676  		RequestType:  guestrequest.RequestTypeAdd,
   677  		Settings:     cfg,
   678  	}
   679  
   680  	return uvm.modify(ctx, &request)
   681  }
   682  
   683  // RemoveNICInGuest makes a request to remove a network interface inside the lcow guest.
   684  // This is primarily used for removing NICs in the guest that were VPCI assigned.
   685  func (uvm *UtilityVM) RemoveNICInGuest(ctx context.Context, cfg *guestresource.LCOWNetworkAdapter) error {
   686  	if !uvm.isNetworkNamespaceSupported() {
   687  		return fmt.Errorf("guest does not support network namespaces and cannot remove VF NIC %+v", cfg)
   688  	}
   689  	request := hcsschema.ModifySettingRequest{}
   690  	request.GuestRequest = guestrequest.ModificationRequest{
   691  		ResourceType: guestresource.ResourceTypeNetwork,
   692  		RequestType:  guestrequest.RequestTypeRemove,
   693  		Settings:     cfg,
   694  	}
   695  
   696  	return uvm.modify(ctx, &request)
   697  }
   698  

View as plain text