11package netlink
22
33import (
4+ "context"
45 "fmt"
6+ "net"
7+ "net/netip"
8+ "time"
59)
610
711type IPv6SupportLevel uint8
@@ -21,7 +25,9 @@ func (i IPv6SupportLevel) IsSupported() bool {
2125 return i == IPv6Supported || i == IPv6Internet
2226}
2327
24- func (n * NetLink ) FindIPv6SupportLevel () (level IPv6SupportLevel , err error ) {
28+ func (n * NetLink ) FindIPv6SupportLevel (ctx context.Context ,
29+ checkAddress netip.AddrPort , firewall Firewall ,
30+ ) (level IPv6SupportLevel , err error ) {
2531 routes , err := n .RouteList (FamilyV6 )
2632 if err != nil {
2733 return IPv6Unsupported , fmt .Errorf ("listing IPv6 routes: %w" , err )
@@ -44,7 +50,14 @@ func (n *NetLink) FindIPv6SupportLevel() (level IPv6SupportLevel, err error) {
4450 case sourceIsIPv4 && destinationIsIPv4 ,
4551 destinationIsIPv6 && route .Dst .Addr ().IsLoopback ():
4652 case route .Dst .Addr ().IsUnspecified (): // default ipv6 route
47- n .debugLogger .Debugf ("IPv6 internet access is enabled on link %s" , link .Name )
53+ n .debugLogger .Debugf ("IPv6 default route found on link %s" , link .Name )
54+ err = dialAddrThroughFirewall (ctx , link .Name , checkAddress , firewall )
55+ if err != nil {
56+ n .debugLogger .Debugf ("IPv6 query failed on %s: %w" , link .Name , err )
57+ level = IPv6Supported
58+ continue
59+ }
60+ n .debugLogger .Debugf ("IPv6 internet is accessible through link %s" , link .Name )
4861 return IPv6Internet , nil
4962 default : // non-default ipv6 route found
5063 n .debugLogger .Debugf ("IPv6 is supported by link %s" , link .Name )
@@ -57,3 +70,37 @@ func (n *NetLink) FindIPv6SupportLevel() (level IPv6SupportLevel, err error) {
5770 }
5871 return level , nil
5972}
73+
74+ func dialAddrThroughFirewall (ctx context.Context , intf string ,
75+ checkAddress netip.AddrPort , firewall Firewall ,
76+ ) (err error ) {
77+ const protocol = "tcp"
78+ remove := false
79+ err = firewall .AcceptOutput (ctx , protocol , intf ,
80+ checkAddress .Addr (), checkAddress .Port (), remove )
81+ if err != nil {
82+ return fmt .Errorf ("accepting output traffic: %w" , err )
83+ }
84+ defer func () {
85+ remove = true
86+ firewallErr := firewall .AcceptOutput (ctx , protocol , intf ,
87+ checkAddress .Addr (), checkAddress .Port (), remove )
88+ if err == nil && firewallErr != nil {
89+ err = fmt .Errorf ("removing output traffic rule: %w" , firewallErr )
90+ }
91+ }()
92+
93+ dialer := & net.Dialer {
94+ Timeout : time .Second ,
95+ }
96+ conn , err := dialer .DialContext (ctx , protocol , checkAddress .String ())
97+ if err != nil {
98+ return fmt .Errorf ("dialing: %w" , err )
99+ }
100+ err = conn .Close ()
101+ if err != nil {
102+ return fmt .Errorf ("closing connection: %w" , err )
103+ }
104+
105+ return nil
106+ }
0 commit comments