func()

in network/endpoint_linux.go [52:265]


func (nw *network) newEndpointImpl(
	_ apipaClient,
	nl netlink.NetlinkInterface,
	plc platform.ExecClient,
	netioCli netio.NetIOInterface,
	testEpClient EndpointClient,
	nsc NamespaceClientInterface,
	iptc ipTablesClient,
	dhcpclient dhcpClient,
	epInfo *EndpointInfo,
) (*endpoint, error) {
	var (
		err         error
		hostIfName  string
		contIfName  string
		localIP     string
		vlanid      = 0
		containerIf *net.Interface
	)

	if nw.Endpoints[epInfo.EndpointID] != nil {
		logger.Info("[net] Endpoint already exists.")
		err = errEndpointExists
		return nil, err
	}

	if epInfo.Data != nil {
		if _, ok := epInfo.Data[VlanIDKey]; ok {
			vlanid = epInfo.Data[VlanIDKey].(int)
		}

		if _, ok := epInfo.Data[LocalIPKey]; ok {
			localIP = epInfo.Data[LocalIPKey].(string)
		}
	}

	if _, ok := epInfo.Data[OptVethName]; ok {
		key := epInfo.Data[OptVethName].(string)
		logger.Info("Generate veth name based on the key provided", zap.String("key", key))
		vethname := generateVethName(key)
		hostIfName = fmt.Sprintf("%s%s", hostVEthInterfacePrefix, vethname)
		// we don't save the contIfName here or below because it will be renamed to ep.IfName anyway when we call SetupContainerInterfaces in the clients
		// however, contIfName is still passed into our clients
		contIfName = fmt.Sprintf("%s%s2", hostVEthInterfacePrefix, vethname)
	} else {
		// Create a veth pair.
		logger.Info("Generate veth name based on endpoint id")
		hostIfName = fmt.Sprintf("%s%s", hostVEthInterfacePrefix, epInfo.EndpointID[:7])
		contIfName = fmt.Sprintf("%s%s-2", hostVEthInterfacePrefix, epInfo.EndpointID[:7])
	}

	nicName := epInfo.IfName
	// infra nic nicname will look like eth0, and delegated/secondary nics will be moved into the container namespace
	if epInfo.NICType != cns.InfraNIC {
		nicName = epInfo.MasterIfName
	}

	ep := &endpoint{
		Id: epInfo.EndpointID,
		// IfName should end up being eth0 in non-delegated nic cases
		IfName:                   nicName, // container veth pair name. In cnm, we won't rename this and docker expects veth name.
		HostIfName:               hostIfName,
		InfraVnetIP:              epInfo.InfraVnetIP,
		LocalIP:                  localIP,
		IPAddresses:              epInfo.IPAddresses,
		DNS:                      epInfo.EndpointDNS,
		VlanID:                   vlanid,
		EnableSnatOnHost:         epInfo.EnableSnatOnHost,
		EnableInfraVnet:          epInfo.EnableInfraVnet,
		EnableMultitenancy:       epInfo.EnableMultiTenancy,
		AllowInboundFromHostToNC: epInfo.AllowInboundFromHostToNC,
		AllowInboundFromNCToHost: epInfo.AllowInboundFromNCToHost,
		NetworkNameSpace:         epInfo.NetNsPath,
		ContainerID:              epInfo.ContainerID,
		PODName:                  epInfo.PODName,
		PODNameSpace:             epInfo.PODNameSpace,
		Routes:                   epInfo.Routes,
		SecondaryInterfaces:      make(map[string]*InterfaceInfo),
		NICType:                  epInfo.NICType,
	}
	if nw.extIf != nil {
		ep.Gateways = []net.IP{nw.extIf.IPv4Gateway}
	}

	// testEpClient is non-nil only when the endpoint is created for the unit test
	// resetting epClient to testEpClient in loop to use the test endpoint client if specified
	epClient := testEpClient
	if epClient == nil {
		//nolint:gocritic
		if vlanid != 0 {
			if nw.Mode == opModeTransparentVlan {
				logger.Info("Transparent vlan client")
				if _, ok := epInfo.Data[SnatBridgeIPKey]; ok {
					nw.SnatBridgeIP = epInfo.Data[SnatBridgeIPKey].(string)
				}
				epClient = NewTransparentVlanEndpointClient(nw, epInfo, hostIfName, contIfName, vlanid, localIP, nl, plc, nsc, iptc)
			} else {
				logger.Info("OVS client")
				if _, ok := epInfo.Data[SnatBridgeIPKey]; ok {
					nw.SnatBridgeIP = epInfo.Data[SnatBridgeIPKey].(string)
				}

				epClient = NewOVSEndpointClient(
					nw,
					epInfo,
					hostIfName,
					contIfName,
					vlanid,
					localIP,
					nl,
					ovsctl.NewOvsctl(),
					plc,
					iptc)
			}
		} else if nw.Mode != opModeTransparent {
			logger.Info("Bridge client")
			epClient = NewLinuxBridgeEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, plc)
		} else if epInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC {
			logger.Info("Secondary client")
			epClient = NewSecondaryEndpointClient(nl, netioCli, plc, nsc, dhcpclient, ep)
		} else {
			logger.Info("Transparent client")
			epClient = NewTransparentEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, netioCli, plc)
		}
	}

	//nolint:gocritic
	defer func(client EndpointClient, contIfName string) {
		// Cleanup on failure.
		if err != nil {
			logger.Error("CNI error. Delete Endpoint and rules that are created", zap.Error(err), zap.String("contIfName", contIfName))
			if containerIf != nil {
				client.DeleteEndpointRules(ep)
			}
			// set deleteHostVeth to true to cleanup host veth interface if created
			//nolint:errcheck // ignore error
			client.DeleteEndpoints(ep)
		}
	}(epClient, contIfName)

	// wrapping endpoint client commands in anonymous func so that namespace can be exit and closed before the next loop
	//nolint:wrapcheck // ignore wrap check
	err = func() error {
		if epErr := epClient.AddEndpoints(epInfo); epErr != nil {
			return epErr
		}

		if epInfo.NICType == cns.InfraNIC {
			var epErr error
			containerIf, epErr = netioCli.GetNetworkInterfaceByName(contIfName)
			if epErr != nil {
				return epErr
			}
			ep.MacAddress = containerIf.HardwareAddr
		}

		// Setup rules for IP addresses on the container interface.
		if epErr := epClient.AddEndpointRules(epInfo); epErr != nil {
			return epErr
		}

		// If a network namespace for the container interface is specified...
		if epInfo.NetNsPath != "" {
			// Open the network namespace.
			logger.Info("Opening netns", zap.Any("NetNsPath", epInfo.NetNsPath))
			ns, epErr := nsc.OpenNamespace(epInfo.NetNsPath)
			if epErr != nil {
				return epErr
			}
			defer ns.Close()

			if epErr := epClient.MoveEndpointsToContainerNS(epInfo, ns.GetFd()); epErr != nil {
				return epErr
			}

			// Enter the container network namespace.
			logger.Info("Entering netns", zap.Any("NetNsPath", epInfo.NetNsPath))
			if epErr := ns.Enter(); epErr != nil {
				return epErr
			}

			// Return to host network namespace.
			defer func() {
				logger.Info("Exiting netns", zap.Any("NetNsPath", epInfo.NetNsPath))
				if epErr := ns.Exit(); epErr != nil {
					logger.Error("Failed to exit netns with", zap.Error(epErr))
				}
			}()
		}

		if epInfo.IPV6Mode != "" {
			// Enable ipv6 setting in container
			logger.Info("Enable ipv6 setting in container.")
			nuc := networkutils.NewNetworkUtils(nl, plc)
			if epErr := nuc.UpdateIPV6Setting(0); epErr != nil {
				return fmt.Errorf("enable ipv6 in container failed:%w", epErr)
			}
		}

		// If a name for the container interface is specified...
		if epInfo.IfName != "" {
			if epErr := epClient.SetupContainerInterfaces(epInfo); epErr != nil {
				return epErr
			}
		}

		return epClient.ConfigureContainerInterfacesAndRoutes(epInfo)
	}()
	if err != nil {
		return nil, err
	}

	return ep, nil
}