1""" 2This salt util uses WMI to gather network information on Windows 7 and .NET 4.0+ 3on newer systems. 4The reason for this is that calls to WMI tend to be slower. Especially if the 5query has not been optimized. For example, timing to gather NIC info from WMI 6and .NET were as follows in testing: 7WMI: 3.4169998168945312 seconds 8NET: 1.0390000343322754 seconds 9Since this is used to generate grain information we want to avoid using WMI as 10much as possible. 11There are 3 functions in this salt util. 12- get_interface_info_dot_net 13- get_interface_info_wmi 14- get_interface_info 15The ``get_interface_info`` function will call one of the other two functions 16depending on the version of Windows this is run on. Once support for Windows 177 is dropped we can remove the WMI stuff and just use .NET. 18:depends: - pythonnet 19 - wmi 20""" 21# https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface.getallnetworkinterfaces?view=netframework-4.7.2 22 23import platform 24 25from salt._compat import ipaddress 26from salt.utils.versions import StrictVersion 27 28IS_WINDOWS = platform.system() == "Windows" 29 30__virtualname__ = "win_network" 31 32if IS_WINDOWS: 33 USE_WMI = StrictVersion(platform.version()) < StrictVersion("6.2") 34 if USE_WMI: 35 import wmi 36 import salt.utils.winapi 37 else: 38 import clr 39 from System.Net import NetworkInformation 40 41enum_adapter_types = { 42 1: "Unknown", 43 6: "Ethernet", 44 9: "TokenRing", 45 15: "FDDI", 46 20: "BasicISDN", 47 21: "PrimaryISDN", 48 23: "PPP", 49 24: "Loopback", 50 26: "Ethernet3Megabit", 51 28: "Slip", 52 37: "ATM", 53 48: "GenericModem", 54 53: "TAPAdapter", # Not in MSDN Defined enumeration 55 62: "FastEthernetT", 56 63: "ISDN", 57 69: "FastEthernetFx", 58 71: "Wireless802.11", 59 94: "AsymmetricDSL", 60 95: "RateAdaptDSL", 61 96: "SymmetricDSL", 62 97: "VeryHighSpeedDSL", 63 114: "IPOverATM", 64 117: "GigabitEthernet", 65 131: "Tunnel", 66 143: "MultiRateSymmetricDSL", 67 144: "HighPerformanceSerialBus", 68 237: "WMAN", 69 243: "WWANPP", 70 244: "WWANPP2", 71} 72 73enum_operational_status = { 74 1: "Up", 75 2: "Down", 76 3: "Testing", 77 4: "Unknown", 78 5: "Dormant", 79 6: "NotPresent", 80 7: "LayerDown", 81} 82 83enum_prefix_suffix = { 84 0: "Other", 85 1: "Manual", 86 2: "WellKnown", 87 3: "DHCP", 88 4: "Router", 89 5: "Random", 90} 91 92af_inet = 2 93af_inet6 = 23 94 95 96def __virtual__(): 97 """ 98 Only load if windows 99 """ 100 if not IS_WINDOWS: 101 return False, "This utility will only run on Windows" 102 103 return __virtualname__ 104 105 106def _get_base_properties(i_face): 107 raw_mac = i_face.GetPhysicalAddress().ToString() 108 try: 109 i_face_type = enum_adapter_types[i_face.NetworkInterfaceType] 110 except KeyError: 111 i_face_type = i_face.Description 112 return { 113 "alias": i_face.Name, 114 "description": i_face.Description, 115 "id": i_face.Id, 116 "receive_only": i_face.IsReceiveOnly, 117 "type": i_face_type, 118 "status": enum_operational_status[i_face.OperationalStatus], 119 "physical_address": ":".join(raw_mac[i : i + 2] for i in range(0, 12, 2)), 120 } 121 122 123def _get_ip_base_properties(i_face): 124 ip_properties = i_face.GetIPProperties() 125 return { 126 "dns_suffix": ip_properties.DnsSuffix, 127 "dns_enabled": ip_properties.IsDnsEnabled, 128 "dynamic_dns_enabled": ip_properties.IsDynamicDnsEnabled, 129 } 130 131 132def _get_ip_unicast_info(i_face): 133 ip_properties = i_face.GetIPProperties() 134 int_dict = {} 135 if ip_properties.UnicastAddresses.Count > 0: 136 names = {af_inet: "ip_addresses", af_inet6: "ipv6_addresses"} 137 for addrs in ip_properties.UnicastAddresses: 138 if addrs.Address.AddressFamily == af_inet: 139 ip = addrs.Address.ToString() 140 mask = addrs.IPv4Mask.ToString() 141 net = ipaddress.IPv4Network(ip + "/" + mask, False) 142 ip_info = { 143 "address": ip, 144 "netmask": mask, 145 "broadcast": net.broadcast_address.compressed, 146 "loopback": addrs.Address.Loopback.ToString(), 147 } 148 else: 149 ip_info = { 150 "address": addrs.Address.ToString().split("%")[0], 151 # ScopeID is a suffix affixed to the end of an IPv6 152 # address it denotes the adapter. This is different from 153 # ScopeLevel. Need to figure out how to get ScopeLevel 154 # for feature parity with Linux 155 "interface_index": int(addrs.Address.ScopeId), 156 } 157 ip_info.update( 158 { 159 "prefix_length": addrs.PrefixLength, 160 "prefix_origin": enum_prefix_suffix[addrs.PrefixOrigin], 161 "suffix_origin": enum_prefix_suffix[addrs.SuffixOrigin], 162 } 163 ) 164 int_dict.setdefault(names[addrs.Address.AddressFamily], []).append(ip_info) 165 return int_dict 166 167 168def _get_ip_gateway_info(i_face): 169 ip_properties = i_face.GetIPProperties() 170 int_dict = {} 171 if ip_properties.GatewayAddresses.Count > 0: 172 names = {af_inet: "ip_gateways", af_inet6: "ipv6_gateways"} 173 for addrs in ip_properties.GatewayAddresses: 174 int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( 175 addrs.Address.ToString().split("%")[0] 176 ) 177 return int_dict 178 179 180def _get_ip_dns_info(i_face): 181 ip_properties = i_face.GetIPProperties() 182 int_dict = {} 183 if ip_properties.DnsAddresses.Count > 0: 184 names = {af_inet: "ip_dns", af_inet6: "ipv6_dns"} 185 for addrs in ip_properties.DnsAddresses: 186 int_dict.setdefault(names[addrs.AddressFamily], []).append( 187 addrs.ToString().split("%")[0] 188 ) 189 return int_dict 190 191 192def _get_ip_multicast_info(i_face): 193 ip_properties = i_face.GetIPProperties() 194 int_dict = {} 195 if ip_properties.MulticastAddresses.Count > 0: 196 names = {af_inet: "ip_multicast", af_inet6: "ipv6_multicast"} 197 for addrs in ip_properties.MulticastAddresses: 198 int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( 199 addrs.Address.ToString().split("%")[0] 200 ) 201 return int_dict 202 203 204def _get_ip_anycast_info(i_face): 205 ip_properties = i_face.GetIPProperties() 206 int_dict = {} 207 if ip_properties.AnycastAddresses.Count > 0: 208 names = {af_inet: "ip_anycast", af_inet6: "ipv6_anycast"} 209 for addrs in ip_properties.AnycastAddresses: 210 int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( 211 addrs.Address.ToString() 212 ) 213 return int_dict 214 215 216def _get_ip_wins_info(i_face): 217 ip_properties = i_face.GetIPProperties() 218 int_dict = {} 219 if ip_properties.WinsServersAddresses.Count > 0: 220 for addrs in ip_properties.WinsServersAddresses: 221 int_dict.setdefault("ip_wins", []).append(addrs.ToString()) 222 return int_dict 223 224 225def _get_network_interfaces(): 226 clr.AddReference("System.Net") 227 return NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() 228 229 230def get_interface_info_dot_net_formatted(): 231 """ 232 Returns data gathered via ``get_interface_info_dot_net`` and returns the 233 info in the same manner as ``get_interface_info_wmi`` 234 235 Returns: 236 dict: A dictionary of information about all interfaces on the system 237 """ 238 # Massage the data returned by dotnet to mirror that returned by wmi 239 interfaces = get_interface_info_dot_net() 240 i_faces = {} 241 for i_face in interfaces: 242 if interfaces[i_face]["status"] == "Up": 243 name = interfaces[i_face]["description"] 244 i_faces.setdefault(name, {}).update( 245 {"hwaddr": interfaces[i_face]["physical_address"], "up": True} 246 ) 247 for ip in interfaces[i_face].get("ip_addresses", []): 248 i_faces[name].setdefault("inet", []).append( 249 { 250 "address": ip["address"], 251 "broadcast": ip["broadcast"], 252 "netmask": ip["netmask"], 253 "gateway": interfaces[i_face].get("ip_gateways", [""])[0], 254 "label": name, 255 } 256 ) 257 for ip in interfaces[i_face].get("ipv6_addresses", []): 258 i_faces[name].setdefault("inet6", []).append( 259 { 260 "address": ip["address"], 261 "gateway": interfaces[i_face].get("ipv6_gateways", [""])[0], 262 # Add prefix length 263 } 264 ) 265 266 return i_faces 267 268 269def get_interface_info_dot_net(): 270 """ 271 Uses .NET 4.0+ to gather Network Interface information. Should only run on 272 Windows systems newer than Windows 7/Server 2008R2 273 274 Returns: 275 dict: A dictionary of information about all interfaces on the system 276 """ 277 interfaces = {} 278 for i_face in _get_network_interfaces(): 279 temp_dict = _get_base_properties(i_face) 280 temp_dict.update(_get_ip_base_properties(i_face)) 281 temp_dict.update(_get_ip_unicast_info(i_face)) 282 temp_dict.update(_get_ip_gateway_info(i_face)) 283 temp_dict.update(_get_ip_dns_info(i_face)) 284 temp_dict.update(_get_ip_multicast_info(i_face)) 285 temp_dict.update(_get_ip_anycast_info(i_face)) 286 temp_dict.update(_get_ip_wins_info(i_face)) 287 interfaces[i_face.Name] = temp_dict 288 289 return interfaces 290 291 292def get_interface_info_wmi(): 293 """ 294 Uses WMI to gather Network Interface information. Should only run on 295 Windows 7/2008 R2 and lower systems. This code was pulled from the 296 ``win_interfaces`` function in ``salt.utils.network`` unchanged. 297 298 Returns: 299 dict: A dictionary of information about all interfaces on the system 300 """ 301 with salt.utils.winapi.Com(): 302 c = wmi.WMI() 303 i_faces = {} 304 for i_face in c.Win32_NetworkAdapterConfiguration(IPEnabled=1): 305 i_faces[i_face.Description] = {} 306 if i_face.MACAddress: 307 i_faces[i_face.Description]["hwaddr"] = i_face.MACAddress 308 if i_face.IPEnabled: 309 i_faces[i_face.Description]["up"] = True 310 for ip in i_face.IPAddress: 311 if "." in ip: 312 if "inet" not in i_faces[i_face.Description]: 313 i_faces[i_face.Description]["inet"] = [] 314 item = {"address": ip, "label": i_face.Description} 315 if i_face.DefaultIPGateway: 316 broadcast = next( 317 (i for i in i_face.DefaultIPGateway if "." in i), "" 318 ) 319 if broadcast: 320 item["broadcast"] = broadcast 321 if i_face.IPSubnet: 322 netmask = next((i for i in i_face.IPSubnet if "." in i), "") 323 if netmask: 324 item["netmask"] = netmask 325 i_faces[i_face.Description]["inet"].append(item) 326 if ":" in ip: 327 if "inet6" not in i_faces[i_face.Description]: 328 i_faces[i_face.Description]["inet6"] = [] 329 item = {"address": ip} 330 if i_face.DefaultIPGateway: 331 broadcast = next( 332 (i for i in i_face.DefaultIPGateway if ":" in i), "" 333 ) 334 if broadcast: 335 item["broadcast"] = broadcast 336 if i_face.IPSubnet: 337 netmask = next((i for i in i_face.IPSubnet if ":" in i), "") 338 if netmask: 339 item["netmask"] = netmask 340 i_faces[i_face.Description]["inet6"].append(item) 341 else: 342 i_faces[i_face.Description]["up"] = False 343 return i_faces 344 345 346def get_interface_info(): 347 """ 348 This function will return network interface information for the system and 349 will use the best method to retrieve that information. Windows 7/2008R2 and 350 below will use WMI. Newer systems will use .NET. 351 Returns: 352 dict: A dictionary of information about the Network interfaces 353 """ 354 # On Windows 7 machines, use WMI as dotnet 4.0 is not available by default 355 if USE_WMI: 356 return get_interface_info_wmi() 357 else: 358 return get_interface_info_dot_net_formatted() 359