netutil: add dualstack to linux_route
in v3.1.0 netutil couldn't get default interface for ipv6only hosts Fixes #7219
This commit is contained in:
@ -68,8 +68,8 @@ func init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// found default host, advertise on it
|
// found default host, advertise on it
|
||||||
DefaultInitialAdvertisePeerURLs = "http://" + ip + ":2380"
|
DefaultInitialAdvertisePeerURLs = "http://" + net.JoinHostPort(ip, "2380")
|
||||||
DefaultAdvertiseClientURLs = "http://" + ip + ":2379"
|
DefaultAdvertiseClientURLs = "http://" + net.JoinHostPort(ip, "2379")
|
||||||
defaultHostname = ip
|
defaultHostname = ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func RecoverPort(port int) error {
|
|||||||
|
|
||||||
// SetLatency adds latency in millisecond scale with random variations.
|
// SetLatency adds latency in millisecond scale with random variations.
|
||||||
func SetLatency(ms, rv int) error {
|
func SetLatency(ms, rv int) error {
|
||||||
ifce, err := GetDefaultInterface()
|
ifces, err := GetDefaultInterfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -51,6 +51,7 @@ func SetLatency(ms, rv int) error {
|
|||||||
if rv > ms {
|
if rv > ms {
|
||||||
rv = 1
|
rv = 1
|
||||||
}
|
}
|
||||||
|
for ifce := range ifces {
|
||||||
cmdStr := fmt.Sprintf("sudo tc qdisc add dev %s root netem delay %dms %dms distribution normal", ifce, ms, rv)
|
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()
|
_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -61,15 +62,21 @@ func SetLatency(ms, rv int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveLatency resets latency configurations.
|
// RemoveLatency resets latency configurations.
|
||||||
func RemoveLatency() error {
|
func RemoveLatency() error {
|
||||||
ifce, err := GetDefaultInterface()
|
ifces, err := GetDefaultInterfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for ifce := range ifces {
|
||||||
_, err = exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo tc qdisc del dev %s root netem", ifce)).Output()
|
_, err = exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo tc qdisc del dev %s root netem", ifce)).Output()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -27,13 +27,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var errNoDefaultRoute = fmt.Errorf("could not find default route")
|
var errNoDefaultRoute = fmt.Errorf("could not find default route")
|
||||||
|
var errNoDefaultHost = fmt.Errorf("could not find default host")
|
||||||
|
var errNoDefaultInterface = fmt.Errorf("could not find default interface")
|
||||||
|
|
||||||
|
// GetDefaultHost obtains the first IP address of machine from the routing table and returns the IP address as string.
|
||||||
|
// An IPv4 address is preferred to an IPv6 address for backward compatibility.
|
||||||
func GetDefaultHost() (string, error) {
|
func GetDefaultHost() (string, error) {
|
||||||
rmsg, rerr := getDefaultRoute()
|
rmsgs, rerr := getDefaultRoutes()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
return "", rerr
|
return "", rerr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for family, rmsg := range rmsgs {
|
||||||
host, oif, err := parsePREFSRC(rmsg)
|
host, oif, err := parsePREFSRC(rmsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -43,7 +48,7 @@ func GetDefaultHost() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prefsrc not detected, fall back to getting address from iface
|
// prefsrc not detected, fall back to getting address from iface
|
||||||
ifmsg, ierr := getIface(oif)
|
ifmsg, ierr := getIfaceAddr(oif, family)
|
||||||
if ierr != nil {
|
if ierr != nil {
|
||||||
return "", ierr
|
return "", ierr
|
||||||
}
|
}
|
||||||
@ -54,15 +59,17 @@ func GetDefaultHost() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
if attr.Attr.Type == syscall.RTA_SRC {
|
// search for RTA_DST because ipv6 doesn't have RTA_SRC
|
||||||
|
if attr.Attr.Type == syscall.RTA_DST {
|
||||||
return net.IP(attr.Value).String(), nil
|
return net.IP(attr.Value).String(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errNoDefaultRoute
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultRoute() (*syscall.NetlinkMessage, error) {
|
return "", errNoDefaultHost
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultRoutes() (map[uint8]*syscall.NetlinkMessage, error) {
|
||||||
dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
|
dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -73,6 +80,7 @@ func getDefaultRoute() (*syscall.NetlinkMessage, error) {
|
|||||||
return nil, msgErr
|
return nil, msgErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
routes := make(map[uint8]*syscall.NetlinkMessage)
|
||||||
rtmsg := syscall.RtMsg{}
|
rtmsg := syscall.RtMsg{}
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
if m.Header.Type != syscall.RTM_NEWROUTE {
|
if m.Header.Type != syscall.RTM_NEWROUTE {
|
||||||
@ -82,17 +90,23 @@ func getDefaultRoute() (*syscall.NetlinkMessage, error) {
|
|||||||
if rerr := binary.Read(buf, cpuutil.ByteOrder(), &rtmsg); rerr != nil {
|
if rerr := binary.Read(buf, cpuutil.ByteOrder(), &rtmsg); rerr != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rtmsg.Dst_len == 0 {
|
if rtmsg.Dst_len == 0 && rtmsg.Table == syscall.RT_TABLE_MAIN {
|
||||||
// zero-length Dst_len implies default route
|
// zero-length Dst_len implies default route
|
||||||
return &m, nil
|
msg := m
|
||||||
|
routes[rtmsg.Family] = &msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(routes) > 0 {
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, errNoDefaultRoute
|
return nil, errNoDefaultRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIface(idx uint32) (*syscall.NetlinkMessage, error) {
|
// Used to get an address of interface.
|
||||||
dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
|
func getIfaceAddr(idx uint32, family uint8) (*syscall.NetlinkMessage, error) {
|
||||||
|
dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, int(family))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -116,38 +130,75 @@ func getIface(idx uint32) (*syscall.NetlinkMessage, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errNoDefaultRoute
|
return nil, fmt.Errorf("could not find address for interface index %v", idx)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errNoDefaultInterface = fmt.Errorf("could not find default interface")
|
// Used to get a name of interface.
|
||||||
|
func getIfaceLink(idx uint32) (*syscall.NetlinkMessage, error) {
|
||||||
|
dat, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
func GetDefaultInterface() (string, error) {
|
msgs, msgErr := syscall.ParseNetlinkMessage(dat)
|
||||||
rmsg, rerr := getDefaultRoute()
|
if msgErr != nil {
|
||||||
|
return nil, msgErr
|
||||||
|
}
|
||||||
|
|
||||||
|
ifinfomsg := syscall.IfInfomsg{}
|
||||||
|
for _, m := range msgs {
|
||||||
|
if m.Header.Type != syscall.RTM_NEWLINK {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfInfomsg])
|
||||||
|
if rerr := binary.Read(buf, cpuutil.ByteOrder(), &ifinfomsg); rerr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ifinfomsg.Index == int32(idx) {
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("could not find link for interface index %v", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaces gets names of interfaces and returns a map[interface]families.
|
||||||
|
func GetDefaultInterfaces() (map[string]uint8, error) {
|
||||||
|
interfaces := make(map[string]uint8)
|
||||||
|
rmsgs, rerr := getDefaultRoutes()
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
return "", rerr
|
return interfaces, rerr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for family, rmsg := range rmsgs {
|
||||||
_, oif, err := parsePREFSRC(rmsg)
|
_, oif, err := parsePREFSRC(rmsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return interfaces, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ifmsg, ierr := getIface(oif)
|
ifmsg, ierr := getIfaceLink(oif)
|
||||||
if ierr != nil {
|
if ierr != nil {
|
||||||
return "", ierr
|
return interfaces, ierr
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
|
attrs, aerr := syscall.ParseNetlinkRouteAttr(ifmsg)
|
||||||
if aerr != nil {
|
if aerr != nil {
|
||||||
return "", aerr
|
return interfaces, aerr
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
if attr.Attr.Type == syscall.IFLA_IFNAME {
|
if attr.Attr.Type == syscall.IFLA_IFNAME {
|
||||||
return string(attr.Value[:len(attr.Value)-1]), nil
|
// key is an interface name
|
||||||
|
// possible values: 2 - AF_INET, 10 - AF_INET6, 12 - dualstack
|
||||||
|
interfaces[string(attr.Value[:len(attr.Value)-1])] += family
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", errNoDefaultInterface
|
}
|
||||||
|
if len(interfaces) > 0 {
|
||||||
|
return interfaces, nil
|
||||||
|
}
|
||||||
|
return interfaces, errNoDefaultInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePREFSRC returns preferred source address and output interface index (RTA_OIF).
|
// parsePREFSRC returns preferred source address and output interface index (RTA_OIF).
|
||||||
|
@ -19,9 +19,17 @@ package netutil
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestGetDefaultInterface(t *testing.T) {
|
func TestGetDefaultInterface(t *testing.T) {
|
||||||
ifc, err := GetDefaultInterface()
|
ifc, err := GetDefaultInterfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("default network interface: %q\n", ifc)
|
t.Logf("default network interfaces: %+v\n", ifc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultHost(t *testing.T) {
|
||||||
|
ip, err := GetDefaultHost()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("default ip: %v", ip)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user