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
}