From 097cdbd0e42055210fdadaa4d84858b3577ce409 Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Thu, 17 Nov 2016 00:05:40 -0800 Subject: [PATCH] pkg/netutil: get default interface for tc commands Fix https://github.com/coreos/etcd/issues/6841. --- pkg/netutil/isolate_linux.go | 17 +++++-- pkg/netutil/routes.go | 5 ++ pkg/netutil/routes_linux.go | 81 +++++++++++++++++++++++++------- pkg/netutil/routes_linux_test.go | 27 +++++++++++ 4 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 pkg/netutil/routes_linux_test.go diff --git a/pkg/netutil/isolate_linux.go b/pkg/netutil/isolate_linux.go index c866cea8e..c0128a5a5 100644 --- a/pkg/netutil/isolate_linux.go +++ b/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 } diff --git a/pkg/netutil/routes.go b/pkg/netutil/routes.go index 38642c608..d6979c130 100644 --- a/pkg/netutil/routes.go +++ b/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) +} diff --git a/pkg/netutil/routes_linux.go b/pkg/netutil/routes_linux.go index 39040ab02..f8718ae6a 100644 --- a/pkg/netutil/routes_linux.go +++ b/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 + host, oif, err := parsePREFSRC(rmsg) + if err != nil { + return "", err } - - 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) - } - } - - 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 +} diff --git a/pkg/netutil/routes_linux_test.go b/pkg/netutil/routes_linux_test.go new file mode 100644 index 000000000..4252178ad --- /dev/null +++ b/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) +}