github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/hypervisor/metadatad/daemons.go (about)

     1  // +build go1.10
     2  
     3  package metadatad
     4  
     5  import (
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"os/exec"
    10  	"runtime"
    11  	"strconv"
    12  	"time"
    13  
    14  	"github.com/Cloud-Foundations/Dominator/lib/log"
    15  	"github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger"
    16  	"github.com/Cloud-Foundations/Dominator/lib/wsyscall"
    17  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    18  )
    19  
    20  type statusType struct {
    21  	namespaceFd int
    22  	threadId    int
    23  	err         error
    24  }
    25  
    26  func httpServe(listener net.Listener, handler http.Handler,
    27  	idleTimeout time.Duration) error {
    28  	httpServer := &http.Server{Handler: handler, IdleTimeout: idleTimeout}
    29  	return httpServer.Serve(listener)
    30  }
    31  
    32  func (s *server) startServer() error {
    33  	cmd := exec.Command("ebtables", "-t", "nat", "-F")
    34  	if output, err := cmd.CombinedOutput(); err != nil {
    35  		return fmt.Errorf("error running ebtables: %s: %s", err, string(output))
    36  	}
    37  	runtime.LockOSThread()
    38  	defer runtime.UnlockOSThread()
    39  	for _, bridge := range s.bridges {
    40  		if err := s.startServerOnBridge(bridge); err != nil {
    41  			return err
    42  		}
    43  	}
    44  	return nil
    45  }
    46  
    47  func (s *server) startServerOnBridge(bridge net.Interface) error {
    48  	logger := prefixlogger.New(bridge.Name+": ", s.logger)
    49  	startChannel := make(chan struct{})
    50  	statusChannel := make(chan statusType, 1)
    51  	go s.createNamespace(startChannel, statusChannel, logger)
    52  	status := <-statusChannel
    53  	if status.err != nil {
    54  		return status.err
    55  	}
    56  	if err := createInterface(bridge, status.threadId, logger); err != nil {
    57  		return err
    58  	}
    59  	startChannel <- struct{}{}
    60  	status = <-statusChannel
    61  	if status.err != nil {
    62  		return status.err
    63  	}
    64  	subnetChannel := s.manager.MakeSubnetChannel()
    65  	go s.addSubnets(bridge, status.namespaceFd, subnetChannel, logger)
    66  	return nil
    67  }
    68  
    69  func (s *server) addSubnets(bridge net.Interface, namespaceFd int,
    70  	subnetChannel <-chan proto.Subnet, logger log.DebugLogger) {
    71  	logger.Debugf(0, "waiting for subnet updates in namespaceFD=%d\n",
    72  		namespaceFd)
    73  	if err := wsyscall.SetNetNamespace(namespaceFd); err != nil {
    74  		logger.Println(err)
    75  		return
    76  	}
    77  	for subnet := range subnetChannel {
    78  		addRouteForBridge(bridge, subnet, logger)
    79  	}
    80  }
    81  
    82  func addRouteForBridge(bridge net.Interface, subnet proto.Subnet,
    83  	logger log.DebugLogger) {
    84  	if subnet.DisableMetadata {
    85  		logger.Debugf(0, "metadata service disabled for subnet: %s\n",
    86  			subnet.Id)
    87  		return
    88  	}
    89  	subnetMask := net.IPMask(subnet.IpMask)
    90  	subnetAddr := subnet.IpGateway.Mask(subnetMask)
    91  	addr := subnetAddr.String()
    92  	mask := fmt.Sprintf("%d.%d.%d.%d",
    93  		subnetMask[0], subnetMask[1], subnetMask[2], subnetMask[3])
    94  	cmd := exec.Command("route", "add", "-net", addr, "netmask", mask, "eth0")
    95  	if output, err := cmd.CombinedOutput(); err != nil {
    96  		logger.Printf("error adding route: for subnet: %s: %s/%s: %s: %s",
    97  			subnet.Id, addr, mask, err, string(output))
    98  	} else {
    99  		logger.Debugf(0, "added route for subnet: %s: %s/%s\n",
   100  			subnet.Id, addr, mask)
   101  	}
   102  }
   103  
   104  func (s *server) createNamespace(startChannel <-chan struct{},
   105  	statusChannel chan<- statusType, logger log.DebugLogger) {
   106  	namespaceFd, threadId, err := wsyscall.UnshareNetNamespace()
   107  	if err != nil {
   108  		statusChannel <- statusType{err: err}
   109  		return
   110  	}
   111  	statusChannel <- statusType{namespaceFd: namespaceFd, threadId: threadId}
   112  	<-startChannel
   113  	cmd := exec.Command("ifconfig", "eth0", "169.254.169.254", "netmask",
   114  		"255.255.255.255", "up")
   115  	if err := cmd.Run(); err != nil {
   116  		statusChannel <- statusType{err: err}
   117  		return
   118  	}
   119  	hypervisorListener, err := net.Listen("tcp",
   120  		fmt.Sprintf("169.254.169.254:%d", s.hypervisorPortNum))
   121  	if err != nil {
   122  		statusChannel <- statusType{err: err}
   123  		return
   124  	}
   125  	metadataListener, err := net.Listen("tcp", "169.254.169.254:80")
   126  	if err != nil {
   127  		statusChannel <- statusType{err: err}
   128  		return
   129  	}
   130  	statusChannel <- statusType{namespaceFd: namespaceFd, threadId: threadId}
   131  	logger.Printf("starting metadata server in thread: %d\n", threadId)
   132  	go httpServe(hypervisorListener, nil, time.Second*5)
   133  	httpServe(metadataListener, s, time.Second*5)
   134  }
   135  
   136  func createInterface(bridge net.Interface, threadId int,
   137  	logger log.DebugLogger) error {
   138  	localName := bridge.Name + "-ll"
   139  	remoteName := bridge.Name + "-lr"
   140  	if _, err := net.InterfaceByName(localName); err == nil {
   141  		exec.Command("ip", "link", "delete", localName).Run()
   142  	}
   143  	cmd := exec.Command("ip", "link", "add", localName, "type", "veth",
   144  		"peer", "name", remoteName)
   145  	if output, err := cmd.CombinedOutput(); err != nil {
   146  		return fmt.Errorf("error creating veth for bridge: %s: %s: %s",
   147  			bridge.Name, err, output)
   148  	}
   149  	cmd = exec.Command("ifconfig", localName, "up")
   150  	if output, err := cmd.CombinedOutput(); err != nil {
   151  		return fmt.Errorf("error bringing up local interface: %s: %s: %s",
   152  			localName, err, output)
   153  	}
   154  	remoteInterface, err := net.InterfaceByName(remoteName)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	cmd = exec.Command("ip", "link", "set", remoteName, "netns",
   159  		strconv.FormatInt(int64(threadId), 10), "name", "eth0")
   160  	if output, err := cmd.CombinedOutput(); err != nil {
   161  		return fmt.Errorf("error moving interface to namespace: %s: %s: %s",
   162  			remoteName, err, output)
   163  	}
   164  	cmd = exec.Command("ip", "link", "set", localName, "master", bridge.Name)
   165  	if output, err := cmd.CombinedOutput(); err != nil {
   166  		return fmt.Errorf("error adding interface: %s to bridge: %s: %s: %s",
   167  			localName, bridge.Name, err, output)
   168  	}
   169  	hwAddr := remoteInterface.HardwareAddr.String()
   170  	cmd = exec.Command("ebtables", "-t", "nat", "-A", "PREROUTING",
   171  		"--logical-in", bridge.Name, "-p", "ip",
   172  		"--ip-dst", "169.254.0.0/16", "-j", "dnat", "--to-destination",
   173  		hwAddr)
   174  	if output, err := cmd.CombinedOutput(); err != nil {
   175  		return fmt.Errorf(
   176  			"error adding ebtables dnat to: %s to bridge: %s: %s: %s",
   177  			hwAddr, bridge.Name, err, output)
   178  	}
   179  	logger.Printf("created veth, remote addr: %s\n", hwAddr)
   180  	return nil
   181  }