mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			210 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2016 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 netsh
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"k8s.io/klog/v2"
 | |
| 	utilexec "k8s.io/utils/exec"
 | |
| )
 | |
| 
 | |
| // Interface is an injectable interface for running netsh commands.  Implementations must be goroutine-safe.
 | |
| type Interface interface {
 | |
| 	// EnsurePortProxyRule checks if the specified redirect exists, if not creates it
 | |
| 	EnsurePortProxyRule(args []string) (bool, error)
 | |
| 	// DeletePortProxyRule deletes the specified portproxy rule.  If the rule did not exist, return error.
 | |
| 	DeletePortProxyRule(args []string) error
 | |
| 	// EnsureIPAddress checks if the specified IP Address is added to vEthernet (HNSTransparent) interface, if not, add it.  If the address existed, return true.
 | |
| 	EnsureIPAddress(args []string, ip net.IP) (bool, error)
 | |
| 	// DeleteIPAddress checks if the specified IP address is present and, if so, deletes it.
 | |
| 	DeleteIPAddress(args []string) error
 | |
| 	// Restore runs `netsh exec` to restore portproxy or addresses using a file.
 | |
| 	// TODO Check if this is required, most likely not
 | |
| 	Restore(args []string) error
 | |
| 
 | |
| 	// GetInterfaceToAddIP returns the interface name where Service IP needs to be added
 | |
| 	// IP Address needs to be added for netsh portproxy to redirect traffic
 | |
| 	// Reads Environment variable INTERFACE_TO_ADD_SERVICE_IP, if it is not defined then "vEthernet (HNSTransparent)" is returned
 | |
| 	GetInterfaceToAddIP() string
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	cmdNetsh string = "netsh"
 | |
| )
 | |
| 
 | |
| // runner implements Interface in terms of exec("netsh").
 | |
| type runner struct {
 | |
| 	exec utilexec.Interface
 | |
| }
 | |
| 
 | |
| // New returns a new Interface which will exec netsh.
 | |
| func New(exec utilexec.Interface) Interface {
 | |
| 	runner := &runner{
 | |
| 		exec: exec,
 | |
| 	}
 | |
| 	return runner
 | |
| }
 | |
| 
 | |
| // EnsurePortProxyRule checks if the specified redirect exists, if not creates it.
 | |
