@@ -9,6 +9,7 @@ package dnsx
99import (
1010 "encoding/binary"
1111 "errors"
12+ "hash/fnv"
1213 "net/netip"
1314 "strconv"
1415 "strings"
@@ -29,7 +30,7 @@ const (
2930 key4 = ":a"
3031 key6 = ":aaaa"
3132 NoTransport = "NoTransport"
32- maxiter = 1000 // max number alg/nat evict iterations
33+ maxiter = 100 // max number alg/nat evict iterations
3334)
3435
3536var (
@@ -105,6 +106,7 @@ type dnsgateway struct {
105106 rdns RdnsResolver // local and remote rdns blocks
106107 octets []uint8 // ip4 octets, 100.x.y.z
107108 hexes []uint16 // ip6 hex, 64:ff9b:1:da19:0100.x.y.z
109+ chash bool // use consistent hashing to generae alg ips
108110}
109111
110112// NewDNSGateway returns a DNS ALG, ready for use.
@@ -120,6 +122,7 @@ func NewDNSGateway(inner Transport, outer RdnsResolver) (t *dnsgateway) {
120122 rdns : outer ,
121123 octets : rfc6598 ,
122124 hexes : rfc8215a ,
125+ chash : true ,
123126 }
124127 // initial transport must be set before starting the gateway
125128 t .withTransport (inner )
@@ -493,6 +496,7 @@ func (t *dnsgateway) registerMultiLocked(q string, am *ansMulti) bool {
493496 return true
494497}
495498
499+ // register mapping from qname -> algip+realip (alg) and algip -> qname+realip (nat)
496500func (t * dnsgateway ) registerNatLocked (q string , idx int , x * ans ) bool {
497501 ip := x .algip
498502 var k string
@@ -508,6 +512,7 @@ func (t *dnsgateway) registerNatLocked(q string, idx int, x *ans) bool {
508512 return true
509513}
510514
515+ // register mapping from realip -> algip+qname (px)
511516func (t * dnsgateway ) registerPxLocked (q string , idx int , x * ans ) bool {
512517 ip := x .realip [idx ]
513518 t .px [* ip ] = x
@@ -528,6 +533,20 @@ func (t *dnsgateway) take4Locked(q string, idx int) (*netip.Addr, bool) {
528533 }
529534 }
530535
536+ if t .chash {
537+ for i := 0 ; i < maxiter ; i ++ {
538+ genip := gen4Locked (k , i )
539+ if ! genip .IsGlobalUnicast () {
540+ continue
541+ }
542+ if _ , taken := t .nat [genip ]; ! taken {
543+ return & genip , genip .IsValid ()
544+ }
545+ }
546+ log .W ("alg: gen: no more IP4s (%v)" , q )
547+ return nil , false
548+ }
549+
531550 gen := true
532551 // 100.x.y.z: 4m+ ip4s
533552 if z := t .octets [3 ]; z < 254 {
@@ -566,6 +585,20 @@ func (t *dnsgateway) take4Locked(q string, idx int) (*netip.Addr, bool) {
566585 return nil , false
567586}
568587
588+ func gen4Locked (k string , hop int ) netip.Addr {
589+ s := strconv .Itoa (hop ) + k
590+ v18 := hash18 (s )
591+ // 100.64.y.z/14 2m+ ip4s
592+ b4 := [4 ]byte {
593+ rfc6598 [0 ], // 100
594+ rfc6598 [1 ] | uint8 (v18 >> 16 )<< 6 , // 64 | msb 2 bits
595+ uint8 ((v18 >> 8 ) & 0xff ), // extract next 8 bits
596+ uint8 (v18 & 0xff ), // extract last 8 bits
597+ }
598+
599+ return netip .AddrFrom4 (b4 ).Unmap ()
600+ }
601+
569602func (t * dnsgateway ) take6Locked (q string , idx int ) (* netip.Addr , bool ) {
570603 k := q + key6 + strconv .Itoa (idx )
571604 if ans , ok := t .alg [k ]; ok {
@@ -580,6 +613,17 @@ func (t *dnsgateway) take6Locked(q string, idx int) (*netip.Addr, bool) {
580613 }
581614 }
582615
616+ if t .chash {
617+ for i := 0 ; i < maxiter ; i ++ {
618+ genip := gen6Locked (k , i )
619+ if _ , taken := t .nat [genip ]; ! taken {
620+ return & genip , genip .IsValid ()
621+ }
622+ }
623+ log .W ("alg: gen: no more IP6s (%v)" , q )
624+ return nil , false
625+ }
626+
583627 gen := true
584628 // 64:ff9b:1:da19:0100.x.y.z: 281 trillion ip6s
585629 if z := t .hexes [7 ]; z < 65534 {
@@ -610,6 +654,28 @@ func (t *dnsgateway) take6Locked(q string, idx int) (*netip.Addr, bool) {
610654 return nil , false
611655}
612656
657+ func gen6Locked (k string , hop int ) netip.Addr {
658+ s := strconv .Itoa (hop ) + k
659+ v48 := hash48 (s )
660+ // 64:ff9b:1:da19:0100.x.y.z: 281 trillion ip6s
661+ a16 := [8 ]uint16 {
662+ rfc8215a [0 ], // 64
663+ rfc8215a [1 ], // ff9b
664+ rfc8215a [2 ], // 1
665+ rfc8215a [3 ], // da19
666+ rfc8215a [4 ], // 0100
667+ uint16 ((v48 >> 32 ) & 0xffff ), // extract the top 16 bits
668+ uint16 ((v48 >> 16 ) & 0xffff ), // extract the mid 16 bits
669+ uint16 (v48 & 0xffff ), // extract the last 16 bits
670+ }
671+ b16 := [16 ]byte {}
672+ for i , hx := range a16 {
673+ i = i * 2
674+ binary .BigEndian .PutUint16 (b16 [i :i + 2 ], hx )
675+ }
676+ return netip .AddrFrom16 (b16 )
677+ }
678+
613679// Implements Gateway
614680func (t * dnsgateway ) withTransport (inner Transport ) bool {
615681 if inner == nil {
@@ -748,3 +814,19 @@ func (t *dnsgateway) rdnsbl(algip *netip.Addr) (bcsv string) {
748814 }
749815 return
750816}
817+
818+ // xor fold fnv to 18 bits: www.isthe.com/chongo/tech/comp/fnv
819+ func hash18 (s string ) uint32 {
820+ h := fnv .New64a ()
821+ h .Write ([]byte (s ))
822+ v64 := h .Sum64 ()
823+ return (uint32 (v64 >> 18 ) ^ uint32 (v64 )) & 0x3FFFF // 18 bits
824+ }
825+
826+ // xor fold fnv to 48 bits: www.isthe.com/chongo/tech/comp/fnv
827+ func hash48 (s string ) uint64 {
828+ h := fnv .New64a ()
829+ h .Write ([]byte (s ))
830+ v64 := h .Sum64 ()
831+ return (uint64 (v64 >> 48 ) ^ uint64 (v64 )) & 0xFFFFFFFFFFFF // 48 bits
832+ }
0 commit comments