github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/networking/kvm.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  // kvm.go file provides networking supporting functions for kvm flavor
    16  package networking
    17  
    18  import (
    19  	"crypto/sha512"
    20  	"encoding/json"
    21  	"fmt"
    22  	"log"
    23  	"net"
    24  
    25  	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/ip"
    26  	cnitypes "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/types"
    27  	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
    28  	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink"
    29  
    30  	"github.com/coreos/rkt/common"
    31  	"github.com/coreos/rkt/networking/tuntap"
    32  )
    33  
    34  // setupTapDevice creates persistent tap devices
    35  // and returns a newly created netlink.Link structure
    36  func setupTapDevice(podID types.UUID) (netlink.Link, error) {
    37  	// network device names are limited to 16 characters
    38  	// the suffix %d will be replaced by the kernel with a suitable number
    39  	nameTemplate := fmt.Sprintf("rkt-%s-tap%%d", podID.String()[0:4])
    40  	ifName, err := tuntap.CreatePersistentIface(nameTemplate, tuntap.Tap)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("tuntap persist %v", err)
    43  	}
    44  	link, err := netlink.LinkByName(ifName)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("cannot find link %q: %v", ifName, err)
    47  	}
    48  	err = netlink.LinkSetUp(link)
    49  	if err != nil {
    50  		return nil, fmt.Errorf("cannot set link up %q: %v", ifName, err)
    51  	}
    52  	return link, nil
    53  }
    54  
    55  // kvmSetupNetAddressing calls IPAM plugin (with a hack) to reserve an IP to be
    56  // used by newly create tuntap pair
    57  // in result it updates activeNet.runtime configuration with IP, Mask and HostIP
    58  func kvmSetupNetAddressing(network *Networking, n activeNet, ifName string) error {
    59  	// TODO: very ugly hack, that go through upper plugin, down to ipam plugin
    60  	if err := ip.EnableIP4Forward(); err != nil {
    61  		return fmt.Errorf("failed to enable forwarding: %v", err)
    62  	}
    63  	n.conf.Type = n.conf.IPAM.Type
    64  	output, err := network.execNetPlugin("ADD", &n, ifName)
    65  	if err != nil {
    66  		return fmt.Errorf("problem executing network plugin %q (%q): %v", n.conf.Type, ifName, err)
    67  	}
    68  
    69  	result := cnitypes.Result{}
    70  	if err = json.Unmarshal(output, &result); err != nil {
    71  		return fmt.Errorf("error parsing %q result: %v", n.conf.Name, err)
    72  	}
    73  
    74  	if result.IP4 == nil {
    75  		return fmt.Errorf("net-plugin returned no IPv4 configuration")
    76  	}
    77  
    78  	n.runtime.IP, n.runtime.Mask, n.runtime.HostIP = result.IP4.IP.IP, net.IP(result.IP4.IP.Mask), result.IP4.Gateway
    79  	return nil
    80  }
    81  
    82  // kvmSetup prepare new Networking to be used in kvm environment based on tuntap pair interfaces
    83  // to allow communication with virtual machine created by lkvm tool
    84  // right now it only supports default "ptp" network type (other types ends with error)
    85  func kvmSetup(podRoot string, podID types.UUID, fps []ForwardedPort, netList common.NetList, localConfig string) (*Networking, error) {
    86  	network := Networking{
    87  		podEnv: podEnv{
    88  			podRoot:      podRoot,
    89  			podID:        podID,
    90  			netsLoadList: netList,
    91  			localConfig:  localConfig,
    92  		},
    93  	}
    94  	var e error
    95  	network.nets, e = network.loadNets()
    96  	if e != nil {
    97  		return nil, fmt.Errorf("error loading network definitions: %v", e)
    98  	}
    99  
   100  	for _, n := range network.nets {
   101  		switch n.conf.Type {
   102  		case "ptp":
   103  			link, err := setupTapDevice(podID)
   104  			if err != nil {
   105  				return nil, err
   106  			}
   107  			ifName := link.Attrs().Name
   108  			n.runtime.IfName = ifName
   109  
   110  			err = kvmSetupNetAddressing(&network, n, ifName)
   111  			if err != nil {
   112  				return nil, err
   113  			}
   114  
   115  			// add address to host tap device
   116  			err = netlink.AddrAdd(
   117  				link,
   118  				&netlink.Addr{
   119  					IPNet: &net.IPNet{
   120  						IP:   n.runtime.HostIP,
   121  						Mask: net.IPMask(n.runtime.Mask),
   122  					},
   123  					Label: ifName,
   124  				})
   125  			if err != nil {
   126  				return nil, fmt.Errorf("cannot add address to host tap device %q: %v", ifName, err)
   127  			}
   128  
   129  			if n.conf.IPMasq {
   130  				h := sha512.Sum512([]byte(podID.String()))
   131  				chain := fmt.Sprintf("CNI-%s-%x", n.conf.Name, h[:8])
   132  				if err = ip.SetupIPMasq(&net.IPNet{
   133  					IP:   n.runtime.IP,
   134  					Mask: net.IPMask(n.runtime.Mask),
   135  				}, chain); err != nil {
   136  					return nil, err
   137  				}
   138  			}
   139  		default:
   140  			return nil, fmt.Errorf("network %q have unsupported type: %q", n.conf.Name, n.conf.Type)
   141  		}
   142  	}
   143  	err := network.forwardPorts(fps, network.GetDefaultIP())
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	return &network, nil
   149  }
   150  
   151  /*
   152  extend Networking struct with methods to clean up kvm specific network configurations
   153  */
   154  
   155  // teardownKvmNets teardown every active networking from networking by
   156  // removing tuntap interface and releasing its ip from IPAM plugin
   157  func (n *Networking) teardownKvmNets() {
   158  	for _, an := range n.nets {
   159  		switch an.conf.Type {
   160  		case "ptp":
   161  			// remove tuntap interface
   162  			tuntap.RemovePersistentIface(an.runtime.IfName, tuntap.Tap)
   163  			// remove masquerading if it was prepared
   164  			if an.conf.IPMasq {
   165  				h := sha512.Sum512([]byte(n.podID.String()))
   166  				chain := fmt.Sprintf("CNI-%s-%x", an.conf.Name, h[:8])
   167  				err := ip.TeardownIPMasq(&net.IPNet{
   168  					IP:   an.runtime.IP,
   169  					Mask: net.IPMask(an.runtime.Mask),
   170  				}, chain)
   171  				if err != nil {
   172  					log.Printf("Error on removing masquerading: %q", err)
   173  				}
   174  			}
   175  			// ugly hack again to directly call IPAM plugin to release IP
   176  			an.conf.Type = an.conf.IPAM.Type
   177  
   178  			_, err := n.execNetPlugin("DEL", &an, an.runtime.IfName)
   179  			if err != nil {
   180  				log.Printf("Error executing network plugin: %q", err)
   181  			}
   182  		default:
   183  			log.Printf("Unsupported network type: %q", an.conf.Type)
   184  		}
   185  	}
   186  }
   187  
   188  // kvmTeardown network teardown for kvm flavor based pods
   189  // similar to Networking.Teardown but without host namespaces
   190  func (n *Networking) kvmTeardown() {
   191  
   192  	if err := n.unforwardPorts(); err != nil {
   193  		log.Printf("Error removing forwarded ports (kvm): %v", err)
   194  	}
   195  	n.teardownKvmNets()
   196  
   197  }
   198  
   199  // Following methods implements behavior of netDescriber by activeNet
   200  // (behavior required by stage1/init/kvm package and its kernel parameters configuration)
   201  
   202  func (an activeNet) HostIP() net.IP {
   203  	return an.runtime.HostIP
   204  }
   205  func (an activeNet) GuestIP() net.IP {
   206  	return an.runtime.IP
   207  }
   208  func (an activeNet) IfName() string {
   209  	return an.runtime.IfName
   210  }
   211  func (an activeNet) Mask() net.IP {
   212  	return an.runtime.Mask
   213  }
   214  func (an activeNet) Name() string {
   215  	return an.conf.Name
   216  }
   217  func (an activeNet) IPMasq() bool {
   218  	return an.conf.IPMasq
   219  }
   220  
   221  // GetActiveNetworks returns activeNets to be used as NetDescriptors
   222  // by plugins, which are required for stage1 executor to run (only for KVM)
   223  func (e *Networking) GetActiveNetworks() []activeNet {
   224  	return e.nets
   225  }