Skip to content

Commit

Permalink
Merge pull request kubernetes#4865 from sub-mod/proxier_go_change
Browse files Browse the repository at this point in the history
use ChooseHostInterface in kube-proxy
  • Loading branch information
thockin committed Mar 11, 2015
2 parents 6d465c4 + b8c91e7 commit 52298ab
Show file tree
Hide file tree
Showing 3 changed files with 450 additions and 44 deletions.
46 changes: 2 additions & 44 deletions pkg/proxy/proxier.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,12 @@ func NewProxier(loadBalancer LoadBalancer, listenIP net.IP, iptables iptables.In
return nil
}

hostIP, err := chooseHostInterface()
hostIP, err := util.ChooseHostInterface()
if err != nil {
glog.Errorf("Failed to select a host interface: %v", err)
return nil
}

glog.Infof("Setting Proxy IP to %v", hostIP)
glog.Infof("Initializing iptables")
// Clean up old messes. Ignore erors.
iptablesDeleteOld(iptables)
Expand Down Expand Up @@ -761,45 +761,3 @@ func (proxier *Proxier) iptablesHostPortalArgs(destIP net.IP, destPort int, prot
args = append(args, "-j", "DNAT", "--to-destination", net.JoinHostPort(proxyIP.String(), strconv.Itoa(proxyPort)))
return args
}

func chooseHostInterface() (net.IP, error) {
intfs, err := net.Interfaces()
if err != nil {
return nil, err
}
i := 0
for i = range intfs {
if flagsSet(intfs[i].Flags, net.FlagUp) && flagsClear(intfs[i].Flags, net.FlagLoopback|net.FlagPointToPoint) {
addrs, err := intfs[i].Addrs()
if err != nil {
return nil, err
}
if len(addrs) > 0 {
// This interface should suffice.
break
}
}
}
if i == len(intfs) {
return nil, err
}
glog.V(2).Infof("Choosing interface %s for from-host portals", intfs[i].Name)
addrs, err := intfs[i].Addrs()
if err != nil {
return nil, err
}
glog.V(2).Infof("Interface %s = %s", intfs[i].Name, addrs[0].String())
ip, _, err := net.ParseCIDR(addrs[0].String())
if err != nil {
return nil, err
}
return ip, nil
}

func flagsSet(flags net.Flags, test net.Flags) bool {
return flags&test != 0
}

func flagsClear(flags net.Flags, test net.Flags) bool {
return flags&test == 0
}
190 changes: 190 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ limitations under the License.
package util

import (
"bufio"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path"
"reflect"
"regexp"
Expand Down Expand Up @@ -252,3 +257,188 @@ func SplitQualifiedName(str string) (string, string) {
func JoinQualifiedName(namespace, name string) string {
return path.Join(namespace, name)
}

type Route struct {
Interface string
Destination net.IP
Gateway net.IP
// TODO: add more fields here if needed
}

func getRoutes(input io.Reader) ([]Route, error) {
routes := []Route{}
if input == nil {
return nil, fmt.Errorf("input is nil")
}
scanner := bufio.NewReader(input)
for {
line, err := scanner.ReadString('\n')
if err == io.EOF {
break
}
//ignore the headers in the route info
if strings.HasPrefix(line, "Iface") {
continue
}
fields := strings.Fields(line)
routes = append(routes, Route{})
route := &routes[len(routes)-1]
route.Interface = fields[0]
ip, err := parseIP(fields[1])
if err != nil {
return nil, err
}
route.Destination = ip
ip, err = parseIP(fields[2])
if err != nil {
return nil, err
}
route.Gateway = ip
}
return routes, nil
}

func parseIP(str string) (net.IP, error) {
if str == "" {
return nil, fmt.Errorf("input is nil")
}
bytes, err := hex.DecodeString(str)
if err != nil {
return nil, err
}
//TODO add ipv6 support
if len(bytes) != net.IPv4len {
return nil, fmt.Errorf("only IPv4 is supported")
}
bytes[0], bytes[1], bytes[2], bytes[3] = bytes[3], bytes[2], bytes[1], bytes[0]
return net.IP(bytes), nil
}

func isInterfaceUp(intf *net.Interface) bool {
if intf == nil {
return false
}
if intf.Flags&net.FlagUp != 0 {
glog.V(4).Infof("Interface %v is up", intf.Name)
return true
}
return false
}

//getFinalIP method receives all the IP addrs of a Interface
//and returns a nil if the address is Loopback , Ipv6 or nil.
//It returns a valid IPv4 if an Ipv4 address is found in the array.
func getFinalIP(addrs []net.Addr) (net.IP, error) {
if len(addrs) > 0 {
for i := range addrs {
glog.V(4).Infof("Checking addr %s.", addrs[i].String())
ip, _, err := net.ParseCIDR(addrs[i].String())
if err != nil {
return nil, err
}
//Only IPv4
//TODO : add IPv6 support
if ip.To4() != nil {
if !ip.IsLoopback() {
glog.V(4).Infof("IP found %v", ip)
return ip, nil
} else {
glog.V(4).Infof("Loopback found %v", ip)
}
} else {
glog.V(4).Infof("%v is not a valid IPv4 address", ip)
}

}
}
return nil, nil
}

func getIPFromInterface(intfName string, nw networkInterfacer) (net.IP, error) {
intf, err := nw.InterfaceByName(intfName)
if err != nil {
return nil, err
}
if isInterfaceUp(intf) {
addrs, err := nw.Addrs(intf)
if err != nil {
return nil, err
}
glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
finalIP, err := getFinalIP(addrs)
if err != nil {
return nil, err
}
if finalIP != nil {
glog.V(4).Infof("valid IPv4 address for interface %q found as %v.", intfName, finalIP)
return finalIP, nil
}
}

return nil, nil
}

//ChooseHostInterface is a method used fetch an IP for a daemon.
//It uses data from /proc/net/route file.
//For a node with no internet connection ,it returns error
//For a multi n/w interface node it returns the IP of the interface with gateway on it.
func ChooseHostInterface() (net.IP, error) {
inFile, err := os.Open("/proc/net/route")
if err != nil {
return nil, err
}
defer inFile.Close()
var nw networkInterfacer = networkInterface{}
return chooseHostInterfaceFromRoute(inFile, nw)
}

type networkInterfacer interface {
InterfaceByName(intfName string) (*net.Interface, error)
Addrs(intf *net.Interface) ([]net.Addr, error)
}

type networkInterface struct{}

func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
intf, err := net.InterfaceByName(intfName)
if err != nil {
return nil, err
}
return intf, nil
}

func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
addrs, err := intf.Addrs()
if err != nil {
return nil, err
}
return addrs, nil
}

func chooseHostInterfaceFromRoute(inFile io.Reader, nw networkInterfacer) (net.IP, error) {
routes, err := getRoutes(inFile)
if err != nil {
return nil, err
}
zero := net.IP{0, 0, 0, 0}
var finalIP net.IP
for i := range routes {
//find interface with gateway
if routes[i].Destination.Equal(zero) {
glog.V(4).Infof("Default route transits interface %q", routes[i].Interface)
finalIP, err := getIPFromInterface(routes[i].Interface, nw)
if err != nil {
return nil, err
}
if finalIP != nil {
glog.V(4).Infof("Choosing IP %v ", finalIP)
return finalIP, nil
}
}
}
glog.V(4).Infof("No valid IP found")
if finalIP == nil {
return nil, fmt.Errorf("Unable to select an IP.")
}
return nil, nil
}
Loading

0 comments on commit 52298ab

Please sign in to comment.