...

Source file src/github.com/Microsoft/hcsshim/internal/guest/runtime/hcsv2/network.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/runtime/hcsv2

     1  //go:build linux
     2  // +build linux
     3  
     4  package hcsv2
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/vishvananda/netns"
    15  	"go.opencensus.io/trace"
    16  
    17  	"github.com/Microsoft/hcsshim/internal/guest/gcserr"
    18  	"github.com/Microsoft/hcsshim/internal/guest/network"
    19  	"github.com/Microsoft/hcsshim/internal/guest/prot"
    20  	"github.com/Microsoft/hcsshim/internal/oc"
    21  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    22  )
    23  
    24  var (
    25  	// namespaceSync protects access to `namespaces`.
    26  	namespaceSync sync.Mutex
    27  	// namespaces is the set of `in-memory` namespace adapters know to the GCS.
    28  	// These may or may not be assigned to a container as there is support for
    29  	// pre-Add and post-Add.
    30  	namespaces map[string]*namespace
    31  
    32  	networkInstanceIDToName = network.InstanceIDToName
    33  )
    34  
    35  func init() {
    36  	namespaces = make(map[string]*namespace)
    37  }
    38  
    39  // getNetworkNamespace returns the namespace found by `id`. If the namespace
    40  // does not exist returns `gcserr.HrErrNotFound`.
    41  func getNetworkNamespace(id string) (*namespace, error) {
    42  	id = strings.ToLower(id)
    43  
    44  	namespaceSync.Lock()
    45  	defer namespaceSync.Unlock()
    46  
    47  	ns, ok := namespaces[id]
    48  	if !ok {
    49  		return nil, gcserr.WrapHresult(errors.Errorf("namespace '%s' not found", id), gcserr.HrErrNotFound)
    50  	}
    51  	return ns, nil
    52  }
    53  
    54  // GetOrAddNetworkNamespace returns the namespace found by `id` or creates a new
    55  // one and assigns `id.
    56  func GetOrAddNetworkNamespace(id string) *namespace {
    57  	id = strings.ToLower(id)
    58  
    59  	namespaceSync.Lock()
    60  	defer namespaceSync.Unlock()
    61  
    62  	ns, ok := namespaces[id]
    63  	if !ok {
    64  		ns = &namespace{
    65  			id: id,
    66  		}
    67  		namespaces[id] = ns
    68  	}
    69  	return ns
    70  }
    71  
    72  // RemoveNetworkNamespace removes the in-memory `namespace` found by `id`.
    73  func RemoveNetworkNamespace(ctx context.Context, id string) (err error) {
    74  	_, span := oc.StartSpan(ctx, "hcsv2::RemoveNetworkNamespace")
    75  	defer span.End()
    76  	defer func() { oc.SetSpanStatus(span, err) }()
    77  
    78  	id = strings.ToLower(id)
    79  	span.AddAttributes(trace.StringAttribute("id", id))
    80  
    81  	namespaceSync.Lock()
    82  	defer namespaceSync.Unlock()
    83  
    84  	ns, ok := namespaces[id]
    85  	if ok {
    86  		ns.m.Lock()
    87  		defer ns.m.Unlock()
    88  		if len(ns.nics) > 0 {
    89  			return errors.Errorf("network namespace '%s' contains adapters", id)
    90  		}
    91  		delete(namespaces, id)
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // namespace struct maps all vNIC's to the namespace ID used by the HNS.
    98  type namespace struct {
    99  	id string
   100  
   101  	m    sync.Mutex
   102  	pid  int
   103  	nics []*nicInNamespace
   104  }
   105  
   106  // ID is the id of the network namespace
   107  func (n *namespace) ID() string {
   108  	return n.id
   109  }
   110  
   111  // AssignContainerPid assigns `pid` to `n` but does NOT move any previously
   112  // assigned adapters into this namespace. The caller MUST call `Sync()` to
   113  // complete this operation.
   114  func (n *namespace) AssignContainerPid(ctx context.Context, pid int) (err error) {
   115  	_, span := oc.StartSpan(ctx, "namespace::AssignContainerPid")
   116  	defer span.End()
   117  	defer func() { oc.SetSpanStatus(span, err) }()
   118  	span.AddAttributes(
   119  		trace.StringAttribute("namespace", n.id),
   120  		trace.Int64Attribute("pid", int64(pid)))
   121  
   122  	n.m.Lock()
   123  	defer n.m.Unlock()
   124  
   125  	if n.pid != 0 {
   126  		return errors.Errorf("previously assigned container pid %d to network namespace %q", n.pid, n.id)
   127  	}
   128  
   129  	n.pid = pid
   130  	return nil
   131  }
   132  
   133  // Adapters returns a copy of the adapters assigned to `n` at the time of the
   134  // call.
   135  func (n *namespace) Adapters() []*guestresource.LCOWNetworkAdapter {
   136  	n.m.Lock()
   137  	defer n.m.Unlock()
   138  
   139  	adps := make([]*guestresource.LCOWNetworkAdapter, len(n.nics))
   140  	for i, nin := range n.nics {
   141  		adps[i] = nin.adapter
   142  	}
   143  	return adps
   144  }
   145  
   146  // AddAdapter adds `adp` to `n` but does NOT move the adapter into the network
   147  // namespace assigned to `n`. A user must call `Sync()` to complete this
   148  // operation.
   149  func (n *namespace) AddAdapter(ctx context.Context, adp *guestresource.LCOWNetworkAdapter) (err error) {
   150  	ctx, span := oc.StartSpan(ctx, "namespace::AddAdapter")
   151  	defer span.End()
   152  	defer func() { oc.SetSpanStatus(span, err) }()
   153  	span.AddAttributes(
   154  		trace.StringAttribute("namespace", n.id),
   155  		trace.StringAttribute("adapter", fmt.Sprintf("%+v", adp)))
   156  
   157  	n.m.Lock()
   158  	defer n.m.Unlock()
   159  
   160  	for _, nic := range n.nics {
   161  		if strings.EqualFold(nic.adapter.ID, adp.ID) {
   162  			return errors.Errorf("adapter with id: '%s' already present in namespace", adp.ID)
   163  		}
   164  	}
   165  
   166  	resolveCtx, cancel := context.WithTimeout(ctx, time.Second*5)
   167  	defer cancel()
   168  	ifname, err := networkInstanceIDToName(resolveCtx, adp.ID, adp.VPCIAssigned)
   169  	if err != nil {
   170  		return err
   171  	}
   172  	n.nics = append(n.nics, &nicInNamespace{
   173  		adapter: adp,
   174  		ifname:  ifname,
   175  	})
   176  	return nil
   177  }
   178  
   179  // RemoveAdapter removes the adapter matching `id` from `n`. If `id` is not
   180  // found returns no error.
   181  func (n *namespace) RemoveAdapter(ctx context.Context, id string) (err error) {
   182  	_, span := oc.StartSpan(ctx, "namespace::RemoveAdapter")
   183  	defer span.End()
   184  	defer func() { oc.SetSpanStatus(span, err) }()
   185  	span.AddAttributes(
   186  		trace.StringAttribute("namespace", n.id),
   187  		trace.StringAttribute("adapterID", id))
   188  
   189  	n.m.Lock()
   190  	defer n.m.Unlock()
   191  
   192  	// TODO: do we need to remove anything guestside from a sandbox namespace?
   193  
   194  	i := -1
   195  	for j, nic := range n.nics {
   196  		if strings.EqualFold(nic.adapter.ID, id) {
   197  			i = j
   198  			break
   199  		}
   200  	}
   201  	if i > -1 {
   202  		n.nics = append(n.nics[:i], n.nics[i+1:]...)
   203  	}
   204  	return nil
   205  }
   206  
   207  // Sync moves all adapters to the network namespace of `n` if assigned.
   208  func (n *namespace) Sync(ctx context.Context) (err error) {
   209  	ctx, span := oc.StartSpan(ctx, "namespace::Sync")
   210  	defer span.End()
   211  	defer func() { oc.SetSpanStatus(span, err) }()
   212  	span.AddAttributes(trace.StringAttribute("namespace", n.id))
   213  
   214  	n.m.Lock()
   215  	defer n.m.Unlock()
   216  
   217  	if n.pid != 0 {
   218  		for i, a := range n.nics {
   219  			// Lower the metric for anything but the first adapter
   220  			// TODO: remove when we correctly support assigning metrics to the default GWs
   221  			if i > 0 {
   222  				a.adapter.EnableLowMetric = true
   223  			}
   224  			err = a.assignToPid(ctx, n.pid)
   225  			if err != nil {
   226  				return err
   227  			}
   228  		}
   229  	}
   230  	return nil
   231  }
   232  
   233  // nicInNamespace represents a single network adapter that has been added to the
   234  // guest and its mapping to the linux `ifname`.
   235  type nicInNamespace struct {
   236  	// adapter captures the network settings when the nic was added
   237  	adapter *guestresource.LCOWNetworkAdapter
   238  	// ifname is the interface name resolved for this adapter
   239  	ifname string
   240  	// assignedPid will be `0` for any nic in this namespace that has not been
   241  	// moved into a specific pid network namespace.
   242  	assignedPid int
   243  }
   244  
   245  // assignToPid assigns `nin.adapter`, represented by `nin.ifname` to `pid`.
   246  func (nin *nicInNamespace) assignToPid(ctx context.Context, pid int) (err error) {
   247  	ctx, span := oc.StartSpan(ctx, "nicInNamespace::assignToPid")
   248  	defer span.End()
   249  	defer func() { oc.SetSpanStatus(span, err) }()
   250  	span.AddAttributes(
   251  		trace.StringAttribute("adapterID", nin.adapter.ID),
   252  		trace.StringAttribute("ifname", nin.ifname),
   253  		trace.Int64Attribute("pid", int64(pid)))
   254  
   255  	v1Adapter := &prot.NetworkAdapter{
   256  		NatEnabled:           (nin.adapter.IPAddress != "") || (nin.adapter.IPv6Address != ""),
   257  		AllocatedIPAddress:   nin.adapter.IPAddress,
   258  		HostIPAddress:        nin.adapter.GatewayAddress,
   259  		HostIPPrefixLength:   nin.adapter.PrefixLength,
   260  		AllocatedIPv6Address: nin.adapter.IPv6Address,
   261  		HostIPv6Address:      nin.adapter.IPv6GatewayAddress,
   262  		HostIPv6PrefixLength: nin.adapter.IPv6PrefixLength,
   263  		EnableLowMetric:      nin.adapter.EnableLowMetric,
   264  		EncapOverhead:        nin.adapter.EncapOverhead,
   265  	}
   266  
   267  	if err := network.MoveInterfaceToNS(nin.ifname, pid); err != nil {
   268  		return errors.Wrapf(err, "failed to move interface %s to network namespace", nin.ifname)
   269  	}
   270  
   271  	// Get a reference to the new network namespace
   272  	ns, err := netns.GetFromPid(pid)
   273  	if err != nil {
   274  		return errors.Wrapf(err, "netns.GetFromPid(%d) failed", pid)
   275  	}
   276  	defer ns.Close()
   277  
   278  	netNSCfg := func() error {
   279  		return network.NetNSConfig(ctx, nin.ifname, pid, v1Adapter)
   280  	}
   281  
   282  	if err := network.DoInNetNS(ns, netNSCfg); err != nil {
   283  		return errors.Wrapf(err, "failed to configure adapter aid: %s, if id: %s", nin.adapter.ID, nin.ifname)
   284  	}
   285  	nin.assignedPid = pid
   286  	return nil
   287  }
   288  

View as plain text