| func (runner *runner) EnsurePortProxyRule(args []string) (bool, error) {
 | |
| 	klog.V(4).Infof("running netsh interface portproxy add v4tov4 %v", args)
 | |
| 	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
 | |
| 
 | |
| 	if err == nil {
 | |
| 		return true, nil
 | |
| 	}
 | |
| 	if ee, ok := err.(utilexec.ExitError); ok {
 | |
| 		// netsh uses exit(0) to indicate a success of the operation,
 | |
| 		// as compared to a malformed commandline, for example.
 | |
| 		if ee.Exited() && ee.ExitStatus() != 0 {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return false, fmt.Errorf("error checking portproxy rule: %v: %s", err, out)
 | |
| 
 | |
| }
 | |
| 
 | |
| // DeletePortProxyRule deletes the specified portproxy rule.  If the rule did not exist, return error.
 | |
| func (runner *runner) DeletePortProxyRule(args []string) error {
 | |
| 	klog.V(4).Infof("running netsh interface portproxy delete v4tov4 %v", args)
 | |
| 	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
 | |
| 
 | |
| 	if err == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if ee, ok := err.(utilexec.ExitError); ok {
 | |
| 		// netsh uses exit(0) to indicate a success of the operation,
 | |
| 		// as compared to a malformed commandline, for example.
 | |
| 		if ee.Exited() && ee.ExitStatus() == 0 {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 	return fmt.Errorf("error deleting portproxy rule: %v: %s", err, out)
 | |
| }
 | |
| 
 | |
| // EnsureIPAddress checks if the specified IP Address is added to interface identified by Environment variable INTERFACE_TO_ADD_SERVICE_IP, if not, add it.  If the address existed, return true.
 | |
| func (runner *runner) EnsureIPAddress(args []string, ip net.IP) (bool, error) {
 | |
| 	// Check if the ip address exists
 | |
| 	intName := runner.GetInterfaceToAddIP()
 | |
| 	argsShowAddress := []string{
 | |
| 		"interface", "ipv4", "show", "address",
 | |
| 		"name=" + intName,
 | |
| 	}
 | |
| 
 | |
| 	ipToCheck := ip.String()
 | |
| 
 | |
| 	exists, _ := checkIPExists(ipToCheck, argsShowAddress, runner)
 | |
| 	if exists == true {
 | |
| 		klog.V(4).Infof("not adding IP address %q as it already exists", ipToCheck)
 | |
| 		return true, nil
 | |
| 	}
 | |
| 
 | |
| 	// IP Address is not already added, add it now
 | |
| 	klog.V(4).Infof("running netsh interface ipv4 add address %v", args)
 | |
| 	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
 | |
| 
 | |
| 	if err == nil {
 | |
| 		// Once the IP Address is added, it takes a bit to initialize and show up when querying for it
 | |
| 		// Query all the IP addresses and see if the one we added is present
 | |
| 		// PS: We are using netsh interface ipv4 show address here to query all the IP addresses, instead of
 | |
| 		// querying net.InterfaceAddrs() as it returns the IP address as soon as it is added even though it is uninitialized
 | |
| 		klog.V(3).Infof("Waiting until IP: %v is added to the network adapter", ipToCheck)
 | |
| 		for {
 | |
| 			if exists, _ := checkIPExists(ipToCheck, argsShowAddress, runner); exists {
 | |
| 				return true, nil
 | |
| 			}
 | |
| 			time.Sleep(500 * time.Millisecond)
 | |
| 		}
 | |
| 	}
 | |
| 	if ee, ok := err.(utilexec.ExitError); ok {
 | |
| 		// netsh uses exit(0) to indicate a success of the operation,
 | |
| 		// as compared to a malformed commandline, for example.
 | |
| 		if ee.Exited() && ee.ExitStatus() != 0 {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return false, fmt.Errorf("error adding ipv4 address: %v: %s", err, out)
 | |
| }
 | |
| 
 | |
| // DeleteIPAddress checks if the specified IP address is present and, if so, deletes it.
 | |
| func (runner *runner) DeleteIPAddress(args []string) error {
 | |
| 	klog.V(4).Infof("running netsh interface ipv4 delete address %v", args)
 | |
| 	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
 | |
| 
 | |
| 	if err == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if ee, ok := err.(utilexec.ExitError); ok {
 | |
| 		// netsh uses exit(0) to indicate a success of the operation,
 | |
| 		// as compared to a malformed commandline, for example.
 | |
| 		if ee.Exited() && ee.ExitStatus() == 0 {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 	return fmt.Errorf("error deleting ipv4 address: %v: %s", err, out)
 | |
| }
 | |
| 
 | |
| // GetInterfaceToAddIP returns the interface name where Service IP needs to be added
 | |
| // IP Address needs to be added for netsh portproxy to redirect traffic
 | |
| // Reads Environment variable INTERFACE_TO_ADD_SERVICE_IP, if it is not defined then "vEthernet (HNS Internal NIC)" is returned
 | |
| func (runner *runner) GetInterfaceToAddIP() string {
 | |
| 	if iface := os.Getenv("INTERFACE_TO_ADD_SERVICE_IP"); len(iface) > 0 {
 | |
| 		return iface
 | |
| 	}
 | |
| 	return "vEthernet (HNS Internal NIC)"
 | |
| }
 | |
| 
 | |
| // Restore is part of Interface.
 | |
| func (runner *runner) Restore(args []string) error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // checkIPExists checks if an IP address exists in 'netsh interface ipv4 show address' output
 | |
| func checkIPExists(ipToCheck string, args []string, runner *runner) (bool, error) {
 | |
| 	ipAddress, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	ipAddressString := string(ipAddress[:])
 | |
| 	klog.V(3).Infof("Searching for IP: %v in IP dump: %v", ipToCheck, ipAddressString)
 | |
| 	showAddressArray := strings.Split(ipAddressString, "\n")
 | |
| 	for _, showAddress := range showAddressArray {
 | |
| 		if strings.Contains(showAddress, "IP") {
 | |
| 			ipFromNetsh := getIP(showAddress)
 | |
| 			if ipFromNetsh == ipToCheck {
 | |
| 				return true, nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false, nil
 | |
| }
 | |
| 
 | |
| // getIP gets ip from showAddress (e.g. "IP Address: 10.96.0.4").
 | |
| func getIP(showAddress string) string {
 | |
| 	list := strings.SplitN(showAddress, ":", 2)
 | |
| 	if len(list) != 2 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return strings.TrimSpace(list[1])
 | |
| }
 |