Merge pull request #4615 from alexanderkyte/string_error_handling
[mono.git] / mcs / class / System / System.Net.NetworkInformation / NetworkInterface.cs
1 //
2 // System.Net.NetworkInformation.NetworkInterface
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //      Atsushi Enomoto (atsushi@ximian.com)
7 //      Miguel de Icaza (miguel@novell.com)
8 //      Eric Butler (eric@extremeboredom.net)
9 //      Marek Habersack (mhabersack@novell.com)
10 //  Marek Safar (marek.safar@gmail.com)
11 //
12 // Copyright (c) 2006-2008 Novell, Inc. (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 using System;
34 using System.Collections.Generic;
35 using System.Collections;
36 using System.Net;
37 using System.Net.Sockets;
38 using System.Runtime.InteropServices;
39 using System.Text;
40 using System.IO;
41 using System.Globalization;
42
43 namespace System.Net.NetworkInformation {
44         static class SystemNetworkInterface {
45
46                 static readonly NetworkInterfaceFactory nif = NetworkInterfaceFactory.Create ();
47
48                 public static NetworkInterface [] GetNetworkInterfaces ()
49                 {
50                         try {
51                                 return nif.GetAllNetworkInterfaces ();
52                         } catch {
53                                 return new NetworkInterface [0];
54                         }
55                 }
56
57                 public static bool InternalGetIsNetworkAvailable ()
58                 {
59                         // TODO:
60                         return true;
61                 }
62
63                 public static int InternalLoopbackInterfaceIndex {
64                         get {
65                                 return nif.GetLoopbackInterfaceIndex ();
66                         }
67                 }
68
69                 public static int InternalIPv6LoopbackInterfaceIndex {
70                         get {
71                                 throw new NotImplementedException ();
72                         }
73                 }
74
75                 public static IPAddress GetNetMask (IPAddress address)
76                 {
77                         return nif.GetNetMask (address);
78                 }
79         }
80
81         abstract class NetworkInterfaceFactory
82         {
83                 internal abstract class UnixNetworkInterfaceAPI : NetworkInterfaceFactory
84                 {
85 #if ORBIS
86                         public static int if_nametoindex(string ifname)
87                         {
88                                 throw new PlatformNotSupportedException ();
89                         }
90
91                         protected static int getifaddrs (out IntPtr ifap)
92                         {
93                                 throw new PlatformNotSupportedException ();
94                         }
95
96                         protected static void freeifaddrs (IntPtr ifap)
97                         {
98                                 throw new PlatformNotSupportedException ();
99                         }
100 #else
101                         [DllImport("libc")]
102                         public static extern int if_nametoindex(string ifname);
103
104                         [DllImport ("libc")]
105                         protected static extern int getifaddrs (out IntPtr ifap);
106
107                         [DllImport ("libc")]
108                         protected static extern void freeifaddrs (IntPtr ifap);
109 #endif
110                 }
111
112                 class MacOsNetworkInterfaceAPI : UnixNetworkInterfaceAPI
113                 {
114                         const int AF_INET  = 2;
115                         const int AF_INET6 = 30;
116                         const int AF_LINK  = 18;
117
118                         public override NetworkInterface [] GetAllNetworkInterfaces ()
119                         {
120                                 var interfaces = new Dictionary <string, MacOsNetworkInterface> ();
121                                 IntPtr ifap;
122                                 if (getifaddrs (out ifap) != 0)
123                                         throw new SystemException ("getifaddrs() failed");
124
125                                 try {
126                                         IntPtr next = ifap;
127                                         while (next != IntPtr.Zero) {
128                                                 MacOsStructs.ifaddrs addr = (MacOsStructs.ifaddrs) Marshal.PtrToStructure (next, typeof (MacOsStructs.ifaddrs));
129                                                 IPAddress address = IPAddress.None;
130                                                 string    name = addr.ifa_name;
131                                                 int       index = -1;
132                                                 byte[]    macAddress = null;
133                                                 NetworkInterfaceType type = NetworkInterfaceType.Unknown;
134
135                                                 if (addr.ifa_addr != IntPtr.Zero) {
136                                                         // optain IPAddress
137                                                         MacOsStructs.sockaddr sockaddr = (MacOsStructs.sockaddr) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr));
138
139                                                         if (sockaddr.sa_family == AF_INET6) {
140                                                                 MacOsStructs.sockaddr_in6 sockaddr6 = (MacOsStructs.sockaddr_in6) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr_in6));
141                                                                 address = new IPAddress (sockaddr6.sin6_addr.u6_addr8, sockaddr6.sin6_scope_id);
142                                                         } else if (sockaddr.sa_family == AF_INET) {
143                                                                 MacOsStructs.sockaddr_in sockaddrin = (MacOsStructs.sockaddr_in) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr_in));
144                                                                 address = new IPAddress (sockaddrin.sin_addr);
145                                                         } else if (sockaddr.sa_family == AF_LINK) {
146                                                                 MacOsStructs.sockaddr_dl sockaddrdl = new MacOsStructs.sockaddr_dl ();
147                                                                 sockaddrdl.Read (addr.ifa_addr);
148
149                                                                 macAddress = new byte [(int) sockaddrdl.sdl_alen];
150                                                                 // copy mac address from sdl_data field starting at last index pos of interface name into array macaddress, starting
151                                                                 // at index 0
152                                                                 Array.Copy (sockaddrdl.sdl_data, sockaddrdl.sdl_nlen, macAddress, 0, Math.Min (macAddress.Length, sockaddrdl.sdl_data.Length - sockaddrdl.sdl_nlen));
153
154                                                                 index = sockaddrdl.sdl_index;
155
156                                                                 int hwtype = (int) sockaddrdl.sdl_type;
157                                                                 if (Enum.IsDefined (typeof (MacOsArpHardware), hwtype)) {
158                                                                         switch ((MacOsArpHardware) hwtype) {
159                                                                                 case MacOsArpHardware.ETHER:
160                                                                                         type = NetworkInterfaceType.Ethernet;
161                                                                                         break;
162
163                                                                                 case MacOsArpHardware.ATM:
164                                                                                         type = NetworkInterfaceType.Atm;
165                                                                                         break;
166                                                                                 
167                                                                                 case MacOsArpHardware.SLIP:
168                                                                                         type = NetworkInterfaceType.Slip;
169                                                                                         break;
170                                                                                 
171                                                                                 case MacOsArpHardware.PPP:
172                                                                                         type = NetworkInterfaceType.Ppp;
173                                                                                         break;
174                                                                                 
175                                                                                 case MacOsArpHardware.LOOPBACK:
176                                                                                         type = NetworkInterfaceType.Loopback;
177                                                                                         macAddress = null;
178                                                                                         break;
179
180                                                                                 case MacOsArpHardware.FDDI:
181                                                                                         type = NetworkInterfaceType.Fddi;
182                                                                                         break;
183                                                                         }
184                                                                 }
185                                                         }
186                                                 }
187
188                                                 MacOsNetworkInterface iface = null;
189
190                                                 // create interface if not already present
191                                                 if (!interfaces.TryGetValue (name, out iface)) {
192                                                         iface = new MacOsNetworkInterface (name, addr.ifa_flags);
193                                                         interfaces.Add (name, iface);
194                                                 }
195
196                                                 // if a new address has been found, add it
197                                                 if (!address.Equals (IPAddress.None))
198                                                         iface.AddAddress (address);
199
200                                                 // set link layer info, if iface has macaddress or is loopback device
201                                                 if (macAddress != null || type == NetworkInterfaceType.Loopback)
202                                                         iface.SetLinkLayerInfo (index, macAddress, type);
203
204                                                 next = addr.ifa_next;
205                                         }
206                                 } finally {
207                                         freeifaddrs (ifap);
208                                 }
209
210                                 NetworkInterface [] result = new NetworkInterface [interfaces.Count];
211                                 int x = 0;
212                                 foreach (NetworkInterface thisInterface in interfaces.Values) {
213                                         result [x] = thisInterface;
214                                         x++;
215                                 }
216                                 return result;
217                         }
218
219                         public override int GetLoopbackInterfaceIndex ()
220                         {
221                                 return if_nametoindex ("lo0");
222                         }
223
224                         public override IPAddress GetNetMask (IPAddress address)
225                         {
226                                 IntPtr ifap;
227                                 if (getifaddrs (out ifap) != 0)
228                                         throw new SystemException ("getifaddrs() failed");
229
230                                 try {
231                                         IntPtr next = ifap;
232                                         while (next != IntPtr.Zero) {
233                                                 MacOsStructs.ifaddrs addr = (MacOsStructs.ifaddrs) Marshal.PtrToStructure (next, typeof (MacOsStructs.ifaddrs));
234
235                                                 if (addr.ifa_addr != IntPtr.Zero) {
236                                                         // optain IPAddress
237                                                         MacOsStructs.sockaddr sockaddr = (MacOsStructs.sockaddr) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr));
238
239                                                         if (sockaddr.sa_family == AF_INET) {
240                                                                 MacOsStructs.sockaddr_in sockaddrin = (MacOsStructs.sockaddr_in) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr_in));
241                                                                 var saddress = new IPAddress (sockaddrin.sin_addr);
242                                                                 if (address.Equals (saddress))
243                                                                         return new IPAddress(((sockaddr_in)Marshal.PtrToStructure(addr.ifa_netmask, typeof(sockaddr_in))).sin_addr);
244                                                         }
245                                                 }
246                                                 next = addr.ifa_next;
247                                         }
248                                 } finally {
249                                         freeifaddrs (ifap);
250                                 }
251
252                                 return null;
253                         }
254                 }
255
256                 class LinuxNetworkInterfaceAPI : UnixNetworkInterfaceAPI
257                 {
258                         const int AF_INET = 2;
259                         const int AF_INET6 = 10;
260                         const int AF_PACKET = 17;
261
262                         static void FreeInterfaceAddresses (IntPtr ifap)
263                         {
264 #if MONODROID
265                                 AndroidPlatform.FreeInterfaceAddresses (ifap);
266 #else
267                                 freeifaddrs (ifap);
268 #endif
269                         }
270
271                         static int GetInterfaceAddresses (out IntPtr ifap)
272                         {
273 #if MONODROID
274                                 return AndroidPlatform.GetInterfaceAddresses (out ifap);
275 #else
276                                 return getifaddrs (out ifap);
277 #endif
278                         }
279
280                         public override NetworkInterface [] GetAllNetworkInterfaces ()
281                         {
282
283                                 var interfaces = new Dictionary <string, LinuxNetworkInterface> ();
284                                 IntPtr ifap;
285                                 if (GetInterfaceAddresses (out ifap) != 0)
286                                         throw new SystemException ("getifaddrs() failed");
287
288                                 try {
289                                         IntPtr next = ifap;
290                                         while (next != IntPtr.Zero) {
291                                                 ifaddrs   addr = (ifaddrs) Marshal.PtrToStructure (next, typeof (ifaddrs));
292                                                 IPAddress address = IPAddress.None;
293                                                 string    name = addr.ifa_name;
294                                                 int       index = -1;
295                                                 byte[]    macAddress = null;
296                                                 NetworkInterfaceType type = NetworkInterfaceType.Unknown;
297                                                 int       nullNameCount = 0;
298
299                                                 if (addr.ifa_addr != IntPtr.Zero) {
300                                                         sockaddr_in sockaddr = (sockaddr_in) Marshal.PtrToStructure (addr.ifa_addr, typeof (sockaddr_in));
301
302                                                         if (sockaddr.sin_family == AF_INET6) {
303                                                                 sockaddr_in6 sockaddr6 = (sockaddr_in6) Marshal.PtrToStructure (addr.ifa_addr, typeof (sockaddr_in6));
304                                                                 address = new IPAddress (sockaddr6.sin6_addr.u6_addr8, sockaddr6.sin6_scope_id);
305                                                         } else if (sockaddr.sin_family == AF_INET) {
306                                                                 address = new IPAddress (sockaddr.sin_addr);
307                                                         } else if (sockaddr.sin_family == AF_PACKET) {
308                                                                 sockaddr_ll sockaddrll = (sockaddr_ll) Marshal.PtrToStructure (addr.ifa_addr, typeof (sockaddr_ll));
309                                                                 if (((int)sockaddrll.sll_halen) > sockaddrll.sll_addr.Length){
310                                                                         Console.Error.WriteLine ("Got a bad hardware address length for an AF_PACKET {0} {1}",
311                                                                                                  sockaddrll.sll_halen, sockaddrll.sll_addr.Length);
312                                                                         next = addr.ifa_next;
313                                                                         continue;
314                                                                 }
315                                                                 
316                                                                 macAddress = new byte [(int) sockaddrll.sll_halen];
317                                                                 Array.Copy (sockaddrll.sll_addr, 0, macAddress, 0, macAddress.Length);
318                                                                 index = sockaddrll.sll_ifindex;
319
320                                                                 int hwtype = (int)sockaddrll.sll_hatype;
321                                                                 if (Enum.IsDefined (typeof (LinuxArpHardware), hwtype)) {
322                                                                         switch ((LinuxArpHardware)hwtype) {
323                                                                                 case LinuxArpHardware.EETHER:
324                                                                                         goto case LinuxArpHardware.ETHER;
325                                                                                         
326                                                                                 case LinuxArpHardware.ETHER:
327                                                                                         type = NetworkInterfaceType.Ethernet;
328                                                                                         break;
329
330                                                                                 case LinuxArpHardware.PRONET:
331                                                                                         type = NetworkInterfaceType.TokenRing;
332                                                                                         break;
333
334                                                                                 case LinuxArpHardware.ATM:
335                                                                                         type = NetworkInterfaceType.Atm;
336                                                                                         break;
337                                                                                 
338                                                                                 case LinuxArpHardware.SLIP:
339                                                                                 case LinuxArpHardware.CSLIP:
340                                                                                 case LinuxArpHardware.SLIP6:
341                                                                                 case LinuxArpHardware.CSLIP6:
342                                                                                         type = NetworkInterfaceType.Slip;
343                                                                                         break;
344                                                                                 
345                                                                                 case LinuxArpHardware.PPP:
346                                                                                         type = NetworkInterfaceType.Ppp;
347                                                                                         break;
348                                                                                 
349                                                                                 case LinuxArpHardware.LOOPBACK:
350                                                                                         type = NetworkInterfaceType.Loopback;
351                                                                                         macAddress = null;
352                                                                                         break;
353
354                                                                                 case LinuxArpHardware.FDDI:
355                                                                                         type = NetworkInterfaceType.Fddi;
356                                                                                         break;
357
358                                                                                 case LinuxArpHardware.SIT:
359                                                                                 case LinuxArpHardware.IPDDP:
360                                                                                 case LinuxArpHardware.IPGRE:
361                                                                                 case LinuxArpHardware.IP6GRE:
362                                                                                 case LinuxArpHardware.TUNNEL6:
363                                                                                 case LinuxArpHardware.TUNNEL:
364                                                                                         type = NetworkInterfaceType.Tunnel;
365                                                                                         break;
366                                                                         }
367                                                                 }
368                                                         }
369                                                 }
370
371                                                 LinuxNetworkInterface iface = null;
372
373                                                 if (String.IsNullOrEmpty (name))
374                                                         name = "\0" + (++nullNameCount).ToString ();
375                                                 
376                                                 if (!interfaces.TryGetValue (name, out iface)) {
377                                                         iface = new LinuxNetworkInterface (name);
378                                                         interfaces.Add (name, iface);
379                                                 }
380
381                                                 if (!address.Equals (IPAddress.None))
382                                                         iface.AddAddress (address);
383
384                                                 if (macAddress != null || type == NetworkInterfaceType.Loopback) {
385                                                         if (type == NetworkInterfaceType.Ethernet) {
386                                                                 if (Directory.Exists(iface.IfacePath + "wireless")) {
387                                                                         type = NetworkInterfaceType.Wireless80211;
388                                                                 }
389                                                         }
390                                                         iface.SetLinkLayerInfo (index, macAddress, type);
391                                                 }
392
393                                                 next = addr.ifa_next;
394                                         }
395                                 } finally {
396                                         FreeInterfaceAddresses (ifap);
397                                 }
398
399                                 NetworkInterface [] result = new NetworkInterface [interfaces.Count];
400                                 int x = 0;
401                                 foreach (NetworkInterface thisInterface in interfaces.Values) {
402                                         result [x] = thisInterface;
403                                         x++;
404                                 }
405                                 return result;
406                         }
407
408                         public override int GetLoopbackInterfaceIndex ()
409                         {
410                                 return if_nametoindex ("lo");
411                         }
412
413                         public override IPAddress GetNetMask (IPAddress address)
414                         {
415                                 foreach (ifaddrs networkInteface in GetNetworkInterfaces()) {
416                                         if (networkInteface.ifa_addr == IntPtr.Zero)
417                                                 continue;
418
419                                         var sockaddr = (sockaddr_in)Marshal.PtrToStructure(networkInteface.ifa_addr, typeof(sockaddr_in));
420
421                                         if (sockaddr.sin_family != AF_INET)
422                                                 continue;
423
424                                         if (!address.Equals(new IPAddress(sockaddr.sin_addr)))
425                                                 continue;
426
427                                         var netmask = (sockaddr_in)Marshal.PtrToStructure(networkInteface.ifa_netmask, typeof(sockaddr_in));
428                                         return new IPAddress(netmask.sin_addr);
429                                 }
430
431                                 return null;
432                         }
433
434                         private static IEnumerable<ifaddrs> GetNetworkInterfaces()
435                         {
436                                 IntPtr ifap = IntPtr.Zero;
437
438                                 try {
439                                         if (GetInterfaceAddresses(out ifap) != 0)
440                                                 yield break;
441
442                                         var next = ifap;
443                                         while (next != IntPtr.Zero) {
444                                                 var addr = (ifaddrs)Marshal.PtrToStructure(next, typeof(ifaddrs));
445                                                 yield return addr;
446                                                 next = addr.ifa_next;
447                                         }
448                                 } finally {
449                                         if (ifap != IntPtr.Zero)
450                                                 FreeInterfaceAddresses(ifap);
451                                 }
452                         }
453                 }
454
455 #if WIN_PLATFORM
456                 class Win32NetworkInterfaceAPI : NetworkInterfaceFactory
457                 {
458                         private const string IPHLPAPI = "iphlpapi.dll";
459
460                         [DllImport (IPHLPAPI, SetLastError = true)]
461                         static extern int GetAdaptersAddresses (uint family, uint flags, IntPtr reserved, IntPtr info, ref int size);
462
463                         [DllImport (IPHLPAPI)]
464                         static extern uint GetBestInterfaceEx (byte[] ipAddress, out int index);
465
466                         static Win32_IP_ADAPTER_ADDRESSES [] GetAdaptersAddresses ()
467                         {
468                                 IntPtr ptr = IntPtr.Zero;
469                                 int len = 0;
470                                 GetAdaptersAddresses (0, 0, IntPtr.Zero, ptr, ref len);
471                                 ptr = Marshal.AllocHGlobal(len);
472                                 int ret = GetAdaptersAddresses (0, 0, IntPtr.Zero, ptr, ref len);
473                                 if (ret != 0)
474                                         throw new NetworkInformationException (ret);
475
476                                 List<Win32_IP_ADAPTER_ADDRESSES> l = new List<Win32_IP_ADAPTER_ADDRESSES> ();
477                                 Win32_IP_ADAPTER_ADDRESSES info;
478                                 for (IntPtr p = ptr; p != IntPtr.Zero; p = info.Next) {
479                                         info = Marshal.PtrToStructure<Win32_IP_ADAPTER_ADDRESSES> (p);
480                                         l.Add (info);
481                                 }
482
483                                 return l.ToArray ();
484                         }
485
486                         public override NetworkInterface [] GetAllNetworkInterfaces ()
487                         {
488         //                      Win32_IP_ADAPTER_INFO [] ai = GetAdaptersInfo ();
489                                 Win32_IP_ADAPTER_ADDRESSES [] aa = GetAdaptersAddresses ();
490                                 NetworkInterface [] ret = new NetworkInterface [aa.Length];
491                                 for (int i = 0; i < ret.Length; i++)
492                                         ret [i] = new Win32NetworkInterface2 (aa [i]);
493                                 return ret;
494                         }
495
496                         private static int GetBestInterfaceForAddress (IPAddress addr) {
497                                 int index;
498                                 SocketAddress address = new SocketAddress (addr);
499                                 int error = (int) GetBestInterfaceEx (address.m_Buffer, out index);
500                                 if (error != 0) {
501                                         throw new NetworkInformationException (error);
502                                 }
503
504                                 return index;
505                         }
506
507                         public override int GetLoopbackInterfaceIndex ()
508                         {
509                                 return GetBestInterfaceForAddress (IPAddress.Loopback);
510                         }
511
512                         public override IPAddress GetNetMask (IPAddress address)
513                         {
514                                 throw new NotImplementedException ();
515                         }
516                 }
517 #endif
518
519                 public abstract NetworkInterface [] GetAllNetworkInterfaces ();
520                 public abstract int GetLoopbackInterfaceIndex ();
521                 public abstract IPAddress GetNetMask (IPAddress address);
522
523                 public static NetworkInterfaceFactory Create ()
524                 {
525 #if MONOTOUCH || XAMMAC
526                         return new MacOsNetworkInterfaceAPI ();
527 #else
528                         bool runningOnUnix = (Environment.OSVersion.Platform == PlatformID.Unix);
529
530                         if (runningOnUnix) {
531                                 if (Platform.IsMacOS || Platform.IsFreeBSD)
532                                         return new MacOsNetworkInterfaceAPI ();
533                                         
534                                 return new LinuxNetworkInterfaceAPI ();
535                         }
536
537 #if WIN_PLATFORM
538                         Version windowsVer51 = new Version (5, 1);
539                         if (Environment.OSVersion.Version >= windowsVer51)
540                                 return new Win32NetworkInterfaceAPI ();
541 #endif
542
543                         throw new NotImplementedException ();
544 #endif
545                 }
546         }
547
548         abstract class UnixNetworkInterface : NetworkInterface
549         {
550
551                 protected IPv4InterfaceStatistics ipv4stats;
552                 protected IPInterfaceProperties ipproperties;
553                 
554                 string               name;
555                 //int                  index;
556                 protected List <IPAddress> addresses;
557                 byte[]               macAddress;
558                 NetworkInterfaceType type;
559                 
560                 internal UnixNetworkInterface (string name)
561                 {
562                         this.name = name;
563                         addresses = new List<IPAddress> ();
564                 }
565
566                 internal void AddAddress (IPAddress address)
567                 {
568                         addresses.Add (address);
569                 }
570
571                 internal void SetLinkLayerInfo (int index, byte[] macAddress, NetworkInterfaceType type)
572                 {
573                         //this.index = index;
574                         this.macAddress = macAddress;
575                         this.type = type;
576                 }
577
578                 public override PhysicalAddress GetPhysicalAddress ()
579                 {
580                         if (macAddress != null)
581                                 return new PhysicalAddress (macAddress);
582                         else
583                                 return PhysicalAddress.None;
584                 }
585
586                 public override bool Supports (NetworkInterfaceComponent networkInterfaceComponent)
587                 {
588                         bool wantIPv4 = networkInterfaceComponent == NetworkInterfaceComponent.IPv4;
589                         bool wantIPv6 = wantIPv4 ? false : networkInterfaceComponent == NetworkInterfaceComponent.IPv6;
590                                 
591                         foreach (IPAddress address in addresses) {
592                                 if (wantIPv4 && address.AddressFamily == AddressFamily.InterNetwork)
593                                         return true;
594                                 else if (wantIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6)
595                                         return true;
596                         }
597                         
598                         return false;
599                 }
600
601                 public override string Description {
602                         get { return name; }
603                 }
604
605                 public override string Id {
606                         get { return name; }
607                 }
608
609                 public override bool IsReceiveOnly {
610                         get { return false; }
611                 }
612
613                 public override string Name {
614                         get { return name; }
615                 }
616                 
617                 public override NetworkInterfaceType NetworkInterfaceType {
618                         get { return type; }
619                 }
620                 
621                 [MonoTODO ("Parse dmesg?")]
622                 public override long Speed {
623                         get {
624                                 // Bits/s
625                                 return 1000000;
626                         }
627                 }
628
629                 internal int NameIndex {
630                         get {
631                                 return NetworkInterfaceFactory.UnixNetworkInterfaceAPI.if_nametoindex (Name);
632                         }
633                 }
634         }
635
636         //
637         // This class needs support from the libsupport.so library to fetch the
638         // data using arch-specific ioctls.
639         //
640         // For this to work, we have to create this on the factory above.
641         //
642         sealed class LinuxNetworkInterface : UnixNetworkInterface
643         {
644                 //NetworkInterfaceType type;
645                 string               iface_path;
646                 string               iface_operstate_path;
647                 string               iface_flags_path;          
648
649 #if MONODROID
650                 [DllImport ("__Internal")]
651                 static extern int _monodroid_get_android_api_level ();
652
653                 [DllImport ("__Internal")]
654                 static extern bool _monodroid_get_network_interface_up_state (string ifname, ref bool is_up);
655
656                 [DllImport ("__Internal")]
657                 static extern bool _monodroid_get_network_interface_supports_multicast (string ifname, ref bool supports_multicast);
658
659                 bool android_use_java_api;
660 #endif
661
662                 internal string IfacePath {
663                         get { return iface_path; }
664                 }
665                 
666                 internal LinuxNetworkInterface (string name)
667                         : base (name)
668                 {
669                         iface_path = "/sys/class/net/" + name + "/";
670                         iface_operstate_path = iface_path + "operstate";
671                         iface_flags_path = iface_path + "flags";
672 #if MONODROID
673                         android_use_java_api = _monodroid_get_android_api_level () >= 24;
674 #endif
675                 }
676
677                 public override IPInterfaceProperties GetIPProperties ()
678                 {
679                         if (ipproperties == null)
680                                 ipproperties = new LinuxIPInterfaceProperties (this, addresses);
681                         return ipproperties;
682                 }
683
684                 public override IPv4InterfaceStatistics GetIPv4Statistics ()
685                 {
686                         if (ipv4stats == null)
687                                 ipv4stats = new LinuxIPv4InterfaceStatistics (this);
688                         return ipv4stats;
689                 }
690
691                 public override OperationalStatus OperationalStatus {
692                         get {
693 #if MONODROID
694                                 if (android_use_java_api) {
695                                         // Starting from API 24 (Android 7 "Nougat") Android restricts access to many
696                                         // files in the /sys filesystem (see https://code.google.com/p/android/issues/detail?id=205565
697                                         // for more information) and therefore we are forced to call into Java API in
698                                         // order to get the information. Alas, what we can obtain in this way is quite
699                                         // limited. In the case of OperationalStatus we can only determine whether the
700                                         // interface is up or down. There is a way to get more detailed information but
701                                         // it requires an instance of the Android Context class which is not available
702                                         // to us here.
703                                         bool is_up = false;
704                                         if (_monodroid_get_network_interface_up_state (Name, ref is_up))
705                                                 return is_up ? OperationalStatus.Up : OperationalStatus.Down;
706                                         else
707                                                 return OperationalStatus.Unknown;
708                                 }
709 #endif
710                                 if (!Directory.Exists (iface_path))
711                                         return OperationalStatus.Unknown;
712                                 
713                                 try {
714                                         string s = ReadLine (iface_operstate_path);
715
716                                         switch (s){
717                                                 case "unknown":
718                                                         return OperationalStatus.Unknown;
719                                                 
720                                                 case "notpresent":
721                                                         return OperationalStatus.NotPresent;
722
723                                                 case "down":
724                                                         return OperationalStatus.Down;
725
726                                                 case "lowerlayerdown":
727                                                         return OperationalStatus.LowerLayerDown;
728
729                                                 case "testing":
730                                                         return OperationalStatus.Testing;
731
732                                                 case "dormant":
733                                                         return OperationalStatus.Dormant;
734
735                                                 case "up":
736                                                         return OperationalStatus.Up;
737                                         }
738                                 } catch {
739                                 }
740                                 return OperationalStatus.Unknown;
741                         }
742                 }
743
744                 public override bool SupportsMulticast {
745                         get {
746 #if MONODROID
747                                 if (android_use_java_api) {
748                                         // Starting from API 24 (Android 7 "Nougat") Android restricts access to many
749                                         // files in the /sys filesystem (see https://code.google.com/p/android/issues/detail?id=205565
750                                         // for more information) and therefore we are forced to call into Java API in
751                                         // order to get the information.
752                                         bool supports_multicast = false;
753                                         _monodroid_get_network_interface_supports_multicast (Name, ref supports_multicast);
754                                         return supports_multicast;
755                                 }
756 #endif
757                                 if (!Directory.Exists (iface_path))
758                                         return false;
759                                 
760                                 try {
761                                         string s = ReadLine (iface_flags_path);
762                                         if (s.Length > 2 && s [0] == '0' && s [1] == 'x')
763                                                 s = s.Substring (2);
764                                         
765                                         ulong f = UInt64.Parse (s, NumberStyles.HexNumber);
766
767                                         // Hardcoded, only useful for Linux.
768                                         return ((f & 0x1000) == 0x1000);
769                                 } catch {
770                                         return false;
771                                 }
772                         }
773                 }
774
775                 internal static string ReadLine (string path)
776                 {
777                         using (FileStream fs = File.OpenRead (path)){
778                                 using (StreamReader sr = new StreamReader (fs)){
779                                         return sr.ReadLine ();
780                                 }
781                         }
782                 }               
783         }
784
785         sealed class MacOsNetworkInterface : UnixNetworkInterface
786         {
787                 private uint _ifa_flags;
788
789                 internal MacOsNetworkInterface (string name, uint ifa_flags)
790                         : base (name)
791                 {
792                         _ifa_flags = ifa_flags;
793                 }
794
795                 public override IPInterfaceProperties GetIPProperties ()
796                 {
797                         if (ipproperties == null)
798                                 ipproperties = new MacOsIPInterfaceProperties (this, addresses);
799                         return ipproperties;
800                 }
801
802                 public override IPv4InterfaceStatistics GetIPv4Statistics ()
803                 {
804                         if (ipv4stats == null)
805                                 ipv4stats = new MacOsIPv4InterfaceStatistics (this);
806                         return ipv4stats;
807                 }
808
809                 public override OperationalStatus OperationalStatus {
810                         get {
811                                 if(((MacOsInterfaceFlags)_ifa_flags & MacOsInterfaceFlags.IFF_UP) == MacOsInterfaceFlags.IFF_UP){
812                                         return OperationalStatus.Up;
813                                 }
814                                 return OperationalStatus.Unknown;
815                         }
816                 }
817
818                 public override bool SupportsMulticast {
819                         get {
820                                 return ((MacOsInterfaceFlags)_ifa_flags & MacOsInterfaceFlags.IFF_MULTICAST) == MacOsInterfaceFlags.IFF_MULTICAST;
821                         }
822                 }
823         }
824
825 #if WIN_PLATFORM
826         class Win32NetworkInterface2 : NetworkInterface
827         {
828                 [DllImport ("iphlpapi.dll", SetLastError = true)]
829                 static extern int GetAdaptersInfo (IntPtr info, ref int size);
830
831                 [DllImport ("iphlpapi.dll", SetLastError = true)]
832                 static extern int GetIfEntry (ref Win32_MIB_IFROW row);
833
834                 public static Win32_IP_ADAPTER_INFO GetAdapterInfoByIndex (int index)
835                 {
836                         foreach (Win32_IP_ADAPTER_INFO info in GetAdaptersInfo ())
837                                 if (info.Index == index)
838                                         return info;
839                         throw new IndexOutOfRangeException ("No adapter found for index " + index);
840                 }
841
842                 static Win32_IP_ADAPTER_INFO [] GetAdaptersInfo ()
843                 {
844                         int len = 0;
845                         IntPtr ptr = IntPtr.Zero;
846                         GetAdaptersInfo (ptr, ref len);
847                         ptr = Marshal.AllocHGlobal(len);
848                         int ret = GetAdaptersInfo (ptr, ref len);
849
850                         if (ret != 0)
851                                 throw new NetworkInformationException (ret);
852
853                         List<Win32_IP_ADAPTER_INFO> l = new List<Win32_IP_ADAPTER_INFO> ();
854                         Win32_IP_ADAPTER_INFO info;
855                         for (IntPtr p = ptr; p != IntPtr.Zero; p = info.Next) {
856                                 info = Marshal.PtrToStructure<Win32_IP_ADAPTER_INFO> (p);
857                                 l.Add (info);
858                         }
859                         return l.ToArray ();
860                 }
861
862                 Win32_IP_ADAPTER_ADDRESSES addr;
863                 Win32_MIB_IFROW mib4, mib6;
864                 Win32IPv4InterfaceStatistics ip4stats;
865                 IPInterfaceProperties ip_if_props;
866
867                 internal Win32NetworkInterface2 (Win32_IP_ADAPTER_ADDRESSES addr)
868                 {
869                         this.addr = addr;
870                         mib4 = default (Win32_MIB_IFROW);
871                         mib4.Index = addr.Alignment.IfIndex;
872                         if (GetIfEntry (ref mib4) != 0)
873                                 mib4.Index = -1; // unavailable;
874                         mib6 = default (Win32_MIB_IFROW);
875                         mib6.Index = addr.Ipv6IfIndex;
876                         if (GetIfEntry (ref mib6) != 0)
877                                 mib6.Index = -1; // unavailable;
878                         ip4stats = new Win32IPv4InterfaceStatistics (mib4);
879                         ip_if_props = new Win32IPInterfaceProperties2 (addr, mib4, mib6);
880                 }
881
882                 public override IPInterfaceProperties GetIPProperties ()
883                 {
884                         return ip_if_props;
885                 }
886
887                 public override IPv4InterfaceStatistics GetIPv4Statistics ()
888                 {
889                         return ip4stats;
890                 }
891
892                 public override PhysicalAddress GetPhysicalAddress ()
893                 {
894                         byte [] bytes = new byte [addr.PhysicalAddressLength];
895                         Array.Copy (addr.PhysicalAddress, 0, bytes, 0, bytes.Length);
896                         return new PhysicalAddress (bytes);
897                 }
898
899                 public override bool Supports (NetworkInterfaceComponent networkInterfaceComponent)
900                 {
901                         switch (networkInterfaceComponent) {
902                         case NetworkInterfaceComponent.IPv4:
903                                 return mib4.Index >= 0;
904                         case NetworkInterfaceComponent.IPv6:
905                                 return mib6.Index >= 0;
906                         }
907                         return false;
908                 }
909
910                 public override string Description {
911                         get { return addr.Description; }
912                 }
913                 public override string Id {
914                         get { return addr.AdapterName; }
915                 }
916                 public override bool IsReceiveOnly {
917                         get { return addr.IsReceiveOnly; }
918                 }
919                 public override string Name {
920                         get { return addr.FriendlyName; }
921                 }
922                 public override NetworkInterfaceType NetworkInterfaceType {
923                         get { return addr.IfType; }
924                 }
925                 public override OperationalStatus OperationalStatus {
926                         get { return addr.OperStatus; }
927                 }
928                 public override long Speed {
929                         get { return mib6.Index >= 0 ? mib6.Speed : mib4.Speed; }
930                 }
931                 public override bool SupportsMulticast {
932                         get { return !addr.NoMulticast; }
933                 }
934         }
935 #endif
936 }
937