github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/metadatad/daemons.go (about)

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