...

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

Documentation: github.com/Microsoft/hcsshim/internal/guest/network

     1  //go:build linux
     2  // +build linux
     3  
     4  package network
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/Microsoft/hcsshim/internal/guest/storage"
    16  	"github.com/Microsoft/hcsshim/internal/guest/storage/pci"
    17  	"github.com/Microsoft/hcsshim/internal/guest/storage/vmbus"
    18  	"github.com/Microsoft/hcsshim/internal/log"
    19  	"github.com/Microsoft/hcsshim/internal/oc"
    20  	"github.com/pkg/errors"
    21  	"go.opencensus.io/trace"
    22  )
    23  
    24  // mock out calls for testing
    25  var (
    26  	pciFindDeviceFullPath             = pci.FindDeviceFullPath
    27  	storageWaitForFileMatchingPattern = storage.WaitForFileMatchingPattern
    28  	vmbusWaitForDevicePath            = vmbus.WaitForDevicePath
    29  	ioReadDir                         = os.ReadDir
    30  )
    31  
    32  // maxDNSSearches is limited to 6 in `man 5 resolv.conf`
    33  const maxDNSSearches = 6
    34  
    35  // GenerateEtcHostsContent generates a /etc/hosts file based on `hostname`.
    36  func GenerateEtcHostsContent(ctx context.Context, hostname string) string {
    37  	_, span := oc.StartSpan(ctx, "network::GenerateEtcHostsContent")
    38  	defer span.End()
    39  	span.AddAttributes(trace.StringAttribute("hostname", hostname))
    40  
    41  	nameParts := strings.Split(hostname, ".")
    42  	buf := bytes.Buffer{}
    43  	buf.WriteString("127.0.0.1 localhost\n")
    44  	if len(nameParts) > 1 {
    45  		buf.WriteString(fmt.Sprintf("127.0.0.1 %s %s\n", hostname, nameParts[0]))
    46  	} else {
    47  		buf.WriteString(fmt.Sprintf("127.0.0.1 %s\n", hostname))
    48  	}
    49  	buf.WriteString("\n")
    50  	buf.WriteString("# The following lines are desirable for IPv6 capable hosts\n")
    51  	buf.WriteString("::1     ip6-localhost ip6-loopback\n")
    52  	buf.WriteString("fe00::0 ip6-localnet\n")
    53  	buf.WriteString("ff00::0 ip6-mcastprefix\n")
    54  	buf.WriteString("ff02::1 ip6-allnodes\n")
    55  	buf.WriteString("ff02::2 ip6-allrouters\n")
    56  	return buf.String()
    57  }
    58  
    59  // GenerateResolvConfContent generates the resolv.conf file content based on
    60  // `searches`, `servers`, and `options`.
    61  func GenerateResolvConfContent(ctx context.Context, searches, servers, options []string) (_ string, err error) {
    62  	_, span := oc.StartSpan(ctx, "network::GenerateResolvConfContent")
    63  	defer span.End()
    64  	defer func() { oc.SetSpanStatus(span, err) }()
    65  
    66  	span.AddAttributes(
    67  		trace.StringAttribute("searches", strings.Join(searches, ", ")),
    68  		trace.StringAttribute("servers", strings.Join(servers, ", ")),
    69  		trace.StringAttribute("options", strings.Join(options, ", ")))
    70  
    71  	if len(searches) > maxDNSSearches {
    72  		return "", errors.Errorf("searches has more than %d domains", maxDNSSearches)
    73  	}
    74  
    75  	content := ""
    76  	if len(searches) > 0 {
    77  		content += fmt.Sprintf("search %s\n", strings.Join(searches, " "))
    78  	}
    79  	if len(servers) > 0 {
    80  		content += fmt.Sprintf("nameserver %s\n", strings.Join(servers, "\nnameserver "))
    81  	}
    82  	if len(options) > 0 {
    83  		content += fmt.Sprintf("options %s\n", strings.Join(options, " "))
    84  	}
    85  	return content, nil
    86  }
    87  
    88  // MergeValues merges `first` and `second` maintaining order `first, second`.
    89  func MergeValues(first, second []string) []string {
    90  	if len(first) == 0 {
    91  		return second
    92  	}
    93  	if len(second) == 0 {
    94  		return first
    95  	}
    96  	values := make([]string, len(first), len(first)+len(second))
    97  	copy(values, first)
    98  	for _, v := range second {
    99  		found := false
   100  		for i := 0; i < len(values); i++ {
   101  			if v == values[i] {
   102  				found = true
   103  				break
   104  			}
   105  		}
   106  		if !found {
   107  			values = append(values, v)
   108  		}
   109  	}
   110  	return values
   111  }
   112  
   113  // InstanceIDToName converts from the given instance ID (a GUID generated on the
   114  // Windows host) to its corresponding interface name (e.g. "eth0").
   115  //
   116  // Will retry the operation until `ctx` is exceeded or canceled.
   117  func InstanceIDToName(ctx context.Context, id string, vpciAssigned bool) (_ string, err error) {
   118  	ctx, span := oc.StartSpan(ctx, "network::InstanceIDToName")
   119  	defer span.End()
   120  	defer func() { oc.SetSpanStatus(span, err) }()
   121  
   122  	vmBusID := strings.ToLower(id)
   123  	span.AddAttributes(trace.StringAttribute("adapterInstanceID", vmBusID))
   124  
   125  	var netDevicePath string
   126  	if vpciAssigned {
   127  		var pciDevicePath string
   128  		pciDevicePath, err = pciFindDeviceFullPath(ctx, vmBusID)
   129  		if err != nil {
   130  			return "", err
   131  		}
   132  		pciNetDirPattern := filepath.Join(pciDevicePath, "net")
   133  		netDevicePath, err = storageWaitForFileMatchingPattern(ctx, pciNetDirPattern)
   134  	} else {
   135  		vmBusNetSubPath := filepath.Join(vmBusID, "net")
   136  		netDevicePath, err = vmbusWaitForDevicePath(ctx, vmBusNetSubPath)
   137  	}
   138  	if err != nil {
   139  		return "", errors.Wrapf(err, "failed to find adapter %v sysfs path", vmBusID)
   140  	}
   141  
   142  	var deviceDirs []os.DirEntry
   143  	for {
   144  		deviceDirs, err = ioReadDir(netDevicePath)
   145  		if err != nil {
   146  			if os.IsNotExist(err) {
   147  				select {
   148  				case <-ctx.Done():
   149  					return "", errors.Wrap(ctx.Err(), "timed out waiting for net adapter")
   150  				default:
   151  					time.Sleep(10 * time.Millisecond)
   152  					continue
   153  				}
   154  			} else {
   155  				return "", errors.Wrapf(err, "failed to read vmbus network device from /sys filesystem for adapter %s", vmBusID)
   156  			}
   157  		}
   158  		break
   159  	}
   160  	if len(deviceDirs) == 0 {
   161  		return "", errors.Errorf("no interface name found for adapter %s", vmBusID)
   162  	}
   163  	if len(deviceDirs) > 1 {
   164  		return "", errors.Errorf("multiple interface names found for adapter %s", vmBusID)
   165  	}
   166  	ifname := deviceDirs[0].Name()
   167  	log.G(ctx).WithField("ifname", ifname).Debug("resolved ifname")
   168  	return ifname, nil
   169  }
   170  

View as plain text