feat: allow tcp_check_url and udp_check_dns are not double stack

This commit is contained in:
mzz2017 2023-02-13 02:40:34 +08:00
parent affb49d85b
commit 0b5263c89b
5 changed files with 66 additions and 29 deletions

View File

@ -7,7 +7,6 @@ package netutils
import (
"context"
"fmt"
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/proxy"
"net/netip"
@ -18,28 +17,20 @@ type Ip46 struct {
Ip6 netip.Addr
}
func ParseIp46(ctx context.Context, dialer proxy.Dialer, dns netip.AddrPort, host string, must46 bool, tcp bool) (ipv46 *Ip46, err error) {
func ParseIp46(ctx context.Context, dialer proxy.Dialer, dns netip.AddrPort, host string, tcp bool) (ipv46 *Ip46, err error) {
addrs4, err := ResolveNetip(ctx, dialer, dns, host, dnsmessage.TypeA, tcp)
if err != nil {
return nil, err
}
if len(addrs4) == 0 && must46 {
if must46 {
return nil, fmt.Errorf("domain \"%v\" has no ipv4 record", host)
} else {
addrs4 = []netip.Addr{{}}
}
if len(addrs4) == 0 {
addrs4 = []netip.Addr{{}}
}
addrs6, err := ResolveNetip(ctx, dialer, dns, host, dnsmessage.TypeAAAA, tcp)
if err != nil {
return nil, err
}
if len(addrs6) == 0 {
if must46 {
return nil, fmt.Errorf("domain \"%v\" has no ipv6 record", host)
} else {
addrs6 = []netip.Addr{{}}
}
addrs6 = []netip.Addr{{}}
}
return &Ip46{
Ip4: addrs4[0],

View File

@ -115,7 +115,7 @@ func ParseTcpCheckOption(ctx context.Context, rawURL string) (opt *TcpCheckOptio
if err != nil {
return nil, err
}
ip46, err := netutils.ParseIp46(ctx, SymmetricDirect, systemDns, u.Hostname(), true, false)
ip46, err := netutils.ParseIp46(ctx, SymmetricDirect, systemDns, u.Hostname(), false)
if err != nil {
return nil, err
}
@ -150,7 +150,7 @@ func ParseCheckDnsOption(ctx context.Context, dnsHostPort string) (opt *CheckDns
if err != nil {
return nil, fmt.Errorf("bad port: %v", err)
}
ip46, err := netutils.ParseIp46(ctx, SymmetricDirect, systemDns, host, true, false)
ip46, err := netutils.ParseIp46(ctx, SymmetricDirect, systemDns, host, false)
if err != nil {
return nil, err
}
@ -205,7 +205,7 @@ func (c *CheckDnsOptionRaw) Option() (opt *CheckDnsOption, err error) {
type CheckOption struct {
networkType *NetworkType
CheckFunc func(ctx context.Context) (ok bool, err error)
CheckFunc func(ctx context.Context, typ *NetworkType) (ok bool, err error)
}
func (d *Dialer) ActivateCheck() {
@ -227,11 +227,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_4,
IsDns: false,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.TcpCheckOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip4.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.TcpCheckOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.HttpCheck(ctx, opt.Url, opt.Ip4)
},
}
@ -241,11 +248,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_6,
IsDns: false,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.TcpCheckOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip6.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.TcpCheckOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.HttpCheck(ctx, opt.Url, opt.Ip6)
},
}
@ -255,11 +269,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_4,
IsDns: true,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.CheckDnsOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip4.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.CheckDnsOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), true)
},
}
@ -269,11 +290,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_6,
IsDns: true,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.CheckDnsOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip6.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.CheckDnsOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), true)
},
}
@ -283,11 +311,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_4,
IsDns: true,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.CheckDnsOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip4.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.CheckDnsOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip4, opt.DnsPort), false)
},
}
@ -297,11 +332,18 @@ func (d *Dialer) aliveBackground() {
IpVersion: consts.IpVersionStr_6,
IsDns: true,
},
CheckFunc: func(ctx context.Context) (ok bool, err error) {
CheckFunc: func(ctx context.Context, typ *NetworkType) (ok bool, err error) {
opt, err := d.CheckDnsOptionRaw.Option()
if err != nil {
return false, err
}
if !opt.Ip6.IsValid() {
d.Log.WithFields(logrus.Fields{
"link": d.CheckDnsOptionRaw.Raw,
"network": typ.String(),
}).Debugln("Skip check due to no record.")
return false, nil
}
return d.DnsCheck(ctx, netip.AddrPortFrom(opt.Ip6, opt.DnsPort), false)
},
}
@ -401,7 +443,7 @@ func (d *Dialer) Check(timeout time.Duration,
start := time.Now()
// Calc latency.
collection := d.mustGetCollection(opts.networkType)
if ok, err = opts.CheckFunc(ctx); ok && err == nil {
if ok, err = opts.CheckFunc(ctx, opts.networkType); ok && err == nil {
// No error.
latency := time.Since(start)
latencies10 := d.mustGetCollection(opts.networkType).Latencies10

View File

@ -79,7 +79,7 @@ func ResolveDnsUpstream(ctx context.Context, dnsUpstream *url.URL) (up *DnsUpstr
}
}()
ip46, err := netutils.ParseIp46(ctx, dialer.SymmetricDirect, systemDns, hostname, false, false)
ip46, err := netutils.ParseIp46(ctx, dialer.SymmetricDirect, systemDns, hostname, false)
if err != nil {
return nil, fmt.Errorf("failed to resolve dns_upstream: %w", err)
}

View File

@ -1195,7 +1195,6 @@ int tproxy_lan_egress(struct __sk_buff *skb) {
if (*tproxy_port != tuples.src.port) {
return TC_ACT_PIPE;
}
bpf_printk("SAME");
struct ip_port_outbound ori_src;
if ((ret = decap_after_udp_hdr(skb, ipversion, ihl,
@ -1969,8 +1968,12 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) {
bpf_printk("zero cookie");
return -EINVAL;
}
int ret;
if (bpf_map_lookup_elem(&cookie_pid_map, &cookie)) {
// Cookie to pid mapping already exists.
return 0;
}
int ret;
// Build value.
struct pid_pname val;
__builtin_memset(&val, 0, sizeof(struct pid_pname));

View File

@ -10,9 +10,9 @@ global {
# Host of URL should have both IPv4 and IPv6 if you have double stack in local.
tcp_check_url: 'http://cp.cloudflare.com'
# This DNS will be used to check UDP connectivity. And if dns_upstream below contains tcp, it also be used to check
# This DNS will be used to check UDP connectivity of nodes. And if dns_upstream below contains tcp, it also be used to check
# TCP DNS connectivity of nodes.
# Host of DNS should have both IPv4 and IPv6 if you have double stack in local.
# This DNS should have both IPv4 and IPv6 if you have double stack in local.
udp_check_dns: 'dns.google:53'
check_interval: 30s
@ -93,7 +93,8 @@ group {
# See routing.md for full examples.
routing {
ip(geoip:private, 224.0.0.0/3, 'ff00::/8') -> direct # Put it first unless you know what you're doing.
pname(dnsmasq, systemd-resolved) -> must_direct # Traffic of DNS in local must be direct to avoid loop when binding to WAN.
ip(geoip:private, 224.0.0.0/3, 'ff00::/8') -> direct # Put it in front unless you know what you're doing.
# Write your rules below.
# dae arms DNS rush-answer filter so we can use dns.google regardless of DNS pollution.