123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /*
- Copyright 2017 The Kubernetes Authors.
- 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 util
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/sets"
- "k8s.io/client-go/tools/record"
- helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
- utilnet "k8s.io/utils/net"
- "k8s.io/klog"
- )
- const (
- // IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space
- IPv4ZeroCIDR = "0.0.0.0/0"
- // IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space
- IPv6ZeroCIDR = "::/0"
- )
- var (
- // ErrAddressNotAllowed indicates the address is not allowed
- ErrAddressNotAllowed = errors.New("address not allowed")
- // ErrNoAddresses indicates there are no addresses for the hostname
- ErrNoAddresses = errors.New("No addresses for hostname")
- )
- // IsZeroCIDR checks whether the input CIDR string is either
- // the IPv4 or IPv6 zero CIDR
- func IsZeroCIDR(cidr string) bool {
- if cidr == IPv4ZeroCIDR || cidr == IPv6ZeroCIDR {
- return true
- }
- return false
- }
- // IsProxyableIP checks if a given IP address is permitted to be proxied
- func IsProxyableIP(ip string) error {
- netIP := net.ParseIP(ip)
- if netIP == nil {
- return ErrAddressNotAllowed
- }
- return isProxyableIP(netIP)
- }
- func isProxyableIP(ip net.IP) error {
- if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
- return ErrAddressNotAllowed
- }
- return nil
- }
- // Resolver is an interface for net.Resolver
- type Resolver interface {
- LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error)
- }
- // IsProxyableHostname checks if the IP addresses for a given hostname are permitted to be proxied
- func IsProxyableHostname(ctx context.Context, resolv Resolver, hostname string) error {
- resp, err := resolv.LookupIPAddr(ctx, hostname)
- if err != nil {
- return err
- }
- if len(resp) == 0 {
- return ErrNoAddresses
- }
- for _, host := range resp {
- if err := isProxyableIP(host.IP); err != nil {
- return err
- }
- }
- return nil
- }
- // IsLocalIP checks if a given IP address is bound to an interface
- // on the local system
- func IsLocalIP(ip string) (bool, error) {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return false, err
- }
- for i := range addrs {
- intf, _, err := net.ParseCIDR(addrs[i].String())
- if err != nil {
- return false, err
- }
- if net.ParseIP(ip).Equal(intf) {
- return true, nil
- }
- }
- return false, nil
- }
- // ShouldSkipService checks if a given service should skip proxying
- func ShouldSkipService(svcName types.NamespacedName, service *v1.Service) bool {
- // if ClusterIP is "None" or empty, skip proxying
- if !helper.IsServiceIPSet(service) {
- klog.V(3).Infof("Skipping service %s due to clusterIP = %q", svcName, service.Spec.ClusterIP)
- return true
- }
- // Even if ClusterIP is set, ServiceTypeExternalName services don't get proxied
- if service.Spec.Type == v1.ServiceTypeExternalName {
- klog.V(3).Infof("Skipping service %s due to Type=ExternalName", svcName)
- return true
- }
- return false
- }
- // GetNodeAddresses return all matched node IP addresses based on given cidr slice.
- // Some callers, e.g. IPVS proxier, need concrete IPs, not ranges, which is why this exists.
- // NetworkInterfacer is injected for test purpose.
- // We expect the cidrs passed in is already validated.
- // Given an empty input `[]`, it will return `0.0.0.0/0` and `::/0` directly.
- // If multiple cidrs is given, it will return the minimal IP sets, e.g. given input `[1.2.0.0/16, 0.0.0.0/0]`, it will
- // only return `0.0.0.0/0`.
- // NOTE: GetNodeAddresses only accepts CIDRs, if you want concrete IPs, e.g. 1.2.3.4, then the input should be 1.2.3.4/32.
- func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error) {
- uniqueAddressList := sets.NewString()
- if len(cidrs) == 0 {
- uniqueAddressList.Insert(IPv4ZeroCIDR)
- uniqueAddressList.Insert(IPv6ZeroCIDR)
- return uniqueAddressList, nil
- }
- // First round of iteration to pick out `0.0.0.0/0` or `::/0` for the sake of excluding non-zero IPs.
- for _, cidr := range cidrs {
- if IsZeroCIDR(cidr) {
- uniqueAddressList.Insert(cidr)
- }
- }
- // Second round of iteration to parse IPs based on cidr.
- for _, cidr := range cidrs {
- if IsZeroCIDR(cidr) {
- continue
- }
- _, ipNet, _ := net.ParseCIDR(cidr)
- itfs, err := nw.Interfaces()
- if err != nil {
- return nil, fmt.Errorf("error listing all interfaces from host, error: %v", err)
- }
- for _, itf := range itfs {
- addrs, err := nw.Addrs(&itf)
- if err != nil {
- return nil, fmt.Errorf("error getting address from interface %s, error: %v", itf.Name, err)
- }
- for _, addr := range addrs {
- if addr == nil {
- continue
- }
- ip, _, err := net.ParseCIDR(addr.String())
- if err != nil {
- return nil, fmt.Errorf("error parsing CIDR for interface %s, error: %v", itf.Name, err)
- }
- if ipNet.Contains(ip) {
- if utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv6ZeroCIDR) {
- uniqueAddressList.Insert(ip.String())
- }
- if !utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv4ZeroCIDR) {
- uniqueAddressList.Insert(ip.String())
- }
- }
- }
- }
- }
- return uniqueAddressList, nil
- }
- // LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event.
- func LogAndEmitIncorrectIPVersionEvent(recorder record.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) {
- errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName)
- klog.Errorf("%s (service %s/%s).", errMsg, svcNamespace, svcName)
- if recorder != nil {
- recorder.Eventf(
- &v1.ObjectReference{
- Kind: "Service",
- Name: svcName,
- Namespace: svcNamespace,
- UID: svcUID,
- }, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", errMsg)
- }
- }
- // FilterIncorrectIPVersion filters out the incorrect IP version case from a slice of IP strings.
- func FilterIncorrectIPVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) {
- return filterWithCondition(ipStrings, isIPv6Mode, utilnet.IsIPv6String)
- }
- // FilterIncorrectCIDRVersion filters out the incorrect IP version case from a slice of CIDR strings.
- func FilterIncorrectCIDRVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) {
- return filterWithCondition(ipStrings, isIPv6Mode, utilnet.IsIPv6CIDRString)
- }
- func filterWithCondition(strs []string, expectedCondition bool, conditionFunc func(string) bool) ([]string, []string) {
- var corrects, incorrects []string
- for _, str := range strs {
- if conditionFunc(str) != expectedCondition {
- incorrects = append(incorrects, str)
- } else {
- corrects = append(corrects, str)
- }
- }
- return corrects, incorrects
- }
- // AppendPortIfNeeded appends the given port to IP address unless it is already in
- // "ipv4:port" or "[ipv6]:port" format.
- func AppendPortIfNeeded(addr string, port int32) string {
- // Return if address is already in "ipv4:port" or "[ipv6]:port" format.
- if _, _, err := net.SplitHostPort(addr); err == nil {
- return addr
- }
- // Simply return for invalid case. This should be caught by validation instead.
- ip := net.ParseIP(addr)
- if ip == nil {
- return addr
- }
- // Append port to address.
- if ip.To4() != nil {
- return fmt.Sprintf("%s:%d", addr, port)
- }
- return fmt.Sprintf("[%s]:%d", addr, port)
- }
|