github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/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/containernetworking/cni/pkg/types"
    26  	"github.com/coreos/go-systemd/unit"
    27  	"github.com/hashicorp/errwrap"
    28  	"github.com/rkt/rkt/networking"
    29  )
    30  
    31  // GetNetworkDescriptions converts activeNets to netDescribers
    32  func GetNetworkDescriptions(n *networking.Networking) []NetDescriber {
    33  	var nds []NetDescriber
    34  	for _, an := range n.GetActiveNetworks() {
    35  		nds = append(nds, an)
    36  	}
    37  	return nds
    38  }
    39  
    40  // NetDescriber is the interface that describes a network configuration
    41  type NetDescriber interface {
    42  	GuestIP() net.IP
    43  	Mask() net.IP
    44  	IfName() string
    45  	IPMasq() bool
    46  	Name() string
    47  	Gateway() net.IP
    48  	Routes() []types.Route
    49  }
    50  
    51  // GetKVMNetArgs returns additional arguments that need to be passed
    52  // to lkvm tool to configure networks properly.
    53  // Logic is based on Network configuration extracted from Networking struct
    54  // and essentially from activeNets that expose netDescriber behavior
    55  func GetKVMNetArgs(nds []NetDescriber) ([]string, error) {
    56  
    57  	var lkvmArgs []string
    58  
    59  	for _, nd := range nds {
    60  		lkvmArgs = append(lkvmArgs, "--network")
    61  		lkvmArg := fmt.Sprintf("mode=tap,tapif=%s,host_ip=%s,guest_ip=%s", nd.IfName(), nd.Gateway(), nd.GuestIP())
    62  		lkvmArgs = append(lkvmArgs, lkvmArg)
    63  	}
    64  
    65  	return lkvmArgs, nil
    66  }
    67  
    68  // generateMacAddress returns net.HardwareAddr filled with fixed 3 byte prefix
    69  // complemented by 3 random bytes.
    70  func generateMacAddress() (net.HardwareAddr, error) {
    71  	mac := []byte{
    72  		2,          // locally administered unicast
    73  		0x65, 0x02, // OUI (randomly chosen by jell)
    74  		0, 0, 0, // bytes to randomly overwrite
    75  	}
    76  
    77  	_, err := rand.Read(mac[3:6])
    78  	if err != nil {
    79  		return nil, errwrap.Wrap(errors.New("cannot generate random mac address"), err)
    80  	}
    81  
    82  	return mac, nil
    83  }
    84  
    85  func setMacCommand(ifName, mac string) string {
    86  	return fmt.Sprintf("/bin/ip link set dev %s address %s", ifName, mac)
    87  }
    88  
    89  func addAddressCommand(address, ifName string) string {
    90  	return fmt.Sprintf("/bin/ip address add %s dev %s", address, ifName)
    91  }
    92  
    93  func addRouteCommand(destination, router string) string {
    94  	return fmt.Sprintf("/bin/ip route add %s via %s", destination, router)
    95  }
    96  
    97  func downInterfaceCommand(ifName string) string {
    98  	return fmt.Sprintf("/bin/ip link set dev %s down", ifName)
    99  }
   100  
   101  func upInterfaceCommand(ifName string) string {
   102  	return fmt.Sprintf("/bin/ip link set dev %s up", ifName)
   103  }
   104  
   105  func GenerateNetworkInterfaceUnits(unitsPath string, netDescriptions []NetDescriber) error {
   106  	for i, netDescription := range netDescriptions {
   107  		ifName := fmt.Sprintf(networking.IfNamePattern, i)
   108  		netAddress := net.IPNet{
   109  			IP:   netDescription.GuestIP(),
   110  			Mask: net.IPMask(netDescription.Mask()),
   111  		}
   112  
   113  		address := netAddress.String()
   114  
   115  		mac, err := generateMacAddress()
   116  		if err != nil {
   117  			return err
   118  		}
   119  
   120  		opts := []*unit.UnitOption{
   121  			unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Network configuration for device: %v", ifName)),
   122  			unit.NewUnitOption("Unit", "DefaultDependencies", "false"),
   123  			unit.NewUnitOption("Service", "Type", "oneshot"),
   124  			unit.NewUnitOption("Service", "RemainAfterExit", "true"),
   125  			unit.NewUnitOption("Service", "ExecStartPre", downInterfaceCommand(ifName)),
   126  			unit.NewUnitOption("Service", "ExecStartPre", setMacCommand(ifName, mac.String())),
   127  			unit.NewUnitOption("Service", "ExecStartPre", upInterfaceCommand(ifName)),
   128  			unit.NewUnitOption("Service", "ExecStart", addAddressCommand(address, ifName)),
   129  			unit.NewUnitOption("Install", "RequiredBy", "default.target"),
   130  		}
   131  
   132  		for _, route := range netDescription.Routes() {
   133  			gw := route.GW
   134  			if gw == nil {
   135  				gw = netDescription.Gateway()
   136  			}
   137  
   138  			opts = append(
   139  				opts,
   140  				unit.NewUnitOption(
   141  					"Service",
   142  					"ExecStartPost",
   143  					addRouteCommand(route.Dst.String(), gw.String()),
   144  				),
   145  			)
   146  		}
   147  
   148  		unitName := fmt.Sprintf("interface-%s", ifName) + ".service"
   149  		unitBytes, err := ioutil.ReadAll(unit.Serialize(opts))
   150  		if err != nil {
   151  			return errwrap.Wrap(fmt.Errorf("failed to serialize network unit file to bytes %q", unitName), err)
   152  		}
   153  
   154  		err = ioutil.WriteFile(filepath.Join(unitsPath, unitName), unitBytes, 0644)
   155  		if err != nil {
   156  			return errwrap.Wrap(fmt.Errorf("failed to create network unit file %q", unitName), err)
   157  		}
   158  
   159  		diag.Printf("network unit created: %q in %q (iface=%q, addr=%q)", unitName, unitsPath, ifName, address)
   160  	}
   161  	return nil
   162  }