google_guest_agent/addresses_windows.go (298 lines of code) (raw):

// Copyright 2017 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // https://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build windows // +build windows package main import ( "bytes" "encoding/binary" "fmt" "net" "syscall" "unsafe" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "golang.org/x/sys/windows" ) var ( ipHlpAPI = windows.NewLazySystemDLL("iphlpapi.dll") procAddIPAddress = ipHlpAPI.NewProc("AddIPAddress") procDeleteIPAddress = ipHlpAPI.NewProc("DeleteIPAddress") procCreateIpForwardEntry = ipHlpAPI.NewProc("CreateIpForwardEntry") procDeleteIpForwardEntry = ipHlpAPI.NewProc("DeleteIpForwardEntry") procGetIpForwardTable = ipHlpAPI.NewProc("GetIpForwardTable") procGetIpInterfaceEntry = ipHlpAPI.NewProc("GetIpInterfaceEntry") procSetIpInterfaceEntry = ipHlpAPI.NewProc("SetIpInterfaceEntry") procCreateUnicastIpAddressEntry = ipHlpAPI.NewProc("CreateUnicastIpAddressEntry") procInitializeUnicastIpAddressEntry = ipHlpAPI.NewProc("InitializeUnicastIpAddressEntry") procGetUnicastIpAddressEntry = ipHlpAPI.NewProc("GetUnicastIpAddressEntry") procDeleteUnicastIpAddressEntry = ipHlpAPI.NewProc("DeleteUnicastIpAddressEntry") ) const ( AF_NET = 2 AF_INET6 = 23 ) type MIB_IPFORWARD_TYPE DWORD const ( MIB_IPROUTE_TYPE_OTHER MIB_IPFORWARD_TYPE = 1 MIB_IPROUTE_TYPE_INVALID = 2 MIB_IPROUTE_TYPE_DIRECT = 3 MIB_IPROUTE_TYPE_INDIRECT = 4 ) type MIB_IPFORWARD_PROTO DWORD const MIB_IPPROTO_NETMGMT MIB_IPFORWARD_PROTO = 3 type ( in_addr struct { S_un struct { S_addr uint32 } } SOCKADDR_IN struct { sin_family int16 sin_addr in_addr } SOCKADDR_INET struct { Ipv4 SOCKADDR_IN si_family int16 } NET_LUID struct { Value uint64 Info struct { NetLuidIndex uint64 IfType uint64 } } MIB_UNICASTIPADDRESS_ROW struct { Address SOCKADDR_INET InterfaceLuid NET_LUID InterfaceIndex uint32 PrefixOrigin uint32 SuffixOrigin uint32 ValidLifetime uint32 PreferredLifetime uint32 OnLinkPrefixLength uint8 SkipAsSource bool } IF_INDEX DWORD MIB_IPFORWARDROW struct { dwForwardDest uint32 dwForwardMask uint32 dwForwardPolicy uint32 dwForwardNextHop uint32 dwForwardIfIndex IF_INDEX dwForwardType MIB_IPFORWARD_TYPE dwForwardProto MIB_IPFORWARD_PROTO dwForwardAge int32 dwForwardNextHopAS int32 dwForwardMetric1 int32 dwForwardMetric2 int32 dwForwardMetric3 int32 dwForwardMetric4 int32 dwForwardMetric5 int32 } ) func addAddress(ip net.IP, mask net.IPMask, index uint32) error { logger.Infof("Adding %q ip address", ip.String()) subnet, _ := mask.Size() // CreateUnicastIpAddressEntry only available Vista onwards. // AddIPAddress api supports only ipv4 address. if isIPv6(ip) { return createUnicastIpAddressEntry6(ip, uint8(subnet), index) } // https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-addipaddress if err := procCreateUnicastIpAddressEntry.Find(); err != nil && !isIPv6(ip) { return addIPAddress(ip, mask, index) } return createUnicastIpAddressEntry(ip, uint8(subnet), index) } func removeAddress(ip net.IP, mask net.IPMask, index uint32) error { logger.Infof("Removing %q ip address", ip.String()) subnet, _ := mask.Size() if isIPv6(ip) { // Unlike ipv4 that can be added either by addIPAddress or createUnicastIpAddressEntry // ipv6 addresses can only be added by createUnicastIpAddressEntry6. // Try removing them by deleteUnicastIpAddressEntry6 only as deleteIPAddress deletes // IP address previously added using AddIPAddress only. return deleteUnicastIpAddressEntry6(ip, uint8(subnet), index) } // DeleteUnicastIpAddressEntry only available Vista onwards. if err := procDeleteUnicastIpAddressEntry.Find(); err != nil { return deleteIPAddress(ip) } return deleteUnicastIpAddressEntry(ip, index) } // LUID represents the locally unique identifier (LUID) for a network interface. // https://learn.microsoft.com/en-us/windows/win32/api/ifdef/ns-ifdef-net_luid_lh type LUID uint64 // RawSockaddrInet represents an IPv4/IPv6 address and family. // https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_inet type RawSockaddrInet struct { Family uint16 data [26]byte } // MIBUnicastIPAddressRow6 stores unicast IP address information used with for // ipforwarding addresses. // https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row type MIBUnicastIPAddressRow6 struct { Address RawSockaddrInet InterfaceLuid LUID InterfaceIndex uint32 PrefixOrigin uint32 SuffixOrigin uint32 ValidLifetime uint32 PreferredLifetime uint32 OnLinkPrefixLength uint8 SkipAsSource bool } // setAddr is helper function to set net.IP in a structure windows syscalls understand. func (addr *RawSockaddrInet) setAddr(ip net.IP) { addr6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(addr)) addr6.Family = windows.AF_INET6 copy(addr6.Addr[:], ip) } // initIpRow6 initializes the MIB_UNICASTIPADDRESS_ROW6 struct based on Ip, prefix length // and the index. func initIpRow6(ip net.IP, prefix uint8, index uint32) (*MIBUnicastIPAddressRow6, error) { ipRow := new(MIBUnicastIPAddressRow6) // No return value. procInitializeUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))) ipRow.InterfaceIndex = index ipRow.OnLinkPrefixLength = prefix // https://blogs.technet.microsoft.com/rmilne/2012/02/08/fine-grained-control-when-registering-multiple-ip-addresses-on-a-network-card/ ipRow.SkipAsSource = true addr := RawSockaddrInet{} addr.setAddr(ip) ipRow.Address = addr return ipRow, nil } // createUnicastIpAddressEntry6 adds a new unicast IPv6 address entry on local machine. func createUnicastIpAddressEntry6(ip net.IP, prefix uint8, index uint32) error { logger.Infof("CreateUnicastIpAddressEntry6 %v, with prefix %d on interface %d", ip, prefix, index) ipRow, err := initIpRow6(ip, prefix, index) if err != nil { return fmt.Errorf("initialize MIB_UNICASTIPADDRESS_ROW failed: %w", err) } if ret, _, err := procCreateUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from CreateUnicastIpAddressEntry: %s, last err: %w", syscall.Errno(ret), err) } return nil } // deleteUnicastIpAddressEntry6 removes a unicast IPv6 address entry from local machine. func deleteUnicastIpAddressEntry6(ip net.IP, prefix uint8, index uint32) error { logger.Infof("DeleteUnicastIpAddressEntry6 %v, with prefix %d on interface %d", ip, prefix, index) ipRow, err := initIpRow6(ip, prefix, index) if err != nil { return fmt.Errorf("initialize MIB_UNICASTIPADDRESS_ROW failed: %w", err) } if ret, _, err := procDeleteUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from DeleteUnicastIpAddressEntry: %s, last err: %w", syscall.Errno(ret), err) } return nil } func createUnicastIpAddressEntry(ip net.IP, prefix uint8, index uint32) error { ipRow := new(MIB_UNICASTIPADDRESS_ROW) // No return value. procInitializeUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))) ipRow.InterfaceIndex = index ipRow.OnLinkPrefixLength = prefix // https://blogs.technet.microsoft.com/rmilne/2012/02/08/fine-grained-control-when-registering-multiple-ip-addresses-on-a-network-card/ ipRow.SkipAsSource = true ipRow.Address.si_family = AF_NET ipRow.Address.Ipv4.sin_family = AF_NET ipRow.Address.Ipv4.sin_addr.S_un.S_addr = binary.LittleEndian.Uint32(ip.To4()) if ret, _, _ := procCreateUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from CreateUnicastIpAddressEntry: %s", syscall.Errno(ret)) } return nil } func deleteUnicastIpAddressEntry(ip net.IP, index uint32) error { ipRow := new(MIB_UNICASTIPADDRESS_ROW) ipRow.InterfaceIndex = index ipRow.Address.si_family = AF_NET ipRow.Address.Ipv4.sin_family = AF_NET ipRow.Address.Ipv4.sin_addr.S_un.S_addr = binary.LittleEndian.Uint32(ip.To4()) ret, _, _ := procGetUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))) // ERROR_NOT_FOUND if ret == 1168 { // This address was added by addIPAddress(), need to remove with deleteIPAddress() return deleteIPAddress(ip) } if ret != 0 { return fmt.Errorf("nonzero return code from GetUnicastIpAddressEntry: %s", syscall.Errno(ret)) } if ret, _, _ := procDeleteUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from DeleteUnicastIpAddressEntry: %s", syscall.Errno(ret)) } return nil } func addIPAddress(ip net.IP, mask net.IPMask, index uint32) error { var nteC int var nteI int ret, _, _ := procAddIPAddress.Call( uintptr(binary.LittleEndian.Uint32(ip.To4())), uintptr(binary.LittleEndian.Uint32(mask)), uintptr(index), uintptr(unsafe.Pointer(&nteC)), uintptr(unsafe.Pointer(&nteI))) if ret != 0 { return fmt.Errorf("nonzero return code from AddIPAddress: %s", syscall.Errno(ret)) } return nil } func deleteIPAddress(ip net.IP) error { ip = ip.To4() b := make([]byte, 1) ai := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) l := uint32(0) syscall.GetAdaptersInfo(ai, &l) b = make([]byte, int32(l)) ai = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) if err := syscall.GetAdaptersInfo(ai, &l); err != nil { return err } for ; ai != nil; ai = ai.Next { for ipl := &ai.IpAddressList; ipl != nil; ipl = ipl.Next { ipb := bytes.Trim(ipl.IpAddress.String[:], "\x00") if string(ipb) != ip.String() { continue } nteC := ipl.Context ret, _, _ := procDeleteIPAddress.Call(uintptr(nteC)) if ret != 0 { return fmt.Errorf("nonzero return code from DeleteIPAddress: %s", syscall.Errno(ret)) } return nil } } return fmt.Errorf("did not find address %s on system", ip) } func getIPForwardEntries() ([]ipForwardEntry, error) { buf := make([]byte, 1) size := uint32(len(buf)) // First call gets the size of MIB_IPFORWARDTABLE. procGetIpForwardTable.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), 0, ) buf = make([]byte, size) if ret, _, _ := procGetIpForwardTable.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), 0, ); ret != 0 { return nil, fmt.Errorf("nonzero return code from GetIpForwardTable: %s", syscall.Errno(ret)) } /* struct MIB_IPFORWARDTABLE { DWORD dwNumEntries; MIB_IPFORWARDROW table[ANY_SIZE]; } */ numEntries := *(*uint32)(unsafe.Pointer(&buf[0])) // Walk through the returned table for each entry. var fes []ipForwardEntry for i := uint32(0); i < numEntries; i++ { // Extract each MIB_IPFORWARDROW from MIB_IPFORWARDTABLE fr := *((*MIB_IPFORWARDROW)(unsafe.Pointer( (uintptr(unsafe.Pointer(&buf[0])) + unsafe.Sizeof(numEntries)) + (unsafe.Sizeof(MIB_IPFORWARDROW{}) * uintptr(i)), ))) fd := make([]byte, 4) binary.LittleEndian.PutUint32(fd, uint32(fr.dwForwardDest)) fm := make([]byte, 4) binary.LittleEndian.PutUint32(fm, uint32(fr.dwForwardMask)) nh := make([]byte, 4) binary.LittleEndian.PutUint32(nh, uint32(fr.dwForwardNextHop)) fe := ipForwardEntry{ ipForwardDest: net.IP(fd), ipForwardMask: net.IPMask(fm), ipForwardNextHop: net.IP(nh), ipForwardIfIndex: int32(fr.dwForwardIfIndex), ipForwardMetric1: fr.dwForwardMetric1, } fes = append(fes, fe) } return fes, nil } func addIPForwardEntry(fe ipForwardEntry) error { // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-createipforwardentry fr := &MIB_IPFORWARDROW{ dwForwardDest: binary.LittleEndian.Uint32(fe.ipForwardDest.To4()), dwForwardMask: binary.LittleEndian.Uint32(fe.ipForwardMask), dwForwardPolicy: 0, // unused dwForwardNextHop: binary.LittleEndian.Uint32(fe.ipForwardNextHop.To4()), dwForwardIfIndex: IF_INDEX(fe.ipForwardIfIndex), dwForwardType: MIB_IPROUTE_TYPE_INDIRECT, // unused dwForwardProto: MIB_IPPROTO_NETMGMT, dwForwardAge: 0, // unused dwForwardNextHopAS: 0, // unused dwForwardMetric1: fe.ipForwardMetric1, dwForwardMetric2: -1, // unused dwForwardMetric3: -1, // unused dwForwardMetric4: -1, // unused dwForwardMetric5: -1, // unused } if ret, _, _ := procCreateIpForwardEntry.Call(uintptr(unsafe.Pointer(fr))); ret != 0 { return fmt.Errorf("nonzero return code from CreateIpForwardEntry: %s", syscall.Errno(ret)) } return nil }