Browse Source

pkg/netutil: get default interface for tc commands

Fix https://github.com/coreos/etcd/issues/6841.
Gyu-Ho Lee 9 years ago
parent
commit
097cdbd0e4
4 changed files with 108 additions and 22 deletions
  1. 13 4
      pkg/netutil/isolate_linux.go
  2. 5 0
      pkg/netutil/routes.go
  3. 63 18
      pkg/netutil/routes_linux.go
  4. 27 0
      pkg/netutil/routes_linux_test.go

+ 13 - 4
pkg/netutil/isolate_linux.go

@@ -43,14 +43,19 @@ func RecoverPort(port int) error {
 
 // SetLatency adds latency in millisecond scale with random variations.
 func SetLatency(ms, rv int) error {
+	ifce, err := GetDefaultInterface()
+	if err != nil {
+		return err
+	}
+
 	if rv > ms {
 		rv = 1
 	}
-	cmdStr := fmt.Sprintf("sudo tc qdisc add dev eth0 root netem delay %dms %dms distribution normal", ms, rv)
-	_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
+	cmdStr := fmt.Sprintf("sudo tc qdisc add dev %s root netem delay %dms %dms distribution normal", ifce, ms, rv)
+	_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
 	if err != nil {
 		// the rule has already been added. Overwrite it.
-		cmdStr = fmt.Sprintf("sudo tc qdisc change dev eth0 root netem delay %dms %dms distribution normal", ms, rv)
+		cmdStr = fmt.Sprintf("sudo tc qdisc change dev %s root netem delay %dms %dms distribution normal", ifce, ms, rv)
 		_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
 		if err != nil {
 			return err
@@ -61,6 +66,10 @@ func SetLatency(ms, rv int) error {
 
 // RemoveLatency resets latency configurations.
 func RemoveLatency() error {
-	_, err := exec.Command("/bin/sh", "-c", "sudo tc qdisc del dev eth0 root netem").Output()
+	ifce, err := GetDefaultInterface()
+	if err != nil {
+		return err
+	}
+	_, err = exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo tc qdisc del dev %s root netem", ifce)).Output()
 	return err
 }

+ 5 - 0
pkg/netutil/routes.go

@@ -26,3 +26,8 @@ import (
 func GetDefaultHost() (string, error) {
 	return "", fmt.Errorf("default host not supported on %s_%s", runtime.GOOS, runtime.GOARCH)
 }
+
+// GetDefaultInterface fetches the device name of default routable interface.
+func GetDefaultInterface() (string, error) {
+	return "", fmt.Errorf("default host not supported on %s_%s", runtime.GOOS, runtime.GOARCH)
+}

+ 63 - 18
pkg/netutil/routes_linux.go

@@ -35,33 +35,21 @@ func GetDefaultHost() (string, error) {
 		return "", rerr
 	}
 
-	attrs, aerr := syscall.ParseNetlinkRouteAttr(rmsg)
-	if aerr != nil {
-		return "", aerr
-	}
-
-	oif := uint32(0)
-	for _, attr := range attrs {
-		if attr.Attr.Type == syscall.RTA_PREFSRC {
-			return net.IP(attr.Value).String(), nil
-		}
-		if attr.Attr.Type == syscall.RTA_OIF {
-			oif = binary.LittleEndian.Uint32(attr.Value)
-		}
+	host, oif, err := parsePREFSRC(rmsg)
+	if err != nil {
+		return "", err
 	}
-
-	if oif == 0 {
-		return "", errNoDefaultRoute
+	if host != "" {
+		return host, nil
 	}
 
 	// prefsrc not detected, fall back to getting address from iface
-
 	ifmsg, ierr := getIface(oif)
 	if ierr != nil {
 		return "", ierr
 	}
 
-	attrs, aerr = syscall.ParseNetlinkRouteAttr(ifmsg)
+	attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
 	if aerr != nil {
 		return "", aerr
 	}
@@ -131,3 +119,60 @@ func getIface(idx uint32) (*syscall.NetlinkMessage, error) {
 
 	return nil, errNoDefaultRoute
 }
+
+var errNoDefaultInterface = fmt.Errorf("could not find default interface")
+
+func GetDefaultInterface() (string, error) {
+	rmsg, rerr := getDefaultRoute()
+	if rerr != nil {
+		return "", rerr
+	}
+
+	_, oif, err := parsePREFSRC(rmsg)
+	if err != nil {
+		return "", err
+	}
+
+	ifmsg, ierr := getIface(oif)
+	if ierr != nil {
+		return "", ierr
+	}
+
+	attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
+	if aerr != nil {
+		return "", aerr
+	}
+
+	for _, attr := range attrs {
+		if attr.Attr.Type == syscall.IFLA_IFNAME {
+			return string(attr.Value[:len(attr.Value)-1]), nil
+		}
+	}
+	return "", errNoDefaultInterface
+}
+
+// parsePREFSRC returns preferred source address and output interface index (RTA_OIF).
+func parsePREFSRC(m *syscall.NetlinkMessage) (host string, oif uint32, err error) {
+	var attrs []syscall.NetlinkRouteAttr
+	attrs, err = syscall.ParseNetlinkRouteAttr(m)
+	if err != nil {
+		return "", 0, err
+	}
+
+	for _, attr := range attrs {
+		if attr.Attr.Type == syscall.RTA_PREFSRC {
+			host = net.IP(attr.Value).String()
+		}
+		if attr.Attr.Type == syscall.RTA_OIF {
+			oif = binary.LittleEndian.Uint32(attr.Value)
+		}
+		if host != "" && oif != uint32(0) {
+			break
+		}
+	}
+
+	if oif == 0 {
+		err = errNoDefaultRoute
+	}
+	return
+}

+ 27 - 0
pkg/netutil/routes_linux_test.go

@@ -0,0 +1,27 @@
+// Copyright 2016 The etcd 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.
+
+// +build linux
+
+package netutil
+
+import "testing"
+
+func TestGetDefaultInterface(t *testing.T) {
+	ifc, err := GetDefaultInterface()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Logf("default network interface: %q\n", ifc)
+}