-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New autodetection method KubernetesInternalIP added
- Loading branch information
Hanamantagoud
committed
Nov 10, 2021
1 parent
cc42121
commit 95fae8a
Showing
4 changed files
with
310 additions
and
192 deletions.
There are no files selected for viewing
232 changes: 232 additions & 0 deletions
232
pkg/lifecycle/startup/autodetection/autodetection_methods.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
// Copyright (c) 2016 Tigera, Inc. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package autodetection | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"regexp" | ||
"strings" | ||
|
||
cnet "github.com/projectcalico/libcalico-go/lib/net" | ||
"github.com/projectcalico/node/pkg/lifecycle/utils" | ||
log "github.com/sirupsen/logrus" | ||
v1 "k8s.io/api/core/v1" | ||
) | ||
|
||
const ( | ||
AUTODETECTION_METHOD_FIRST = "first-found" | ||
AUTODETECTION_METHOD_CAN_REACH = "can-reach=" | ||
AUTODETECTION_METHOD_INTERFACE = "interface=" | ||
AUTODETECTION_METHOD_SKIP_INTERFACE = "skip-interface=" | ||
AUTODETECTION_METHOD_CIDR = "cidr=" | ||
K8S_INTERNAL_IP = "kubernetes-internal-ip" | ||
) | ||
|
||
// autoDetectCIDR auto-detects the IP and Network using the requested | ||
// detection method. | ||
func AutoDetectCIDR(method string, version int, k8sNode *v1.Node, defInterfacesToExclude []string) *cnet.IPNet { | ||
if method == "" || method == AUTODETECTION_METHOD_FIRST { | ||
// Autodetect the IP by enumerating all interfaces (excluding | ||
// known internal interfaces). | ||
return autoDetectCIDRFirstFound(version, defInterfacesToExclude) | ||
} else if strings.HasPrefix(method, AUTODETECTION_METHOD_INTERFACE) { | ||
// Autodetect the IP from the specified interface. | ||
ifStr := strings.TrimPrefix(method, AUTODETECTION_METHOD_INTERFACE) | ||
// Regexes are passed in as a string separated by "," | ||
ifRegexes := regexp.MustCompile(`\s*,\s*`).Split(ifStr, -1) | ||
return autoDetectCIDRByInterface(ifRegexes, version) | ||
} else if strings.HasPrefix(method, AUTODETECTION_METHOD_CIDR) { | ||
// Autodetect the IP by filtering interface by its address. | ||
cidrStr := strings.TrimPrefix(method, AUTODETECTION_METHOD_CIDR) | ||
// CIDRs are passed in as a string separated by "," | ||
matches := []cnet.IPNet{} | ||
for _, r := range regexp.MustCompile(`\s*,\s*`).Split(cidrStr, -1) { | ||
_, cidr, err := cnet.ParseCIDR(r) | ||
if err != nil { | ||
log.Errorf("Invalid CIDR %q for IP autodetection method: %s", r, method) | ||
return nil | ||
} | ||
matches = append(matches, *cidr) | ||
} | ||
return autoDetectCIDRByCIDR(matches, version) | ||
} else if strings.HasPrefix(method, AUTODETECTION_METHOD_CAN_REACH) { | ||
// Autodetect the IP by connecting a UDP socket to a supplied address. | ||
destStr := strings.TrimPrefix(method, AUTODETECTION_METHOD_CAN_REACH) | ||
return autoDetectCIDRByReach(destStr, version) | ||
} else if strings.HasPrefix(method, AUTODETECTION_METHOD_SKIP_INTERFACE) { | ||
// Autodetect the Ip by enumerating all interfaces (excluding | ||
// known internal interfaces and any interfaces whose name | ||
// matches the given regexes). | ||
ifStr := strings.TrimPrefix(method, AUTODETECTION_METHOD_SKIP_INTERFACE) | ||
// Regexes are passed in as a string separated by "," | ||
ifRegexes := regexp.MustCompile(`\s*,\s*`).Split(ifStr, -1) | ||
return autoDetectCIDRBySkipInterface(ifRegexes, version, defInterfacesToExclude) | ||
} else if strings.HasPrefix(method, K8S_INTERNAL_IP) { | ||
// K8s InternalIP configured for node is used | ||
if k8sNode == nil { | ||
log.Error("Cannot use method 'kubernetes-internal-ip' when not running on a Kubernetes cluster") | ||
return nil | ||
} | ||
return autoDetectUsingK8sInternalIP(version, k8sNode) | ||
} | ||
|
||
// The autodetection method is not recognised and is required. Exit. | ||
log.Errorf("Invalid IP autodetection method: %s", method) | ||
utils.Terminate() | ||
return nil | ||
} | ||
|
||
// autoDetectCIDRFirstFound auto-detects the first valid Network it finds across | ||
// all interfaces (excluding common known internal interface names). | ||
func autoDetectCIDRFirstFound(version int, defInterfacesToExclude []string) *cnet.IPNet { | ||
incl := []string{} | ||
|
||
iface, cidr, err := FilteredEnumeration(incl, defInterfacesToExclude, nil, version) | ||
if err != nil { | ||
log.Warnf("Unable to auto-detect an IPv%d address: %s", version, err) | ||
return nil | ||
} | ||
|
||
log.Infof("Using autodetected IPv%d address on interface %s: %s", version, iface.Name, cidr.String()) | ||
|
||
return cidr | ||
} | ||
|
||
// autoDetectCIDRByInterface auto-detects the first valid Network on the interfaces | ||
// matching the supplied interface regex. | ||
func autoDetectCIDRByInterface(ifaceRegexes []string, version int) *cnet.IPNet { | ||
iface, cidr, err := FilteredEnumeration(ifaceRegexes, nil, nil, version) | ||
if err != nil { | ||
log.Warnf("Unable to auto-detect an IPv%d address using interface regexes %v: %s", version, ifaceRegexes, err) | ||
return nil | ||
} | ||
|
||
log.Infof("Using autodetected IPv%d address %s on matching interface %s", version, cidr.String(), iface.Name) | ||
|
||
return cidr | ||
} | ||
|
||
// autoDetectCIDRByCIDR auto-detects the first valid Network on the interfaces | ||
// matching the supplied cidr. | ||
func autoDetectCIDRByCIDR(matches []cnet.IPNet, version int) *cnet.IPNet { | ||
iface, cidr, err := FilteredEnumeration(nil, nil, matches, version) | ||
if err != nil { | ||
log.Warnf("Unable to auto-detect an IPv%d address using interface cidr %s: %s", version, matches, err) | ||
return nil | ||
} | ||
|
||
log.Infof("Using autodetected IPv%d address %s on interface %s matching cidrs %+v", version, cidr.String(), iface.Name, matches) | ||
|
||
return cidr | ||
} | ||
|
||
// autoDetectCIDRByReach auto-detects the IP and Network by setting up a UDP | ||
// connection to a "reach" address. | ||
func autoDetectCIDRByReach(dest string, version int) *cnet.IPNet { | ||
if cidr, err := ReachDestination(dest, version); err != nil { | ||
log.Warnf("Unable to auto-detect IPv%d address by connecting to %s: %s", version, dest, err) | ||
return nil | ||
} else { | ||
log.Infof("Using autodetected IPv%d address %s, detected by connecting to %s", version, cidr.String(), dest) | ||
return cidr | ||
} | ||
} | ||
|
||
// autoDetectCIDRBySkipInterface auto-detects the first valid Network on the interfaces | ||
// matching the supplied interface regexes. | ||
func autoDetectCIDRBySkipInterface(ifaceRegexes []string, version int, defInterfacesToExclude []string) *cnet.IPNet { | ||
incl := []string{} | ||
excl := defInterfacesToExclude | ||
excl = append(excl, ifaceRegexes...) | ||
|
||
iface, cidr, err := FilteredEnumeration(incl, excl, nil, version) | ||
if err != nil { | ||
log.Warnf("Unable to auto-detect an IPv%d address while excluding %v: %s", version, ifaceRegexes, err) | ||
return nil | ||
} | ||
|
||
log.Infof("Using autodetected IPv%d address on interface %s: %s while skipping matching interfaces", version, iface.Name, cidr.String()) | ||
return cidr | ||
} | ||
|
||
// autoDetectUsingK8sInternalIP reads K8s Node InternalIP. | ||
func autoDetectUsingK8sInternalIP(version int, k8sNode *v1.Node) *cnet.IPNet { | ||
var address string | ||
var err error | ||
|
||
nodeAddresses := k8sNode.Status.Addresses | ||
for _, addr := range nodeAddresses { | ||
if addr.Type == v1.NodeInternalIP { | ||
if (version == 4 && utils.IsIPv4String(addr.Address)) || (version == 6 && utils.IsIPv6String(addr.Address)) { | ||
address, err = GetLocalCIDR(addr.Address, version, GetInterfaces) | ||
if err != nil { | ||
return nil | ||
} | ||
} | ||
} | ||
} | ||
|
||
_, ipNet, err := cnet.ParseCIDR(address) | ||
if err != nil { | ||
log.Errorf("Unable to parse CIDR %v : ", address, err) | ||
return nil | ||
} | ||
return ipNet | ||
} | ||
|
||
// getLocalCIDR attempts to merge CIDR information from the host with the given IP address. | ||
// If a CIDR is provided, then it is simply returned. | ||
// If an IP is provided, it attempts to find the matching interface on the host to detect the appropriate prefix length. | ||
// If no match is found, the IP will be returned unmodified. | ||
func GetLocalCIDR(ip string, version int, getInterfaces func([]string, []string, int) ([]Interface, error)) (string, error) { | ||
if strings.Contains(ip, "/") { | ||
// Already a CIDR | ||
return ip, nil | ||
} | ||
|
||
var destCIDR net.IP | ||
if version == 4 { | ||
destCIDR = net.ParseIP(ip).To4() | ||
} else { | ||
destCIDR = net.ParseIP(ip).To16() | ||
} | ||
|
||
if destCIDR == nil { | ||
return ip, fmt.Errorf("%s is invalid.", ip) | ||
} | ||
|
||
// Get a full list of interface and IPs and find the CIDR matching the | ||
// found IP. | ||
interfaces, err := getInterfaces(nil, nil, version) | ||
if err != nil { | ||
return ip, err | ||
} | ||
|
||
log.Debugf("Auto-detecting IPv%d CIDR of %s", version, ip) | ||
for _, iface := range interfaces { | ||
log.WithField("Name", iface.Name).Debug("Checking interface") | ||
for _, cidr := range iface.Cidrs { | ||
log.WithField("CIDR", cidr.String()).Debug("Found") | ||
if cidr.IP.Equal(destCIDR) { | ||
log.WithField("CIDR", cidr.String()).Info("Including CIDR information from host interface.") | ||
return cidr.String(), nil | ||
} | ||
} | ||
} | ||
|
||
// Even if no CIDR is found, it doesn't think it needs to throw an exception | ||
log.Warnf("Unable to find matching host interface for IP %s", ip) | ||
return ip, nil | ||
} |
Oops, something went wrong.