Skip to content

Commit e0dfdaf

Browse files
committed
natpt,dnsx,tcp,udp: 4 via 6
1 parent 7ece230 commit e0dfdaf

10 files changed

Lines changed: 84 additions & 23 deletions

File tree

intra/backend/netstat.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@ type RDNSInfo struct {
155155
NewWireGuard string
156156
Transparency bool
157157
HappyEyeballs bool
158-
PanicTest bool
159-
FatalTest bool
160158
SystemDNSForUndelegated bool
161159
DefaultDNSAsFallback bool
162160
SetUserAgent bool

intra/common.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,8 +700,17 @@ func (h *baseHandler) undoAlg(algip netip.Addr, uid string) (undidAlg bool, real
700700
func filterFamilyForDialingWithFailSafe(ipcsv string) (included []netip.Addr, excluded []netip.Addr, excludedIsIncluded bool) {
701701
included, excluded, excludedIsIncluded = filterFamilyForDialing(ipcsv)
702702
if !excludedIsIncluded && len(excluded) > 0 && len(included) > 0 {
703-
// if not falling back, then include one excluded ip as a fail-safe
704-
included = append(included, core.ChooseOne(excluded))
703+
ptmode := settings.PtMode.Load()
704+
if ptmode == settings.PtModeForce || ptmode == settings.PtModeForce46 {
705+
// when PtMode forces protocol translation, include up to len(included)
706+
// excluded IPs so that both families are available for Happy Eyeballs
707+
n := min(len(included), len(excluded))
708+
included = append(included, excluded[:n]...)
709+
excluded = nil
710+
} else {
711+
// if not falling back, then include one excluded ip as a fail-safe
712+
included = append(included, core.ChooseOne(excluded))
713+
}
705714
}
706715
return included, excluded, excludedIsIncluded
707716
}

intra/dnsx/transport.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ func (r *resolver) Add(dt x.DNSTransport) (ok bool) {
367367
caching = true
368368
}
369369
r.Unlock()
370-
core.Go("r.onAdd", func() { r.listener.OnDNSAdded(tid) })
370+
core.Go("r.onAdd."+tid, func() { r.listener.OnDNSAdded(tid) })
371371
ok = true
372372
case DNS53, DOH, DOT, ODOH:
373373
r.Lock()
@@ -391,10 +391,10 @@ func (r *resolver) Add(dt x.DNSTransport) (ok bool) {
391391
if tid == System || tid == Goos {
392392
// TODO: add other resolvers?
393393
// always add64 after having added the system transport
394-
core.Gx("r.Add64", func() { r.Add64(tid) })
394+
core.Gx("r.Add64."+tid, func() { r.Add64(tid) })
395395
}
396396

397-
core.Go("r.onAdd", func() { r.listener.OnDNSAdded(tid) })
397+
core.Go("r.onAdd."+tid, func() { r.listener.OnDNSAdded(tid) })
398398
ok = true
399399
default:
400400
log.E("dns: unknown transport(%s) type: %s", t.ID(), t.Type())
@@ -478,7 +478,7 @@ func (r *resolver) Remove(tid string) (ok bool) {
478478

479479
if hasTransport {
480480
if id == System || id == Goos {
481-
core.Gx("r.Remove64", func() { r.Remove64(id) })
481+
core.Gx("r.Remove64."+tid, func() { r.Remove64(id) })
482482
}
483483
r.Lock()
484484
r.stopIfExistsLocked(id)
@@ -499,7 +499,7 @@ func (r *resolver) Remove(tid string) (ok bool) {
499499
}
500500

501501
if hasTransport {
502-
core.Go("r.onRemove", func() { r.listener.OnDNSRemoved(id) })
502+
core.Go("r.onRemove."+tid, func() { r.listener.OnDNSRemoved(id) })
503503
}
504504

505505
return hasTransport
@@ -616,7 +616,34 @@ func (r *resolver) forward(q []byte, who, fid, uid string, chosenids ...string)
616616
return nil, NoDNS, errMissingQueryName
617617
}
618618

619-
pref, oqcompleted := core.Grx("r.onQuery", func(_ context.Context) (*x.DNSOpts, error) {
619+
// when PtMode forces protocol translation, resolve AAAA in parallel
620+
// so that dns64/nat64 caches are warm for subsequent translations.
621+
pt := settings.PtMode.Load()
622+
if who == OriginTunnel && xdns.IsAQType(uint16(qtyp)) && ptmodeIsForce(pt) {
623+
msg6 := xdns.Request6FromRequest4(msg)
624+
smm6 := copySummary(ogsmm)
625+
smm6.QType = int(dns.TypeAAAA)
626+
fid6 := core.Rand64()
627+
628+
if log.Verbose {
629+
log.V("dns: fwd: for %s; force6 for %s:%s:%d; ptmode=%s", uid, fid6, qname, qtyp, pt)
630+
}
631+
632+
core.Gx("r.fwd.aaaa."+fid6+"."+qname, func() {
633+
_, _, _ = r.forwardInner(msg6, smm6, who, fid6, uid, chosenids...)
634+
})
635+
}
636+
637+
return r.forwardInner(msg, ogsmm, who, fid, uid, chosenids...)
638+
}
639+
640+
func (r *resolver) forwardInner(msg *dns.Msg, ogsmm *x.DNSSummary, who, fid, uid string, chosenids ...string) ([]byte, string, error) {
641+
starttime := time.Now()
642+
643+
qname := ogsmm.QName
644+
qtyp := ogsmm.QType
645+
646+
pref, oqcompleted := core.Grx("r.onQuery."+fid+"."+qname, func(_ context.Context) (*x.DNSOpts, error) {
620647
return r.listener.OnQuery(who, uid, qname, qtyp), nil
621648
}, listenerTimeout)
622649
if !oqcompleted || pref == nil {
@@ -799,7 +826,7 @@ runagain:
799826
}
800827

801828
if run == 1 {
802-
pref2, ouacompleted := core.Grx("r.onUA."+qname, func(_ context.Context) (*x.DNSOpts, error) {
829+
pref2, ouacompleted := core.Grx("r.onUA."+fid+"."+qname, func(_ context.Context) (*x.DNSOpts, error) {
803830
return r.listener.OnUpstreamAnswer(who, smm, pref.Copy(), realips), nil
804831
}, answerTimeout)
805832
if !ouacompleted {
@@ -1086,7 +1113,7 @@ func (r *resolver) accept(c io.ReadWriteCloser, uid, fid string) (rx, tx int64,
10861113
frees := free
10871114
cnts := cnt
10881115
wg.Add(1)
1089-
core.Gx("r.accept.do", func() {
1116+
core.Gx("r.accept.do."+fid, func() {
10901117
defer wg.Done()
10911118
defer frees()
10921119
m, err := r.dnstcp(qs, c, uid, fid)
@@ -1917,3 +1944,7 @@ func GetIPCsv(t Transport) string {
19171944
}
19181945
return sb.String()
19191946
}
1947+
1948+
func ptmodeIsForce(pt int32) bool {
1949+
return pt == settings.PtModeForce || pt == settings.PtModeForce46 || pt == settings.PtModeForce64
1950+
}

intra/ipn/proxies.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,8 +1379,13 @@ func accStats(a, b *x.RouterStats) (c *x.RouterStats) {
13791379
c.LastGoodRx = max(a.LastGoodRx, b.LastGoodRx)
13801380
c.LastGoodTx = max(a.LastGoodTx, b.LastGoodTx)
13811381
c.LastRefresh = max(a.LastRefresh, b.LastRefresh)
1382-
// todo: a.Since or b.Since may be zero
1383-
c.Since = min(a.Since, b.Since)
1382+
if a.Since > 0 && b.Since > 0 {
1383+
c.Since = min(a.Since, b.Since)
1384+
} else if a.Since > 0 {
1385+
c.Since = a.Since
1386+
} else {
1387+
c.Since = b.Since
1388+
}
13841389
c.Status = strings.Join([]string{a.Status, b.Status}, ";")
13851390
c.StatusReason = strings.Join([]string{a.StatusReason, b.StatusReason}, ";")
13861391
return c

intra/ipn/wgproxy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,9 +1293,9 @@ func (w *wgproxy) Stat() (out *x.RouterStats) {
12931293
out.LastOK = stat.LatestRecentHandshake()
12941294
}
12951295

1296-
if settings.Debug {
1297-
out.Extra = w.remote.Load().String() + "\n" + w.dns.Load().String() + "\nallowed:" + fmt.Sprintf("%v", *w.allowed.Load())
1296+
out.Extra = w.remote.Load().String() + "\n" + w.dns.Load().String() + "\nallowed:" + fmt.Sprintf("%v", *w.allowed.Load())
12981297

1298+
if log.Verbose {
12991299
log.VV("proxy: wg: %s stats: rx: %d, tx: %d, r: %s (rlastok: %s), w: %s (wlastok: %s), lastok: %s",
13001300
w.tag(), out.Rx, out.Tx,
13011301
core.FmtUnixMillisAsPeriod(out.LastRx), core.FmtUnixMillisAsPeriod(out.LastGoodRx),

intra/settings/tunopts.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const (
4444
PtModeForce64 int32 = 1
4545
// Android implements 464Xlat out-of-the-box, so this zero userspace impl
4646
PtModeNo46 int32 = 2
47+
// PtModeForce46 enforces 4 via 6 protocol translation.
48+
PtModeForce46 int32 = 3
49+
// PtModeForce enforces both PtModeForce64 or PtModeForce46.
50+
PtModeForce int32 = 4
4751
)
4852

4953
// Converts a given DNS/Block/Pt mode to its string representation.
@@ -77,6 +81,10 @@ func Mode2String(typ string, mode int32) string {
7781
return "auto"
7882
case PtModeForce64:
7983
return "force64"
84+
case PtModeForce46:
85+
return "force46"
86+
case PtModeForce:
87+
return "force"
8088
case PtModeNo46:
8189
return "no46"
8290
}
@@ -93,7 +101,7 @@ var DNSMode atomic.Int32
93101
var BlockMode atomic.Int32
94102

95103
// PtMode determines 6to4 translation heuristics.
96-
var PtMode = core.NewForeverFlow[int32](PtModeAuto)
104+
var PtMode = core.NewForeverFlow(PtModeAuto)
97105

98106
// SetMode re-assigns d to DNSMode, b to BlockMode, pt to PtMode.
99107
func SetTunMode(d, b, pt int32) {

intra/udp.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (h *udpHandler) Connect(gconn *netstack.GUDPConn, src, target netip.AddrPor
218218
// doesn't exist outside of firestack's tunnel).
219219
targetIsLocalNat64 := h.resolver.IsNat64(dnsx.Local464Resolver, target.Addr())
220220

221-
filtered, _, fallingback := filterFamilyForDialingWithFailSafe(realips)
221+
filtered, excluded, fallingback := filterFamilyForDialingWithFailSafe(realips)
222222
actualTargets := makeIPPorts(filtered, target, !undidAlg && !targetIsLocalNat64, 0)
223223
cid, uid, fid, pids := h.judge(res, domains, target.String())
224224

@@ -304,8 +304,8 @@ func (h *udpHandler) Connect(gconn *netstack.GUDPConn, src, target netip.AddrPor
304304
}
305305

306306
if log.Verbose {
307-
log.V("udp: connect: %s [%s] proxying %s => %s [%v]; pids: %s, mux? %t / fwd? %t / localnat64? %t",
308-
cid, uid, src, target, actualTargets, pids, mux, canportfwd, targetIsLocalNat64)
307+
log.V("udp: connect: %s [%s] proxying %s => %s [%v]; pids: %s, mux? %t / fwd? %t / localnat64? %t / excluded? %v",
308+
cid, uid, src, target, actualTargets, pids, mux, canportfwd, targetIsLocalNat64, excluded)
309309
}
310310

311311
// note: fake-dns-ips shouldn't be un-nated / un-alg'd

intra/x64/dns64.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ func (d *dns64) pauseOrResume() {
9494
log.I("dns64: flow: disabled; pausing...")
9595
}
9696
} else if pt == settings.PtModeAuto && !has6 {
97+
// in auto mode, but no v6; pause 6to4 translations
98+
// in force 4 via 6 mode; 6to4 does not make sense
9799
if d.paused.CompareAndSwap(false, true) {
98100
log.I("dns64: flow: auto; no ipv6; pausing...")
99101
}
@@ -358,7 +360,6 @@ func (d *dns64) ofLocal464() error {
358360

359361
// add adds the nat64 prefixes to the dns64 map; thread-safe.
360362
func (d *dns64) add(serverid string, nat64 []net.IP) error {
361-
362363
if len(nat64) <= 0 {
363364
log.W("dns64: no nat64 ips for %s", serverid)
364365
return errEmpty

intra/x64/natpt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ func NewNatPt2(ctx context.Context) *natPt {
6464
}
6565

6666
// D64 Implements DNS64.
67-
func (pt *natPt) D64(network, id, uid string, ans6 *dns.Msg) *dns.Msg {
67+
func (pt *natPt) D64(network, id, uid string, ans6 *dns.Msg) (ans4 *dns.Msg) {
6868
ptmode := settings.PtMode.Load()
6969
if ptmode != settings.PtModeNo46 { // do64
70-
force64 := ptmode == settings.PtModeForce64
70+
force64 := ptmode == settings.PtModeForce64 || ptmode == settings.PtModeForce
7171
return pt.dns64.eval(network, force64, ans6, id, uid)
7272
}
7373
return nil

intra/xdns/dnsutil.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ func Request4FromRequest6(msg6 *dns.Msg) *dns.Msg {
9494
return msg4
9595
}
9696

97+
func Request6FromRequest4(msg4 *dns.Msg) *dns.Msg {
98+
if !HasAnyQuestion(msg4) {
99+
return nil
100+
}
101+
msg6 := msg4.Copy()
102+
msg6.SetQuestion(QName(msg4), dns.TypeAAAA)
103+
return msg6
104+
}
105+
97106
func EmptyResponseFromMessage(srcMsg *dns.Msg) *dns.Msg {
98107
if !HasAnyQuestion(srcMsg) {
99108
return nil

0 commit comments

Comments
 (0)