2023-06-09 18:27:09 +02:00
|
|
|
//go:build windows
|
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
package systemops
|
2023-06-09 19:15:39 +02:00
|
|
|
|
|
|
|
import (
|
2024-04-08 18:56:52 +02:00
|
|
|
"fmt"
|
2023-06-12 11:43:18 +02:00
|
|
|
"net"
|
2023-06-09 19:15:39 +02:00
|
|
|
"net/netip"
|
2024-04-26 16:37:27 +02:00
|
|
|
"os"
|
2024-04-08 18:56:52 +02:00
|
|
|
"os/exec"
|
2024-04-26 16:37:27 +02:00
|
|
|
"strconv"
|
2024-04-08 18:56:52 +02:00
|
|
|
"strings"
|
2024-04-26 16:37:27 +02:00
|
|
|
"sync"
|
|
|
|
"time"
|
2023-06-09 19:15:39 +02:00
|
|
|
|
2024-04-08 18:56:52 +02:00
|
|
|
log "github.com/sirupsen/logrus"
|
2023-06-09 19:17:26 +02:00
|
|
|
"github.com/yusufpapurcu/wmi"
|
2024-04-08 18:56:52 +02:00
|
|
|
|
2024-05-09 13:48:15 +02:00
|
|
|
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
2024-06-17 09:47:17 +02:00
|
|
|
nbnet "github.com/netbirdio/netbird/util/net"
|
2023-06-09 19:15:39 +02:00
|
|
|
)
|
|
|
|
|
2024-05-07 18:50:34 +02:00
|
|
|
type MSFT_NetRoute struct {
|
|
|
|
DestinationPrefix string
|
|
|
|
NextHop string
|
|
|
|
InterfaceIndex int32
|
|
|
|
InterfaceAlias string
|
|
|
|
AddressFamily uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
type Route struct {
|
|
|
|
Destination netip.Prefix
|
|
|
|
Nexthop netip.Addr
|
|
|
|
Interface *net.Interface
|
|
|
|
}
|
|
|
|
|
|
|
|
type MSFT_NetNeighbor struct {
|
|
|
|
IPAddress string
|
|
|
|
LinkLayerAddress string
|
|
|
|
State uint8
|
|
|
|
AddressFamily uint16
|
|
|
|
InterfaceIndex uint32
|
|
|
|
InterfaceAlias string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Neighbor struct {
|
|
|
|
IPAddress netip.Addr
|
|
|
|
LinkLayerAddress string
|
|
|
|
State uint8
|
|
|
|
AddressFamily uint16
|
|
|
|
InterfaceIndex uint32
|
|
|
|
InterfaceAlias string
|
2023-06-09 19:15:39 +02:00
|
|
|
}
|
|
|
|
|
2024-04-26 16:37:27 +02:00
|
|
|
var prefixList []netip.Prefix
|
|
|
|
var lastUpdate time.Time
|
|
|
|
var mux = sync.Mutex{}
|
|
|
|
|
2024-06-17 09:47:17 +02:00
|
|
|
func (r *SysOps) SetupRouting(initAddresses []net.IP) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
|
2024-06-13 13:24:24 +02:00
|
|
|
return r.setupRefCounter(initAddresses)
|
|
|
|
}
|
2024-04-08 18:56:52 +02:00
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
func (r *SysOps) CleanupRouting() error {
|
|
|
|
return r.cleanupRefCounter()
|
2024-04-08 18:56:52 +02:00
|
|
|
}
|
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
func (r *SysOps) addToRouteTable(prefix netip.Prefix, nexthop Nexthop) error {
|
|
|
|
if nexthop.IP.Zone() != "" && nexthop.Intf == nil {
|
|
|
|
zone, err := strconv.Atoi(nexthop.IP.Zone())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid zone: %w", err)
|
|
|
|
}
|
|
|
|
nexthop.Intf = &net.Interface{Index: zone}
|
|
|
|
}
|
|
|
|
|
|
|
|
return addRouteCmd(prefix, nexthop)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *SysOps) removeFromRouteTable(prefix netip.Prefix, nexthop Nexthop) error {
|
|
|
|
args := []string{"delete", prefix.String()}
|
|
|
|
if nexthop.IP.IsValid() {
|
2024-06-20 13:02:02 +02:00
|
|
|
ip := nexthop.IP.WithZone("")
|
|
|
|
args = append(args, ip.Unmap().String())
|
2024-06-13 13:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
routeCmd := uspfilter.GetSystem32Command("route")
|
|
|
|
|
|
|
|
out, err := exec.Command(routeCmd, args...).CombinedOutput()
|
|
|
|
log.Tracef("route %s: %s", strings.Join(args, " "), out)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("remove route: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
2024-04-08 18:56:52 +02:00
|
|
|
}
|
|
|
|
|
2024-08-02 11:47:12 +02:00
|
|
|
func GetRoutesFromTable() ([]netip.Prefix, error) {
|
2024-04-26 16:37:27 +02:00
|
|
|
mux.Lock()
|
|
|
|
defer mux.Unlock()
|
|
|
|
|
|
|
|
// If many routes are added at the same time this might block for a long time (seconds to minutes), so we cache the result
|
|
|
|
if !isCacheDisabled() && time.Since(lastUpdate) < 2*time.Second {
|
|
|
|
return prefixList, nil
|
|
|
|
}
|
|
|
|
|
2024-05-07 18:50:34 +02:00
|
|
|
routes, err := GetRoutes()
|
2023-06-09 19:15:39 +02:00
|
|
|
if err != nil {
|
2024-04-08 18:56:52 +02:00
|
|
|
return nil, fmt.Errorf("get routes: %w", err)
|
2023-06-09 19:15:39 +02:00
|
|
|
}
|
|
|
|
|
2024-04-26 16:37:27 +02:00
|
|
|
prefixList = nil
|
2023-06-09 19:15:39 +02:00
|
|
|
for _, route := range routes {
|
2024-05-07 18:50:34 +02:00
|
|
|
prefixList = append(prefixList, route.Destination)
|
|
|
|
}
|
|
|
|
|
|
|
|
lastUpdate = time.Now()
|
|
|
|
return prefixList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRoutes() ([]Route, error) {
|
|
|
|
var entries []MSFT_NetRoute
|
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
query := `SELECT DestinationPrefix, Nexthop, InterfaceIndex, InterfaceAlias, AddressFamily FROM MSFT_NetRoute`
|
2024-05-07 18:50:34 +02:00
|
|
|
if err := wmi.QueryNamespace(query, &entries, `ROOT\StandardCimv2`); err != nil {
|
|
|
|
return nil, fmt.Errorf("get routes: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var routes []Route
|
|
|
|
for _, entry := range entries {
|
|
|
|
dest, err := netip.ParsePrefix(entry.DestinationPrefix)
|
2023-11-24 11:31:22 +01:00
|
|
|
if err != nil {
|
2024-05-07 18:50:34 +02:00
|
|
|
log.Warnf("Unable to parse route destination %s: %v", entry.DestinationPrefix, err)
|
2023-11-24 11:31:22 +01:00
|
|
|
continue
|
|
|
|
}
|
2024-05-07 18:50:34 +02:00
|
|
|
|
|
|
|
nexthop, err := netip.ParseAddr(entry.NextHop)
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("Unable to parse route next hop %s: %v", entry.NextHop, err)
|
2023-11-24 11:31:22 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-05-07 18:50:34 +02:00
|
|
|
var intf *net.Interface
|
|
|
|
if entry.InterfaceIndex != 0 {
|
|
|
|
intf = &net.Interface{
|
|
|
|
Index: int(entry.InterfaceIndex),
|
|
|
|
Name: entry.InterfaceAlias,
|
|
|
|
}
|
2024-06-20 13:02:02 +02:00
|
|
|
|
|
|
|
if nexthop.Is6() && (nexthop.IsLinkLocalUnicast() || nexthop.IsLinkLocalMulticast()) {
|
|
|
|
nexthop = nexthop.WithZone(strconv.Itoa(int(entry.InterfaceIndex)))
|
|
|
|
}
|
2023-06-12 11:43:18 +02:00
|
|
|
}
|
2024-05-07 18:50:34 +02:00
|
|
|
|
|
|
|
routes = append(routes, Route{
|
|
|
|
Destination: dest,
|
|
|
|
Nexthop: nexthop,
|
|
|
|
Interface: intf,
|
|
|
|
})
|
2023-06-09 19:15:39 +02:00
|
|
|
}
|
2024-04-26 16:37:27 +02:00
|
|
|
|
2024-05-07 18:50:34 +02:00
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetNeighbors() ([]Neighbor, error) {
|
|
|
|
var entries []MSFT_NetNeighbor
|
|
|
|
query := `SELECT IPAddress, LinkLayerAddress, State, AddressFamily, InterfaceIndex, InterfaceAlias FROM MSFT_NetNeighbor`
|
|
|
|
if err := wmi.QueryNamespace(query, &entries, `ROOT\StandardCimv2`); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to query MSFT_NetNeighbor: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var neighbors []Neighbor
|
|
|
|
for _, entry := range entries {
|
|
|
|
addr, err := netip.ParseAddr(entry.IPAddress)
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("Unable to parse neighbor IP address %s: %v", entry.IPAddress, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
neighbors = append(neighbors, Neighbor{
|
|
|
|
IPAddress: addr,
|
|
|
|
LinkLayerAddress: entry.LinkLayerAddress,
|
|
|
|
State: entry.State,
|
|
|
|
AddressFamily: entry.AddressFamily,
|
|
|
|
InterfaceIndex: entry.InterfaceIndex,
|
|
|
|
InterfaceAlias: entry.InterfaceAlias,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return neighbors, nil
|
2023-06-09 19:15:39 +02:00
|
|
|
}
|
2024-04-08 18:56:52 +02:00
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
func addRouteCmd(prefix netip.Prefix, nexthop Nexthop) error {
|
2024-04-26 16:37:27 +02:00
|
|
|
args := []string{"add", prefix.String()}
|
2024-04-09 13:25:14 +02:00
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
if nexthop.IP.IsValid() {
|
2024-06-20 13:02:02 +02:00
|
|
|
ip := nexthop.IP.WithZone("")
|
|
|
|
args = append(args, ip.Unmap().String())
|
2024-04-26 16:37:27 +02:00
|
|
|
} else {
|
|
|
|
addr := "0.0.0.0"
|
|
|
|
if prefix.Addr().Is6() {
|
|
|
|
addr = "::"
|
|
|
|
}
|
|
|
|
args = append(args, addr)
|
2024-04-08 18:56:52 +02:00
|
|
|
}
|
|
|
|
|
2024-06-13 13:24:24 +02:00
|
|
|
if nexthop.Intf != nil {
|
|
|
|
args = append(args, "if", strconv.Itoa(nexthop.Intf.Index))
|
2024-04-08 18:56:52 +02:00
|
|
|
}
|
|
|
|
|
2024-05-09 13:48:15 +02:00
|
|
|
routeCmd := uspfilter.GetSystem32Command("route")
|
|
|
|
|
|
|
|
out, err := exec.Command(routeCmd, args...).CombinedOutput()
|
2024-04-09 13:25:14 +02:00
|
|
|
log.Tracef("route %s: %s", strings.Join(args, " "), out)
|
2024-04-08 18:56:52 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("route add: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-26 16:37:27 +02:00
|
|
|
func isCacheDisabled() bool {
|
|
|
|
return os.Getenv("NB_DISABLE_ROUTE_CACHE") == "true"
|
|
|
|
}
|