support specify multiple interfaces

This commit is contained in:
Yuan Tong
2022-11-26 23:20:13 +08:00
committed by ginuerzh
parent c2a1dd2a89
commit 330efab056

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"strings"
"syscall" "syscall"
"time" "time"
@ -24,28 +25,10 @@ type NetDialer struct {
Timeout time.Duration Timeout time.Duration
DialFunc func(ctx context.Context, network, addr string) (net.Conn, error) DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
Logger logger.Logger Logger logger.Logger
deadline time.Time
} }
func (d *NetDialer) Dial(ctx context.Context, network, addr string) (net.Conn, error) { func (d *NetDialer) dialOnce(ctx context.Context, network, addr, ifceName string, ifAddr net.Addr, log logger.Logger) (net.Conn, error) {
if d == nil {
d = DefaultNetDialer
}
if d.Timeout <= 0 {
d.Timeout = DefaultTimeout
}
log := d.Logger
if log == nil {
log = logger.Default()
}
ifceName, ifAddr, err := parseInterfaceAddr(d.Interface, network)
if err != nil {
return nil, err
}
if d.DialFunc != nil {
return d.DialFunc(ctx, network, addr)
}
if ifceName != "" { if ifceName != "" {
log.Debugf("interface: %s %v/%s", ifceName, ifAddr, network) log.Debugf("interface: %s %v/%s", ifceName, ifAddr, network)
} }
@ -89,7 +72,7 @@ func (d *NetDialer) Dial(ctx context.Context, network, addr string) (net.Conn, e
return nil, fmt.Errorf("dial: unsupported network %s", network) return nil, fmt.Errorf("dial: unsupported network %s", network)
} }
netd := net.Dialer{ netd := net.Dialer{
Timeout: d.Timeout, Deadline: d.deadline,
LocalAddr: ifAddr, LocalAddr: ifAddr,
Control: func(network, address string, c syscall.RawConn) error { Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) { return c.Control(func(fd uintptr) {
@ -109,8 +92,76 @@ func (d *NetDialer) Dial(ctx context.Context, network, addr string) (net.Conn, e
return netd.DialContext(ctx, network, addr) return netd.DialContext(ctx, network, addr)
} }
func parseInterfaceAddr(ifceName, network string) (ifce string, addr net.Addr, err error) { func (d *NetDialer) Dial(ctx context.Context, network, addr string) (conn net.Conn, err error) {
if d == nil {
d = DefaultNetDialer
}
if d.Timeout <= 0 {
d.Timeout = DefaultTimeout
}
if d.DialFunc != nil {
return d.DialFunc(ctx, network, addr)
}
log := d.Logger
if log == nil {
log = logger.Default()
}
ifces := strings.Split(d.Interface, ",")
d.deadline = time.Now().Add(d.Timeout)
for _, ifce := range ifces {
strict := strings.HasSuffix(ifce, "!")
ifce = strings.TrimSuffix(ifce, "!")
var ifceName string
var ifAddrs []net.Addr
ifceName, ifAddrs, err = parseInterfaceAddr(ifce, network)
if err != nil && strict {
return
}
for _, ifAddr := range ifAddrs {
conn, err = d.dialOnce(ctx, network, addr, ifceName, ifAddr, log)
if err == nil {
return
}
log.Debugf("dial %s %v@%s failed: %s", network, ifAddr, ifceName, err)
if strict &&
!strings.Contains(err.Error(), "no suitable address found") &&
!strings.Contains(err.Error(), "mismatched local address type") {
return
}
if time.Until(d.deadline) < 0 {
return
}
}
}
return
}
func ipToAddr(ip net.IP, network string) (addr net.Addr) {
port := 0
switch network {
case "tcp", "tcp4", "tcp6":
addr = &net.TCPAddr{IP: ip, Port: port}
return
case "udp", "udp4", "udp6":
addr = &net.UDPAddr{IP: ip, Port: port}
return
default:
addr = &net.IPAddr{IP: ip}
return
}
}
func parseInterfaceAddr(ifceName, network string) (ifce string, addr []net.Addr, err error) {
if ifceName == "" { if ifceName == "" {
addr = append(addr, nil)
return return
} }
@ -130,27 +181,21 @@ func parseInterfaceAddr(ifceName, network string) (ifce string, addr net.Addr, e
err = fmt.Errorf("addr not found for interface %s", ifceName) err = fmt.Errorf("addr not found for interface %s", ifceName)
return return
} }
ip = addrs[0].(*net.IPNet).IP
ifce = ifceName ifce = ifceName
for _, addr_ := range addrs {
if ipNet, ok := addr_.(*net.IPNet); ok {
addr = append(addr, ipToAddr(ipNet.IP, network))
}
}
} else { } else {
ifce, err = findInterfaceByIP(ip) ifce, err = findInterfaceByIP(ip)
if err != nil { if err != nil {
return return
} }
addr = []net.Addr{ipToAddr(ip, network)}
} }
port := 0 return
switch network {
case "tcp", "tcp4", "tcp6":
addr = &net.TCPAddr{IP: ip, Port: port}
return
case "udp", "udp4", "udp6":
addr = &net.UDPAddr{IP: ip, Port: port}
return
default:
addr = &net.IPAddr{IP: ip}
return
}
} }
func findInterfaceByIP(ip net.IP) (string, error) { func findInterfaceByIP(ip net.IP) (string, error) {