mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	It was pretty ill-defined before and mostly for logging. But I wanted to start depending on it, so define what it is and make Windows match the other operating systems, without losing the log output we had before. (and add tests for that) Change-Id: I0fbbba1cfc67a265d09dd6cb738b73f0f6005247 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			132 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package interfaces
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"net"
 | |
| 	"syscall"
 | |
| 
 | |
| 	"golang.org/x/net/route"
 | |
| 	"golang.org/x/sys/unix"
 | |
| 	"inet.af/netaddr"
 | |
| )
 | |
| 
 | |
| func defaultRoute() (d DefaultRouteDetails, err error) {
 | |
| 	idx, err := DefaultRouteInterfaceIndex()
 | |
| 	if err != nil {
 | |
| 		return d, err
 | |
| 	}
 | |
| 	iface, err := net.InterfaceByIndex(idx)
 | |
| 	if err != nil {
 | |
| 		return d, err
 | |
| 	}
 | |
| 	d.InterfaceName = iface.Name
 | |
| 	d.InterfaceIndex = idx
 | |
| 	return d, nil
 | |
| }
 | |
| 
 | |
| // fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP2.
 | |
| func fetchRoutingTable() (rib []byte, err error) {
 | |
| 	return route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_DUMP2, 0)
 | |
| }
 | |
| 
 | |
| func DefaultRouteInterfaceIndex() (int, error) {
 | |
| 	// $ netstat -nr
 | |
| 	// Routing tables
 | |
| 	// Internet:
 | |
| 	// Destination        Gateway            Flags        Netif Expire
 | |
| 	// default            10.0.0.1           UGSc           en0         <-- want this one
 | |
| 	// default            10.0.0.1           UGScI          en1
 | |
| 
 | |
| 	// From man netstat:
 | |
| 	// U       RTF_UP           Route usable
 | |
| 	// G       RTF_GATEWAY      Destination requires forwarding by intermediary
 | |
| 	// S       RTF_STATIC       Manually added
 | |
| 	// c       RTF_PRCLONING    Protocol-specified generate new routes on use
 | |
| 	// I       RTF_IFSCOPE      Route is associated with an interface scope
 | |
| 
 | |
| 	rib, err := fetchRoutingTable()
 | |
| 	if err != nil {
 | |
| 		return 0, fmt.Errorf("route.FetchRIB: %w", err)
 | |
| 	}
 | |
| 	msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib)
 | |
| 	if err != nil {
 | |
| 		return 0, fmt.Errorf("route.ParseRIB: %w", err)
 | |
| 	}
 | |
| 	indexSeen := map[int]int{} // index => count
 | |
| 	for _, m := range msgs {
 | |
| 		rm, ok := m.(*route.RouteMessage)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		const RTF_GATEWAY = 0x2
 | |
| 		const RTF_IFSCOPE = 0x1000000
 | |
| 		if rm.Flags&RTF_GATEWAY == 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if rm.Flags&RTF_IFSCOPE != 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		indexSeen[rm.Index]++
 | |
| 	}
 | |
| 	if len(indexSeen) == 0 {
 | |
| 		return 0, errors.New("no gateway index found")
 | |
| 	}
 | |
| 	if len(indexSeen) == 1 {
 | |
| 		for idx := range indexSeen {
 | |
| 			return idx, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, fmt.Errorf("ambiguous gateway interfaces found: %v", indexSeen)
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	likelyHomeRouterIP = likelyHomeRouterIPDarwinFetchRIB
 | |
| }
 | |
| 
 | |
| func likelyHomeRouterIPDarwinFetchRIB() (ret netaddr.IP, ok bool) {
 | |
| 	rib, err := fetchRoutingTable()
 | |
| 	if err != nil {
 | |
| 		log.Printf("routerIP/FetchRIB: %v", err)
 | |
| 		return ret, false
 | |
| 	}
 | |
| 	msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib)
 | |
| 	if err != nil {
 | |
| 		log.Printf("routerIP/ParseRIB: %v", err)
 | |
| 		return ret, false
 | |
| 	}
 | |
| 	for _, m := range msgs {
 | |
| 		rm, ok := m.(*route.RouteMessage)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		const RTF_GATEWAY = 0x2
 | |
| 		const RTF_IFSCOPE = 0x1000000
 | |
| 		if rm.Flags&RTF_GATEWAY == 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if rm.Flags&RTF_IFSCOPE != 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if len(rm.Addrs) > unix.RTAX_GATEWAY {
 | |
| 			dst4, ok := rm.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
 | |
| 			if !ok || dst4.IP != ([4]byte{0, 0, 0, 0}) {
 | |
| 				// Expect 0.0.0.0 as DST field.
 | |
| 				continue
 | |
| 			}
 | |
| 			gw, ok := rm.Addrs[unix.RTAX_GATEWAY].(*route.Inet4Addr)
 | |
| 			if !ok {
 | |
| 				continue
 | |
| 			}
 | |
| 			return netaddr.IPv4(gw.IP[0], gw.IP[1], gw.IP[2], gw.IP[3]), true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret, false
 | |
| }
 |