github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/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  	"bufio"
    20  	"crypto/sha512"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net"
    26  	"os"
    27  	"path/filepath"
    28  	"strconv"
    29  	"strings"
    30  	"syscall"
    31  
    32  	"github.com/appc/spec/schema/types"
    33  	"github.com/containernetworking/cni/pkg/ip"
    34  	cnitypes "github.com/containernetworking/cni/pkg/types"
    35  	cniutils "github.com/containernetworking/cni/pkg/utils"
    36  	cnisysctl "github.com/containernetworking/cni/pkg/utils/sysctl"
    37  	"github.com/hashicorp/errwrap"
    38  	"github.com/vishvananda/netlink"
    39  
    40  	"github.com/rkt/rkt/common"
    41  	commonnet "github.com/rkt/rkt/common/networking"
    42  	"github.com/rkt/rkt/networking/tuntap"
    43  )
    44  
    45  const (
    46  	defaultBrName     = "cni0"
    47  	defaultSubnetFile = "/run/flannel/subnet.env"
    48  	defaultMTU        = 1500
    49  )
    50  
    51  type BridgeNetConf struct {
    52  	NetConf
    53  	BrName string `json:"bridge"`
    54  	IsGw   bool   `json:"isGateway"`
    55  }
    56  
    57  // setupTapDevice creates persistent tap device
    58  // and returns a newly created netlink.Link structure
    59  func setupTapDevice(podID types.UUID) (netlink.Link, error) {
    60  	// network device names are limited to 16 characters
    61  	// the suffix %d will be replaced by the kernel with a suitable number
    62  	nameTemplate := fmt.Sprintf("rkt-%s-tap%%d", podID.String()[0:4])
    63  	ifName, err := tuntap.CreatePersistentIface(nameTemplate, tuntap.Tap)
    64  	if err != nil {
    65  		return nil, errwrap.Wrap(errors.New("tuntap persist"), err)
    66  	}
    67  
    68  	link, err := netlink.LinkByName(ifName)
    69  	if err != nil {
    70  		return nil, errwrap.Wrap(fmt.Errorf("cannot find link %q", ifName), err)
    71  	}
    72  
    73  	if err := netlink.LinkSetUp(link); err != nil {
    74  		return nil, errwrap.Wrap(fmt.Errorf("cannot set link up %q", ifName), err)
    75  	}
    76  	return link, nil
    77  }
    78  
    79  type MacVTapNetConf struct {
    80  	NetConf
    81  	Master string `json:"master"`
    82  	Mode   string `json:"mode"`
    83  }
    84  
    85  const (
    86  	IPv4InterfaceArpProxySysctlTemplate = "net.ipv4.conf.%s.proxy_arp"
    87  )
    88  
    89  // setupTapDevice creates persistent macvtap device
    90  // and returns a newly created netlink.Link structure
    91  // using part of pod hash and interface number in interface name
    92  func setupMacVTapDevice(podID types.UUID, config MacVTapNetConf, interfaceNumber int) (netlink.Link, error) {
    93  	master, err := netlink.LinkByName(config.Master)
    94  	if err != nil {
    95  		return nil, errwrap.Wrap(fmt.Errorf("cannot find master device '%v'", config.Master), err)
    96  	}
    97  	var mode netlink.MacvlanMode
    98  	switch config.Mode {
    99  	// if not set - defaults to bridge mode as in:
   100  	// https://github.com/rkt/rkt/blob/master/Documentation/networking.md#macvlan
   101  	case "", "bridge":
   102  		mode = netlink.MACVLAN_MODE_BRIDGE
   103  	case "private":
   104  		mode = netlink.MACVLAN_MODE_PRIVATE
   105  	case "vepa":
   106  		mode = netlink.MACVLAN_MODE_VEPA
   107  	case "passthru":
   108  		mode = netlink.MACVLAN_MODE_PASSTHRU
   109  	default:
   110  		return nil, fmt.Errorf("unsupported macvtap mode: %v", config.Mode)
   111  	}
   112  	mtu := master.Attrs().MTU
   113  	if config.MTU != 0 {
   114  		mtu = config.MTU
   115  	}
   116  	interfaceName := fmt.Sprintf("rkt-%s-vtap%d", podID.String()[0:4], interfaceNumber)
   117  	link := &netlink.Macvtap{
   118  		Macvlan: netlink.Macvlan{
   119  			LinkAttrs: netlink.LinkAttrs{
   120  				Name:        interfaceName,
   121  				MTU:         mtu,
   122  				ParentIndex: master.Attrs().Index,
   123  			},
   124  			Mode: mode,
   125  		},
   126  	}
   127  
   128  	if err := netlink.LinkAdd(link); err != nil {
   129  		return nil, errwrap.Wrap(errors.New("cannot create macvtap interface"), err)
   130  	}
   131  
   132  	// TODO: duplicate following lines for ipv6 support, when it will be added in other places
   133  	ipv4SysctlValueName := fmt.Sprintf(IPv4InterfaceArpProxySysctlTemplate, interfaceName)
   134  	if _, err := cnisysctl.Sysctl(ipv4SysctlValueName, "1"); err != nil {
   135  		// remove the newly added link and ignore errors, because we already are in a failed state
   136  		_ = netlink.LinkDel(link)
   137  		return nil, errwrap.Wrap(fmt.Errorf("failed to set proxy_arp on newly added interface %q", interfaceName), err)
   138  	}
   139  
   140  	if err := netlink.LinkSetUp(link); err != nil {
   141  		// remove the newly added link and ignore errors, because we already are in a failed state
   142  		_ = netlink.LinkDel(link)
   143  		return nil, errwrap.Wrap(errors.New("cannot set up macvtap interface"), err)
   144  	}
   145  	return link, nil
   146  }
   147  
   148  // kvmSetupNetAddressing calls IPAM plugin (with a hack) to reserve an IP to be
   149  // used by newly create tuntap pair
   150  // in result it updates activeNet.runtime configuration
   151  func kvmSetupNetAddressing(network *Networking, n activeNet, ifName string) error {
   152  	// TODO: very ugly hack, that go through upper plugin, down to ipam plugin
   153  	if err := ip.EnableIP4Forward(); err != nil {
   154  		return errwrap.Wrap(errors.New("failed to enable forwarding"), err)
   155  	}
   156  
   157  	// patch plugin type only for single IPAM run time, then revert this change
   158  	original_type := n.conf.Type
   159  	n.conf.Type = n.conf.IPAM.Type
   160  	output, err := network.execNetPlugin("ADD", &n, ifName)
   161  	n.conf.Type = original_type
   162  	if err != nil {
   163  		return errwrap.Wrap(fmt.Errorf("problem executing network plugin %q (%q)", n.conf.IPAM.Type, ifName), err)
   164  	}
   165  
   166  	result := cnitypes.Result{}
   167  	if err = json.Unmarshal(output, &result); err != nil {
   168  		return errwrap.Wrap(fmt.Errorf("error parsing %q result", n.conf.Name), err)
   169  	}
   170  
   171  	if result.IP4 == nil {
   172  		return fmt.Errorf("net-plugin returned no IPv4 configuration")
   173  	}
   174  
   175  	n.runtime.MergeCNIResult(result)
   176  
   177  	return nil
   178  }
   179  
   180  func ensureHasAddr(link netlink.Link, ipn *net.IPNet) error {
   181  	addrs, err := netlink.AddrList(link, syscall.AF_INET)
   182  	if err != nil && err != syscall.ENOENT {
   183  		return errwrap.Wrap(errors.New("could not get list of IP addresses"), err)
   184  	}
   185  
   186  	// if there're no addresses on the interface, it's ok -- we'll add one
   187  	if len(addrs) > 0 {
   188  		ipnStr := ipn.String()
   189  		for _, a := range addrs {
   190  			// string comp is actually easiest for doing IPNet comps
   191  			if a.IPNet.String() == ipnStr {
   192  				return nil
   193  			}
   194  		}
   195  		return fmt.Errorf("%q already has an IP address different from %v", link.Attrs().Name, ipn.String())
   196  	}
   197  
   198  	addr := &netlink.Addr{IPNet: ipn, Label: link.Attrs().Name}
   199  	if err := netlink.AddrAdd(link, addr); err != nil {
   200  		return errwrap.Wrap(fmt.Errorf("could not add IP address to %q", link.Attrs().Name), err)
   201  	}
   202  	return nil
   203  }
   204  
   205  func bridgeByName(name string) (*netlink.Bridge, error) {
   206  	l, err := netlink.LinkByName(name)
   207  	if err != nil {
   208  		return nil, errwrap.Wrap(fmt.Errorf("could not lookup %q", name), err)
   209  	}
   210  	br, ok := l.(*netlink.Bridge)
   211  	if !ok {
   212  		return nil, fmt.Errorf("%q already exists but is not a bridge", name)
   213  	}
   214  	return br, nil
   215  }
   216  
   217  func ensureBridgeIsUp(brName string, mtu int) (*netlink.Bridge, error) {
   218  	br := &netlink.Bridge{
   219  		LinkAttrs: netlink.LinkAttrs{
   220  			Name: brName,
   221  			MTU:  mtu,
   222  		},
   223  	}
   224  
   225  	if err := netlink.LinkAdd(br); err != nil {
   226  		if err != syscall.EEXIST {
   227  			return nil, errwrap.Wrap(fmt.Errorf("could not add %q", brName), err)
   228  		}
   229  
   230  		// it's ok if the device already exists as long as config is similar
   231  		br, err = bridgeByName(brName)
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  	}
   236  
   237  	if err := netlink.LinkSetUp(br); err != nil {
   238  		return nil, err
   239  	}
   240  
   241  	return br, nil
   242  }
   243  
   244  func addRoute(link netlink.Link, podIP net.IP) error {
   245  	route := netlink.Route{
   246  		LinkIndex: link.Attrs().Index,
   247  		Scope:     netlink.SCOPE_LINK,
   248  		Dst: &net.IPNet{
   249  			IP:   podIP,
   250  			Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0xff),
   251  		},
   252  	}
   253  	return netlink.RouteAdd(&route)
   254  }
   255  
   256  func removeAllRoutesOnLink(link netlink.Link) error {
   257  	routes, err := netlink.RouteList(link, netlink.FAMILY_V4)
   258  	if err != nil {
   259  		return errwrap.Wrap(fmt.Errorf("cannot list routes on link %q", link.Attrs().Name), err)
   260  	}
   261  
   262  	for _, route := range routes {
   263  		if err := netlink.RouteDel(&route); err != nil {
   264  			return errwrap.Wrap(fmt.Errorf("error in time of route removal for route %q", route), err)
   265  		}
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  func getChainName(podUUIDString, confName string) string {
   272  	h := sha512.Sum512([]byte(podUUIDString))
   273  	return fmt.Sprintf("CNI-%s-%x", confName, h[:8])
   274  }
   275  
   276  type FlannelNetConf struct {
   277  	NetConf
   278  
   279  	SubnetFile string                 `json:"subnetFile"`
   280  	Delegate   map[string]interface{} `json:"delegate"`
   281  }
   282  
   283  func loadFlannelNetConf(bytes []byte) (*FlannelNetConf, error) {
   284  	n := &FlannelNetConf{
   285  		SubnetFile: defaultSubnetFile,
   286  	}
   287  	if err := json.Unmarshal(bytes, n); err != nil {
   288  		return nil, errwrap.Wrap(errors.New("failed to load netconf"), err)
   289  	}
   290  	return n, nil
   291  }
   292  
   293  type subnetEnv struct {
   294  	nw     *net.IPNet
   295  	sn     *net.IPNet
   296  	mtu    int
   297  	ipmasq bool
   298  }
   299  
   300  func loadFlannelSubnetEnv(fn string) (*subnetEnv, error) {
   301  	f, err := os.Open(fn)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	defer f.Close()
   306  
   307  	se := &subnetEnv{}
   308  
   309  	s := bufio.NewScanner(f)
   310  	for s.Scan() {
   311  		parts := strings.SplitN(s.Text(), "=", 2)
   312  		switch parts[0] {
   313  		case "FLANNEL_NETWORK":
   314  			_, se.nw, err = net.ParseCIDR(parts[1])
   315  			if err != nil {
   316  				return nil, err
   317  			}
   318  
   319  		case "FLANNEL_SUBNET":
   320  			_, se.sn, err = net.ParseCIDR(parts[1])
   321  			if err != nil {
   322  				return nil, err
   323  			}
   324  
   325  		case "FLANNEL_MTU":
   326  			mtu, err := strconv.ParseUint(parts[1], 10, 32)
   327  			if err != nil {
   328  				return nil, err
   329  			}
   330  			se.mtu = int(mtu)
   331  
   332  		case "FLANNEL_IPMASQ":
   333  			se.ipmasq = parts[1] == "true"
   334  		}
   335  	}
   336  	if err := s.Err(); err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	return se, nil
   341  }
   342  
   343  func hasKey(m map[string]interface{}, k string) bool {
   344  	_, ok := m[k]
   345  	return ok
   346  }
   347  
   348  func isString(i interface{}) bool {
   349  	_, ok := i.(string)
   350  	return ok
   351  }
   352  
   353  func kvmTransformFlannelNetwork(net *activeNet) error {
   354  	n, err := loadFlannelNetConf(net.confBytes)
   355  	if err != nil {
   356  		return err
   357  	}
   358  
   359  	fenv, err := loadFlannelSubnetEnv(n.SubnetFile)
   360  	if err != nil {
   361  		return err
   362  	}
   363  
   364  	if n.Delegate == nil {
   365  		n.Delegate = make(map[string]interface{})
   366  	} else {
   367  		if hasKey(n.Delegate, "type") && !isString(n.Delegate["type"]) {
   368  			return fmt.Errorf("'delegate' dictionary, if present, must have (string) 'type' field")
   369  		}
   370  		if hasKey(n.Delegate, "name") {
   371  			return fmt.Errorf("'delegate' dictionary must not have 'name' field, it'll be set by flannel")
   372  		}
   373  		if hasKey(n.Delegate, "ipam") {
   374  			return fmt.Errorf("'delegate' dictionary must not have 'ipam' field, it'll be set by flannel")
   375  		}
   376  	}
   377  
   378  	n.Delegate["name"] = n.Name
   379  
   380  	if !hasKey(n.Delegate, "type") {
   381  		n.Delegate["type"] = "bridge"
   382  	}
   383  
   384  	if !hasKey(n.Delegate, "isDefaultGateway") {
   385  		n.Delegate["isDefaultGateway"] = false
   386  	}
   387  
   388  	if !hasKey(n.Delegate, "ipMasq") {
   389  		// if flannel is not doing ipmasq, we should
   390  		ipmasq := !fenv.ipmasq
   391  		n.Delegate["ipMasq"] = ipmasq
   392  	}
   393  
   394  	if !hasKey(n.Delegate, "mtu") {
   395  		mtu := fenv.mtu
   396  		n.Delegate["mtu"] = mtu
   397  	}
   398  
   399  	if n.Delegate["type"].(string) == "bridge" {
   400  		if !hasKey(n.Delegate, "isGateway") {
   401  			n.Delegate["isGateway"] = true
   402  		}
   403  	}
   404  
   405  	n.Delegate["ipam"] = map[string]interface{}{
   406  		"type":   "host-local",
   407  		"subnet": fenv.sn.String(),
   408  		"routes": []cnitypes.Route{
   409  			{
   410  				Dst: *fenv.nw,
   411  			},
   412  		},
   413  	}
   414  
   415  	bytes, err := json.Marshal(n.Delegate)
   416  	if err != nil {
   417  		return errwrap.Wrap(errors.New("error in marshaling generated network settings"), err)
   418  	}
   419  
   420  	net.runtime.IP4 = &cnitypes.IPConfig{}
   421  	*net = activeNet{
   422  		confBytes: bytes,
   423  		conf:      &NetConf{},
   424  		runtime:   net.runtime,
   425  	}
   426  	net.conf.Name = n.Name
   427  	net.conf.Type = n.Delegate["type"].(string)
   428  	net.conf.IPMasq = n.Delegate["ipMasq"].(bool)
   429  	net.conf.MTU = n.Delegate["mtu"].(int)
   430  	net.conf.IsDefaultGateway = n.Delegate["isDefaultGateway"].(bool)
   431  	net.conf.IPAM.Type = "host-local"
   432  	return nil
   433  }
   434  
   435  // kvmSetup prepare new Networking to be used in kvm environment based on tuntap pair interfaces
   436  // to allow communication with virtual machine created by lkvm tool
   437  func kvmSetup(podRoot string, podID types.UUID, fps []commonnet.ForwardedPort, netList common.NetList, localConfig string, noDNS bool) (*Networking, error) {
   438  	network := Networking{
   439  		podEnv: podEnv{
   440  			podRoot:      podRoot,
   441  			podID:        podID,
   442  			netsLoadList: netList,
   443  			localConfig:  localConfig,
   444  		},
   445  	}
   446  	var e error
   447  
   448  	// If there's a network set as default in CNI configuration
   449  	defaultGatewaySet := false
   450  
   451  	_, defaultNet, err := net.ParseCIDR("0.0.0.0/0")
   452  	if err != nil {
   453  		return nil, errwrap.Wrap(errors.New("error when parsing net address"), err)
   454  	}
   455  
   456  	network.nets, e = network.loadNets()
   457  	if e != nil {
   458  		return nil, errwrap.Wrap(errors.New("error loading network definitions"), e)
   459  	}
   460  
   461  	// did stage0 already make /etc/rkt-resolv.conf (i.e. --dns passed)
   462  	resolvPath := filepath.Join(common.Stage1RootfsPath(podRoot), "etc/rkt-resolv.conf")
   463  	_, err = os.Stat(resolvPath)
   464  	if err != nil && !os.IsNotExist(err) {
   465  		return nil, errwrap.Wrap(fmt.Errorf("error statting /etc/rkt-resolv.conf"), err)
   466  	}
   467  	podHasResolvConf := err == nil
   468  
   469  	for i, n := range network.nets {
   470  		if n.conf.Type == "flannel" {
   471  			if err := kvmTransformFlannelNetwork(&n); err != nil {
   472  				return nil, errwrap.Wrap(errors.New("cannot transform flannel network into basic network"), err)
   473  			}
   474  		}
   475  		switch n.conf.Type {
   476  		case "ptp":
   477  			link, err := setupTapDevice(podID)
   478  			if err != nil {
   479  				return nil, err
   480  			}
   481  			ifName := link.Attrs().Name
   482  			n.runtime.IfName = ifName
   483  
   484  			err = kvmSetupNetAddressing(&network, n, ifName)
   485  			if err != nil {
   486  				return nil, err
   487  			}
   488  
   489  			// add address to host tap device
   490  			err = ensureHasAddr(
   491  				link,
   492  				&net.IPNet{
   493  					IP:   n.runtime.IP4.Gateway,
   494  					Mask: net.IPMask(n.runtime.Mask),
   495  				},
   496  			)
   497  			if err != nil {
   498  				return nil, errwrap.Wrap(fmt.Errorf("cannot add address to host tap device %q", ifName), err)
   499  			}
   500  
   501  			if err := removeAllRoutesOnLink(link); err != nil {
   502  				return nil, errwrap.Wrap(fmt.Errorf("cannot remove route on host tap device %q", ifName), err)
   503  			}
   504  
   505  			if err := addRoute(link, n.runtime.IP); err != nil {
   506  				return nil, errwrap.Wrap(errors.New("cannot add on host direct route to pod"), err)
   507  			}
   508  
   509  		case "bridge":
   510  			config := BridgeNetConf{
   511  				NetConf: NetConf{
   512  					MTU: defaultMTU,
   513  				},
   514  				BrName: defaultBrName,
   515  			}
   516  			if err := json.Unmarshal(n.confBytes, &config); err != nil {
   517  				return nil, errwrap.Wrap(fmt.Errorf("error parsing %q result", n.conf.Name), err)
   518  			}
   519  
   520  			br, err := ensureBridgeIsUp(config.BrName, config.MTU)
   521  			if err != nil {
   522  				return nil, errwrap.Wrap(errors.New("error in time of bridge setup"), err)
   523  			}
   524  			link, err := setupTapDevice(podID)
   525  			if err != nil {
   526  				return nil, errwrap.Wrap(errors.New("can not setup tap device"), err)
   527  			}
   528  			err = netlink.LinkSetMaster(link, br)
   529  			if err != nil {
   530  				rErr := tuntap.RemovePersistentIface(n.runtime.IfName, tuntap.Tap)
   531  				if rErr != nil {
   532  					stderr.PrintE("warning: could not cleanup tap interface", rErr)
   533  				}
   534  				return nil, errwrap.Wrap(errors.New("can not add tap interface to bridge"), err)
   535  			}
   536  
   537  			ifName := link.Attrs().Name
   538  			n.runtime.IfName = ifName
   539  
   540  			err = kvmSetupNetAddressing(&network, n, ifName)
   541  			if err != nil {
   542  				return nil, err
   543  			}
   544  
   545  			if n.conf.IsDefaultGateway {
   546  				n.runtime.IP4.Routes = append(
   547  					n.runtime.IP4.Routes,
   548  					cnitypes.Route{Dst: *defaultNet, GW: n.runtime.IP4.Gateway},
   549  				)
   550  				defaultGatewaySet = true
   551  				config.IsGw = true
   552  			}
   553  
   554  			if config.IsGw {
   555  				err = ensureHasAddr(
   556  					br,
   557  					&net.IPNet{
   558  						IP:   n.runtime.IP4.Gateway,
   559  						Mask: net.IPMask(n.runtime.Mask),
   560  					},
   561  				)
   562  
   563  				if err != nil {
   564  					return nil, errwrap.Wrap(fmt.Errorf("cannot add address to host bridge device %q", br.Name), err)
   565  				}
   566  			}
   567  
   568  		case "macvlan":
   569  			config := MacVTapNetConf{}
   570  			if err := json.Unmarshal(n.confBytes, &config); err != nil {
   571  				return nil, errwrap.Wrap(fmt.Errorf("error parsing %q result", n.conf.Name), err)
   572  			}
   573  			link, err := setupMacVTapDevice(podID, config, i)
   574  			if err != nil {
   575  				return nil, err
   576  			}
   577  			ifName := link.Attrs().Name
   578  			n.runtime.IfName = ifName
   579  
   580  			err = kvmSetupNetAddressing(&network, n, ifName)
   581  			if err != nil {
   582  				return nil, err
   583  			}
   584  
   585  		default:
   586  			return nil, fmt.Errorf("network %q have unsupported type: %q", n.conf.Name, n.conf.Type)
   587  		}
   588  
   589  		// Check if there's any other network set as default gateway
   590  		if defaultGatewaySet {
   591  			for _, route := range n.runtime.IP4.Routes {
   592  				if (defaultNet.String() == route.Dst.String()) && !n.conf.IsDefaultGateway {
   593  					return nil, fmt.Errorf("flannel config enables default gateway and IPAM sets default gateway via %q", n.runtime.IP4.Gateway)
   594  				}
   595  			}
   596  		}
   597  
   598  		// Generate rkt-resolv.conf if it's not already there.
   599  		// The first network plugin that supplies a non-empty
   600  		// DNS response will win, unless noDNS is true (--dns passed to rkt run)
   601  		if !common.IsDNSZero(&n.runtime.DNS) && !noDNS {
   602  			if !podHasResolvConf {
   603  				err := ioutil.WriteFile(
   604  					resolvPath,
   605  					[]byte(common.MakeResolvConf(n.runtime.DNS, "Generated by rkt from network "+n.conf.Name)),
   606  					0644)
   607  				if err != nil {
   608  					return nil, errwrap.Wrap(fmt.Errorf("error creating resolv.conf"), err)
   609  				}
   610  				podHasResolvConf = true
   611  			} else {
   612  				stderr.Printf("Warning: network %v plugin specified DNS configuration, but DNS already supplied", n.conf.Name)
   613  			}
   614  		}
   615  
   616  		if n.conf.IPMasq {
   617  			chain := cniutils.FormatChainName(n.conf.Name, podID.String())
   618  			comment := cniutils.FormatComment(n.conf.Name, podID.String())
   619  			if err := ip.SetupIPMasq(&net.IPNet{
   620  				IP:   n.runtime.IP,
   621  				Mask: net.IPMask(n.runtime.Mask),
   622  			}, chain, comment); err != nil {
   623  				return nil, err
   624  			}
   625  		}
   626  		network.nets[i] = n
   627  	}
   628  	podIP, err := network.GetForwardableNetPodIP()
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  	if err := network.setupForwarding(); err != nil {
   633  		network.teardownForwarding()
   634  		return nil, err
   635  	}
   636  	if err := network.forwardPorts(fps, podIP); err != nil {
   637  		network.teardownForwarding()
   638  		return nil, err
   639  	}
   640  
   641  	return &network, nil
   642  }
   643  
   644  /*
   645  extend Networking struct with methods to clean up kvm specific network configurations
   646  */
   647  
   648  // teardownKvmNets teardown every active networking from networking by
   649  // removing tuntap interface and releasing its ip from IPAM plugin
   650  func (n *Networking) teardownKvmNets() {
   651  	for _, an := range n.nets {
   652  		if an.conf.Type == "flannel" {
   653  			if err := kvmTransformFlannelNetwork(&an); err != nil {
   654  				stderr.PrintE("error transforming flannel network", err)
   655  				continue
   656  			}
   657  		}
   658  
   659  		switch an.conf.Type {
   660  		case "ptp", "bridge":
   661  			// remove tuntap interface
   662  			tuntap.RemovePersistentIface(an.runtime.IfName, tuntap.Tap)
   663  
   664  		case "macvlan":
   665  			link, err := netlink.LinkByName(an.runtime.IfName)
   666  			if err != nil {
   667  				stderr.PrintE(fmt.Sprintf("cannot find link `%v`", an.runtime.IfName), err)
   668  				continue
   669  			} else {
   670  				err := netlink.LinkDel(link)
   671  				if err != nil {
   672  					stderr.PrintE(fmt.Sprintf("cannot remove link `%v`", an.runtime.IfName), err)
   673  					continue
   674  				}
   675  			}
   676  
   677  		default:
   678  			stderr.Printf("unsupported network type: %q", an.conf.Type)
   679  			continue
   680  		}
   681  		// ugly hack again to directly call IPAM plugin to release IP
   682  		an.conf.Type = an.conf.IPAM.Type
   683  
   684  		_, err := n.execNetPlugin("DEL", &an, an.runtime.IfName)
   685  		if err != nil {
   686  			stderr.PrintE("error executing network plugin", err)
   687  		}
   688  		// remove masquerading if it was prepared
   689  		if an.conf.IPMasq {
   690  			chain := cniutils.FormatChainName(an.conf.Name, n.podID.String())
   691  			comment := cniutils.FormatChainName(an.conf.Name, n.podID.String())
   692  			err := ip.TeardownIPMasq(&net.IPNet{
   693  				IP:   an.runtime.IP,
   694  				Mask: net.IPMask(an.runtime.Mask),
   695  			}, chain, comment)
   696  			if err != nil {
   697  				stderr.PrintE("error on removing masquerading", err)
   698  			}
   699  		}
   700  	}
   701  }
   702  
   703  // kvmTeardown network teardown for kvm flavor based pods
   704  // similar to Networking.Teardown but without host namespaces
   705  func (n *Networking) kvmTeardown() {
   706  
   707  	if err := n.teardownForwarding(); err != nil {
   708  		stderr.PrintE("error removing forwarded ports (kvm)", err)
   709  	}
   710  	n.teardownKvmNets()
   711  }
   712  
   713  // Following methods implements behavior of netDescriber by activeNet
   714  // (behavior required by stage1/init/kvm package and its kernel parameters configuration)
   715  
   716  func (an activeNet) HostIP() net.IP {
   717  	return an.runtime.HostIP
   718  }
   719  func (an activeNet) GuestIP() net.IP {
   720  	return an.runtime.IP
   721  }
   722  func (an activeNet) IfName() string {
   723  	if an.conf.Type == "macvlan" {
   724  		// macvtap device passed as parameter to lkvm binary have different
   725  		// kind of name, path to /dev/tapN made with N as link index
   726  		link, err := netlink.LinkByName(an.runtime.IfName)
   727  		if err != nil {
   728  			stderr.PrintE(fmt.Sprintf("cannot get interface '%v'", an.runtime.IfName), err)
   729  			return ""
   730  		}
   731  		return fmt.Sprintf("/dev/tap%d", link.Attrs().Index)
   732  	}
   733  	return an.runtime.IfName
   734  }
   735  func (an activeNet) Mask() net.IP {
   736  	return an.runtime.Mask
   737  }
   738  func (an activeNet) Name() string {
   739  	return an.conf.Name
   740  }
   741  func (an activeNet) IPMasq() bool {
   742  	return an.conf.IPMasq
   743  }
   744  func (an activeNet) Gateway() net.IP {
   745  	return an.runtime.IP4.Gateway
   746  }
   747  func (an activeNet) Routes() []cnitypes.Route {
   748  	return an.runtime.IP4.Routes
   749  }
   750  
   751  // GetActiveNetworks returns activeNets to be used as NetDescriptors
   752  // by plugins, which are required for stage1 executor to run (only for KVM)
   753  func (e *Networking) GetActiveNetworks() []activeNet {
   754  	return e.nets
   755  }