github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/stage1/init/kvm/network.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kvm
    16  
    17  import (
    18  	"crypto/rand"
    19  	"errors"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"net"
    23  	"path/filepath"
    24  
    25  	"github.com/appc/cni/pkg/types"
    26  	"github.com/coreos/go-systemd/unit"
    27  	"github.com/coreos/rkt/networking"
    28  	"github.com/hashicorp/errwrap"
    29  )
    30  
    31  // GetNetworkDescriptions explicitly convert slice of activeNets to slice of netDescribers
    32  // which is slice required by GetKVMNetArgs
    33  func GetNetworkDescriptions(n *networking.Networking) []netDescriber {
    34  	var nds []netDescriber
    35  	for _, an := range n.GetActiveNetworks() {
    36  		nds = append(nds, an)
    37  	}
    38  	return nds
    39  }
    40  
    41  // netDescriber is something that describes network configuration
    42  type netDescriber interface {
    43  	GuestIP() net.IP
    44  	Mask() net.IP
    45  	IfName() string
    46  	IPMasq() bool
    47  	Name() string
    48  	Gateway() net.IP
    49  	Routes() []types.Route
    50  }
    51  
    52  // GetKVMNetArgs returns additional arguments that need to be passed
    53  // to lkvm tool to configure networks properly.
    54  // Logic is based on Network configuration extracted from Networking struct
    55  // and essentially from activeNets that expose netDescriber behavior
    56  func GetKVMNetArgs(nds []netDescriber) ([]string, error) {
    57  
    58  	var lkvmArgs []string
    59  
    60  	for _, nd := range nds {
    61  		lkvmArgs = append(lkvmArgs, "--network")
    62  		lkvmArg := fmt.Sprintf("mode=tap,tapif=%s,host_ip=%s,guest_ip=%s", nd.IfName(), nd.Gateway(), nd.GuestIP())
    63  		lkvmArgs = append(lkvmArgs, lkvmArg)
    64  	}
    65  
    66  	return lkvmArgs, nil
    67  }
    68  
    69  // generateMacAddress returns net.HardwareAddr filled with fixed 3 byte prefix
    70  // complemented by 3 random bytes.
    71  func generateMacAddress() (net.HardwareAddr, error) {
    72  	mac := []byte{
    73  		2,          // locally administered unicast
    74  		0x65, 0x02, // OUI (randomly choosen by jell)
    75  		0, 0, 0, // bytes to randomly overwrite
    76  	}
    77  
    78  	_, err := rand.Read(mac[3:6])
    79  	if err != nil {
    80  		return nil, errwrap.Wrap(errors.New("cannot generate random mac address"), err)
    81  	}
    82  
    83  	return mac, nil
    84  }
    85  
    86  func setMacCommand(ifName, mac string) string {
    87  	return fmt.Sprintf("/bin/ip link set dev %s address %s", ifName, mac)
    88  }
    89  
    90  func addAddressCommand(address, ifName string) string {
    91  	return fmt.Sprintf("/bin/ip address add %s dev %s", address, ifName)
    92  }
    93  
    94  func addRouteCommand(destination, router string) string {
    95  	return fmt.Sprintf("/bin/ip route add %s via %s", destination, router)
    96  }
    97  
    98  func downInterfaceCommand(ifName string) string {
    99  	return fmt.Sprintf("/bin/ip link set dev %s down", ifName)
   100  }
   101  
   102  func upInterfaceCommand(ifName string) string {
   103  	return fmt.Sprintf("/bin/ip link set dev %s up", ifName)
   104  }
   105  
   106  func GenerateNetworkInterfaceUnits(unitsPath string, netDescriptions []netDescriber) error {
   107  
   108  	for i, netDescription := range netDescriptions {
   109  		ifName := fmt.Sprintf(networking.IfNamePattern, i)
   110  		netAddress := net.IPNet{
   111  			IP:   netDescription.GuestIP(),
   112  			Mask: net.IPMask(netDescription.Mask()),
   113  		}
   114  
   115  		address := netAddress.String()
   116  
   117  		mac, err := generateMacAddress()
   118  		if err != nil {
   119  			return err
   120  		}
   121  
   122  		opts := []*unit.UnitOption{
   123  			unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Network configuration for device: %v", ifName)),
   124  			unit.NewUnitOption("Unit", "DefaultDependencies", "false"),
   125  			unit.NewUnitOption("Service", "Type", "oneshot"),
   126  			unit.NewUnitOption("Service", "RemainAfterExit", "true"),
   127  			unit.NewUnitOption("Service", "ExecStartPre", downInterfaceCommand(ifName)),
   128  			unit.NewUnitOption("Service", "ExecStartPre", setMacCommand(ifName, mac.String())),
   129  			unit.NewUnitOption("Service", "ExecStartPre", upInterfaceCommand(ifName)),
   130  			unit.NewUnitOption("Service", "ExecStart", addAddressCommand(address, ifName)),
   131  			unit.NewUnitOption("Install", "RequiredBy", "default.target"),
   132  		}
   133  
   134  		for _, route := range netDescription.Routes() {
   135  			gw := route.GW
   136  			if gw == nil {
   137  				gw = netDescription.Gateway()
   138  			}
   139  
   140  			opts = append(
   141  				opts,
   142  				unit.NewUnitOption(
   143  					"Service",
   144  					"ExecStartPost",
   145  					addRouteCommand(route.Dst.String(), gw.String()),
   146  				),
   147  			)
   148  		}
   149  
   150  		unitName := fmt.Sprintf("interface-%s", ifName) + ".service"
   151  		unitBytes, err := ioutil.ReadAll(unit.Serialize(opts))
   152  		if err != nil {
   153  			return errwrap.Wrap(fmt.Errorf("failed to serialize network unit file to bytes %q", unitName), err)
   154  		}
   155  
   156  		err = ioutil.WriteFile(filepath.Join(unitsPath, unitName), unitBytes, 0644)
   157  		if err != nil {
   158  			return errwrap.Wrap(fmt.Errorf("failed to create network unit file %q", unitName), err)
   159  		}
   160  
   161  		rlog.Printf("network unit created: %q in %q (iface=%q, addr=%q)", unitName, unitsPath, ifName, address)
   162  	}
   163  	return nil
   164  }