diff options
Diffstat (limited to '')
-rw-r--r-- | tunnel/winipcfg/luid.go | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/tunnel/winipcfg/luid.go b/tunnel/winipcfg/luid.go new file mode 100644 index 00000000..246c7585 --- /dev/null +++ b/tunnel/winipcfg/luid.go @@ -0,0 +1,355 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package winipcfg + +import ( + "fmt" + "net" + + "golang.org/x/sys/windows" +) + +// LUID represents a network interface. +type LUID uint64 + +// IPInterface method retrieves IP information for the specified interface on the local computer. +func (luid LUID) IPInterface(family AddressFamily) (*MibIPInterfaceRow, error) { + row := &MibIPInterfaceRow{} + row.Init() + row.InterfaceLUID = luid + row.Family = family + err := row.get() + if err != nil { + return nil, err + } + return row, nil +} + +// Interface method retrieves information for the specified adapter on the local computer. +// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2 +func (luid LUID) Interface() (*MibIfRow2, error) { + row := &MibIfRow2{} + row.InterfaceLUID = luid + err := row.get() + if err != nil { + return nil, err + } + return row, nil +} + +// GUID method converts a locally unique identifier (LUID) for a network interface to a globally unique identifier (GUID) for the interface. +// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceluidtoguid +func (luid LUID) GUID() (*windows.GUID, error) { + guid := &windows.GUID{} + err := convertInterfaceLUIDToGUID(&luid, guid) + if err != nil { + return nil, err + } + return guid, nil +} + +// LUIDFromGUID function converts a globally unique identifier (GUID) for a network interface to the locally unique identifier (LUID) for the interface. +// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceguidtoluid +func LUIDFromGUID(guid *windows.GUID) (LUID, error) { + var luid LUID + err := convertInterfaceGUIDToLUID(guid, &luid) + if err != nil { + return 0, err + } + return luid, nil +} + +// IPAddress method returns MibUnicastIPAddressRow struct that matches to provided 'ip' argument. Corresponds to GetUnicastIpAddressEntry +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry) +func (luid LUID) IPAddress(ip net.IP) (*MibUnicastIPAddressRow, error) { + row := &MibUnicastIPAddressRow{InterfaceLUID: luid} + + err := row.Address.SetIP(ip, 0) + if err != nil { + return nil, err + } + + err = row.get() + if err != nil { + return nil, err + } + + return row, nil +} + +// AddIPAddress method adds new unicast IP address to the interface. Corresponds to CreateUnicastIpAddressEntry function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry). +func (luid LUID) AddIPAddress(address net.IPNet) error { + row := &MibUnicastIPAddressRow{} + initializeUnicastIPAddressEntry(row) + row.InterfaceLUID = luid + err := row.Address.SetIP(address.IP, 0) + if err != nil { + return err + } + ones, _ := address.Mask.Size() + row.OnLinkPrefixLength = uint8(ones) + return row.Create() +} + +// AddIPAddresses method adds multiple new unicast IP addresses to the interface. Corresponds to CreateUnicastIpAddressEntry function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry). +func (luid LUID) AddIPAddresses(addresses []net.IPNet) error { + for i := range addresses { + err := luid.AddIPAddress(addresses[i]) + if err != nil { + return err + } + } + return nil +} + +// SetIPAddresses method sets new unicast IP addresses to the interface. +func (luid LUID) SetIPAddresses(addresses []net.IPNet) error { + err := luid.FlushIPAddresses(windows.AF_UNSPEC) + if err != nil { + return err + } + return luid.AddIPAddresses(addresses) +} + +// DeleteIPAddress method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry). +func (luid LUID) DeleteIPAddress(ip net.IP) error { + row, err := luid.IPAddress(ip) + if err != nil { + return err + } + return row.Delete() +} + +// FlushIPAddresses method deletes all interface's unicast IP addresses. +func (luid LUID) FlushIPAddresses(family AddressFamily) error { + var tab *mibUnicastIPAddressTable + err := getUnicastIPAddressTable(family, &tab) + if err != nil { + return err + } + t := tab.get() + for i := range t { + if t[i].InterfaceLUID == luid { + t[i].Delete() + } + } + tab.free() + return nil +} + +// Route method returns route determined with the input arguments. Corresponds to GetIpForwardEntry2 function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2). +// NOTE: If the corresponding route isn't found, the method will return error. +func (luid LUID) Route(destination net.IPNet, nextHop net.IP) (*MibIPforwardRow2, error) { + row := &MibIPforwardRow2{} + row.Init() + row.InterfaceLUID = luid + err := row.DestinationPrefix.SetIPNet(destination) + if err != nil { + return nil, err + } + err = row.NextHop.SetIP(nextHop, 0) + if err != nil { + return nil, err + } + + err = row.get() + if err != nil { + return nil, err + } + return row, nil +} + +// AddRoute method adds a route to the interface. Corresponds to CreateIpForwardEntry2 function, with added splitDefault feature. +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2) +func (luid LUID) AddRoute(destination net.IPNet, nextHop net.IP, metric uint32) error { + row := &MibIPforwardRow2{} + row.Init() + row.InterfaceLUID = luid + err := row.DestinationPrefix.SetIPNet(destination) + if err != nil { + return err + } + err = row.NextHop.SetIP(nextHop, 0) + if err != nil { + return err + } + row.Metric = metric + return row.Create() +} + +// AddRoutes method adds multiple routes to the interface. +func (luid LUID) AddRoutes(routesData []*RouteData) error { + for _, rd := range routesData { + err := luid.AddRoute(rd.Destination, rd.NextHop, rd.Metric) + if err != nil { + return err + } + } + return nil +} + +// SetRoutes method sets (flush than add) multiple routes to the interface. +func (luid LUID) SetRoutes(routesData []*RouteData) error { + err := luid.FlushRoutes(windows.AF_UNSPEC) + if err != nil { + return err + } + return luid.AddRoutes(routesData) +} + +// DeleteRoute method deletes a route that matches the criteria. Corresponds to DeleteIpForwardEntry2 function +// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2). +func (luid LUID) DeleteRoute(destination net.IPNet, nextHop net.IP) error { + row := &MibIPforwardRow2{} + row.Init() + row.InterfaceLUID = luid + err := row.DestinationPrefix.SetIPNet(destination) + if err != nil { + return err + } + err = row.NextHop.SetIP(nextHop, 0) + if err != nil { + return err + } + err = row.get() + if err != nil { + return err + } + return row.Delete() +} + +// FlushRoutes method deletes all interface's routes. +// It continues on failures, and returns the last error afterwards. +func (luid LUID) FlushRoutes(family AddressFamily) error { + var tab *mibIPforwardTable2 + err := getIPForwardTable2(family, &tab) + if err != nil { + return err + } + t := tab.get() + for i := range t { + if t[i].InterfaceLUID == luid { + err2 := t[i].Delete() + if err2 != nil { + err = err2 + } + } + } + tab.free() + return err +} + +// DNS method returns all DNS server addresses associated with the adapter. +func (luid LUID) DNS() ([]net.IP, error) { + addresses, err := GetAdaptersAddresses(windows.AF_UNSPEC, GAAFlagDefault) + if err != nil { + return nil, err + } + r := make([]net.IP, 0, len(addresses)) + for _, addr := range addresses { + if addr.LUID == luid { + for dns := addr.FirstDNSServerAddress; dns != nil; dns = dns.Next { + if ip := SocketAddressToIP(&dns.Address); len(ip) != 0 { + r = append(r, ip) + } else { + return nil, windows.ERROR_INVALID_PARAMETER + } + } + } + } + return r, nil +} + +const ( + netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no register=both" + netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no register=both" + netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no" + netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no" +) + +// FlushDNS method clears all DNS servers associated with the adapter. +func (luid LUID) FlushDNS() error { + cmds := make([]string, 0, 2) + ipif4, err := luid.IPInterface(windows.AF_INET) + if err == nil { + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateFlush4, ipif4.InterfaceIndex)) + } + ipif6, err := luid.IPInterface(windows.AF_INET6) + if err == nil { + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateFlush6, ipif6.InterfaceIndex)) + } + + if len(cmds) == 0 { + return nil + } + return runNetsh(cmds) +} + +// AddDNS method associates additional DNS servers with the adapter. +func (luid LUID) AddDNS(dnses []net.IP) error { + var ipif4, ipif6 *MibIPInterfaceRow + var err error + cmds := make([]string, 0, len(dnses)) + for i := 0; i < len(dnses); i++ { + if v4 := dnses[i].To4(); v4 != nil { + if ipif4 == nil { + ipif4, err = luid.IPInterface(windows.AF_INET) + if err != nil { + return err + } + } + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd4, ipif4.InterfaceIndex, v4.String())) + } else if v6 := dnses[i].To16(); v6 != nil { + if ipif6 == nil { + ipif6, err = luid.IPInterface(windows.AF_INET6) + if err != nil { + return err + } + } + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd6, ipif6.InterfaceIndex, v6.String())) + } + } + + if len(cmds) == 0 { + return nil + } + return runNetsh(cmds) +} + +// SetDNS method clears previous and associates new DNS servers with the adapter. +func (luid LUID) SetDNS(dnses []net.IP) error { + cmds := make([]string, 0, 2+len(dnses)) + ipif4, err := luid.IPInterface(windows.AF_INET) + if err == nil { + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateFlush4, ipif4.InterfaceIndex)) + } + ipif6, err := luid.IPInterface(windows.AF_INET6) + if err == nil { + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateFlush6, ipif6.InterfaceIndex)) + } + for i := 0; i < len(dnses); i++ { + if v4 := dnses[i].To4(); v4 != nil { + if ipif4 == nil { + return windows.ERROR_NOT_SUPPORTED + } + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd4, ipif4.InterfaceIndex, v4.String())) + } else if v6 := dnses[i].To16(); v6 != nil { + if ipif6 == nil { + return windows.ERROR_NOT_SUPPORTED + } + cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd6, ipif6.InterfaceIndex, v6.String())) + } + } + + if len(cmds) == 0 { + return nil + } + return runNetsh(cmds) +} |