func()

in network/network_linux.go [463:626]


func (nm *networkManager) connectExternalInterface(extIf *externalInterface, nwInfo *EndpointInfo) error {
	var (
		err           error
		networkClient NetworkClient
	)

	defer func() {
		logger.Info("Connecting interface completed", zap.String("Name", extIf.Name), zap.Error(err))
	}()
	// Check whether this interface is already connected.
	if extIf.BridgeName != "" {
		logger.Info("Interface is already connected to bridge", zap.String("BridgeName", extIf.BridgeName))
		return nil
	}

	// Find the external interface.
	hostIf, err := net.InterfaceByName(extIf.Name)
	if err != nil {
		return err
	}

	// If a bridge name is not specified, generate one based on the external interface index.
	bridgeName := nwInfo.BridgeName
	if bridgeName == "" {
		bridgeName = fmt.Sprintf("%s%d", bridgePrefix, hostIf.Index)
	}

	opt, _ := nwInfo.Options[genericData].(map[string]interface{})
	if opt != nil && opt[VlanIDKey] != nil {
		networkClient = NewOVSClient(bridgeName, extIf.Name, ovsctl.NewOvsctl(), nm.netlink, nm.plClient)
	} else {
		networkClient = NewLinuxBridgeClient(bridgeName, extIf.Name, *nwInfo, nm.netlink, nm.plClient)
	}

	// Check if the bridge already exists.
	bridge, err := net.InterfaceByName(bridgeName)

	if err != nil {
		// Create the bridge.
		if err = networkClient.CreateBridge(); err != nil {
			logger.Error("Error while creating bridge", zap.Error(err))
			return err
		}

		bridge, err = net.InterfaceByName(bridgeName)
		if err != nil {
			return err
		}
	} else {
		// Use the existing bridge.
		logger.Info("Found existing bridge", zap.String("bridgeName", bridgeName))
	}

	defer func() {
		if err != nil {
			logger.Info("cleanup network")
			nm.disconnectExternalInterface(extIf, networkClient)
		}
	}()

	// Save host IP configuration.
	err = nm.saveIPConfig(hostIf, extIf)
	if err != nil {
		logger.Error("Failed to save IP configuration for interface",
			zap.String("Name", hostIf.Name), zap.Error(err))
	}

	/*
		If custom dns server is updated, VM needs reboot for the change to take effect.
	*/
	isGreaterOrEqualUbuntu17 := isGreaterOrEqaulUbuntuVersion(ubuntuVersion17)
	isSystemdResolvedActive := false
	if isGreaterOrEqualUbuntu17 {
		// Don't copy dns servers if systemd-resolved isn't available
		if _, cmderr := nm.plClient.ExecuteRawCommand("systemctl status systemd-resolved"); cmderr == nil {
			isSystemdResolvedActive = true
			logger.Info("Saving dns config from", zap.String("Name", hostIf.Name))
			if err = nm.saveDNSConfig(extIf); err != nil {
				logger.Error("Failed to save dns config", zap.Error(err))
				return err
			}
		}
	}

	logger.Info("Modifying interfaces", zap.String("Name", hostIf.Name))

	// External interface down.
	err = nm.netlink.SetLinkState(hostIf.Name, false)
	if err != nil {
		return errors.Wrap(err, "failed to set external interface down")
	}

	// Connect the external interface to the bridge.
	if err = networkClient.SetBridgeMasterToHostInterface(); err != nil {
		return errors.Wrap(err, "failed to connect external interface to bridge")
	}

	// External interface up.
	err = nm.netlink.SetLinkState(hostIf.Name, true)
	if err != nil {
		return errors.Wrap(err, "failed to set external interface up")
	}

	// Bridge up.
	err = nm.netlink.SetLinkState(bridgeName, true)
	if err != nil {
		return errors.Wrap(err, "failed to set bridge link state up")
	}

	// Add the bridge rules.
	err = networkClient.AddL2Rules(extIf)
	if err != nil {
		return errors.Wrap(err, "failed to add bridge rules")
	}

	// External interface hairpin on.
	if !nwInfo.DisableHairpinOnHostInterface {
		logger.Info("Setting link hairpin on", zap.String("Name", hostIf.Name))
		if err = networkClient.SetHairpinOnHostInterface(true); err != nil {
			return err
		}
	}

	// Apply IP configuration to the bridge for host traffic.
	err = nm.applyIPConfig(extIf, bridge)
	if err != nil {
		logger.Error("Failed to apply interface IP configuration", zap.Error(err))
		return err
	}

	if isGreaterOrEqualUbuntu17 && isSystemdResolvedActive {
		if err = nm.applyDNSConfig(extIf, bridgeName); err != nil {
			logger.Error("Failed to apply DNS configuration with", zap.Error(err))
			return err
		}

		logger.Info("Applied dns config on", zap.Any("DNSInfo", extIf.DNSInfo), zap.String("bridgeName", bridgeName))
	}

	if nwInfo.IPV6Mode == IPV6Nat {
		// adds pod cidr gateway ip to bridge
		if err = nm.addIpv6NatGateway(nwInfo); err != nil {
			logger.Error("Adding IPv6 Nat Gateway failed with", zap.Error(err))
			return err
		}

		if err = nm.addIpv6SnatRule(extIf, nwInfo); err != nil {
			logger.Error("Adding IPv6 Snat Rule failed with", zap.Error(err))
			return err
		}

		// unmark packet if set by kube-proxy to skip kube-postrouting rule and processed
		// by cni snat rule
		if err = nm.iptablesClient.InsertIptableRule(iptables.V6, iptables.Mangle, iptables.Postrouting, "", "MARK --set-mark 0x0"); err != nil {
			logger.Error("Adding Iptable mangle rule failed", zap.Error(err))
			return err
		}
	}

	extIf.BridgeName = bridgeName
	logger.Info("Connected interface to bridge", zap.String("Name", extIf.Name), zap.String("BridgeName", extIf.BridgeName))

	return nil
}