// // System.Net.NetworkInformation.NetworkInterface // // Authors: // Gonzalo Paniagua Javier (gonzalo@novell.com) // Atsushi Enomoto (atsushi@ximian.com) // Miguel de Icaza (miguel@novell.com) // Eric Butler (eric@extremeboredom.net) // Marek Habersack (mhabersack@novell.com) // Marek Safar (marek.safar@gmail.com) // Calvin Buckley (calvin@cmpct.info) // // Copyright (c) 2006-2008 Novell, Inc. (http://www.novell.com) // // Copyright (c) 2018 Calvin Buckley // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace System.Net.NetworkInformation { internal class AixNetworkInterfaceAPI : UnixNetworkInterfaceAPI { const int SOCK_DGRAM = 2; // AIX doesn't have getifaddrs, (i does though) so we instead query the painful way via ioctl. For IBM's docs on this, see: // https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.commtrf2/ioctl_socket_control_operations.htm [DllImport("libc", SetLastError = true)] public static extern int socket (AixAddressFamily family, int type, int protocol); [DllImport("libc")] public static extern int close (int fd); // overloads to make usage less painful [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, IntPtr arg); [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, ref int arg); [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, ref AixStructs.ifconf arg); [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, ref AixStructs.ifreq_flags arg); [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, ref AixStructs.ifreq_mtu arg); [DllImport("libc", SetLastError = true)] public static extern int ioctl (int fd, AixIoctlRequest request, ref AixStructs.ifreq_addrin arg); static unsafe void ByteArrayCopy (byte* dst, byte* src, int elements) { for (int i = 0; i < 16; i++) dst[i] = src[i]; } public override NetworkInterface [] GetAllNetworkInterfaces () { var interfaces = new Dictionary (); AixStructs.ifconf ifc; ifc.ifc_len = 0; ifc.ifc_buf = IntPtr.Zero; int sockfd = -1; try { sockfd = socket (AixAddressFamily.AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) throw new SystemException ("socket for SIOCGIFCONF failed"); if (ioctl (sockfd, AixIoctlRequest.SIOCGSIZIFCONF, ref ifc.ifc_len) < 0 || ifc.ifc_len < 1) { throw new SystemException ("ioctl for SIOCGSIZIFCONF failed"); } ifc.ifc_buf = Marshal.AllocHGlobal(ifc.ifc_len); if (ioctl (sockfd, AixIoctlRequest.SIOCGIFCONF, ref ifc) < 0) throw new SystemException ("ioctl for SIOCGIFCONF failed"); // this is required because the buffer is an array of VARIABLE LENGTH structures, so sane marshalling is impossible AixStructs.ifreq ifr; var curPos = ifc.ifc_buf; var endPos = ifc.ifc_buf.ToInt64() + ifc.ifc_len; for (ifr = (AixStructs.ifreq)Marshal.PtrToStructure (curPos, typeof (AixStructs.ifreq)); curPos.ToInt64() < endPos; // name length + sockaddr length (SIOCGIFCONF only deals in those) curPos = curPos + (16 + ifr.ifru_addr.sa_len)) { // update the structure for next increment ifr = (AixStructs.ifreq)Marshal.PtrToStructure (curPos, typeof (AixStructs.ifreq)); // the goods IPAddress address = IPAddress.None; string name = null; int index = -1; byte[] macAddress = null; var type = NetworkInterfaceType.Unknown; unsafe { name = Marshal.PtrToStringAnsi(new IntPtr(ifr.ifr_name)); } if (Enum.IsDefined (typeof (AixAddressFamily), (int)ifr.ifru_addr.sa_family)) { switch ((AixAddressFamily)ifr.ifru_addr.sa_family) { case AixAddressFamily.AF_INET: AixStructs.sockaddr_in sockaddrin = (AixStructs.sockaddr_in)Marshal.PtrToStructure(curPos + 16, typeof (AixStructs.sockaddr_in)); address = new IPAddress (sockaddrin.sin_addr); break; case AixAddressFamily.AF_INET6: AixStructs.sockaddr_in6 sockaddr6 = (AixStructs.sockaddr_in6) Marshal.PtrToStructure(curPos + 16, typeof (AixStructs.sockaddr_in6)); address = new IPAddress (sockaddr6.sin6_addr.u6_addr8, sockaddr6.sin6_scope_id); break; // XXX: i never returns AF_LINK and SIOCGIFCONF under i doesn't return nameindex values; adapt MacOsNetworkInterface for Qp2getifaddrs instead case AixAddressFamily.AF_LINK: AixStructs.sockaddr_dl sockaddrdl = new AixStructs.sockaddr_dl(); sockaddrdl.Read (curPos + 16); macAddress = new byte [(int) sockaddrdl.sdl_alen]; // copy mac address from sdl_data field starting at last index pos of interface name into array macaddress, starting // at index 0 Array.Copy (sockaddrdl.sdl_data, sockaddrdl.sdl_nlen, macAddress, 0, Math.Min (macAddress.Length, sockaddrdl.sdl_data.Length - sockaddrdl.sdl_nlen)); index = sockaddrdl.sdl_index; int hwtype = (int) sockaddrdl.sdl_type; if (Enum.IsDefined (typeof (AixArpHardware), hwtype)) { switch ((AixArpHardware) hwtype) { case AixArpHardware.ETHER: type = NetworkInterfaceType.Ethernet; break; case AixArpHardware.ATM: type = NetworkInterfaceType.Atm; break; case AixArpHardware.SLIP: type = NetworkInterfaceType.Slip; break; case AixArpHardware.PPP: type = NetworkInterfaceType.Ppp; break; case AixArpHardware.LOOPBACK: type = NetworkInterfaceType.Loopback; macAddress = null; break; case AixArpHardware.FDDI: type = NetworkInterfaceType.Fddi; break; } } break; default: break; } } // get flags uint flags = 0; int mtu = 0; unsafe { AixStructs.ifreq_flags ifrFlags = new AixStructs.ifreq_flags (); ByteArrayCopy (ifrFlags.ifr_name, ifr.ifr_name, 16); if (ioctl (sockfd, AixIoctlRequest.SIOCGIFFLAGS, ref ifrFlags) < 0) throw new SystemException("ioctl for SIOCGIFFLAGS failed"); else flags = ifrFlags.ifru_flags; AixStructs.ifreq_mtu ifrMtu = new AixStructs.ifreq_mtu (); ByteArrayCopy (ifrMtu.ifr_name, ifr.ifr_name, 16); if (ioctl (sockfd, AixIoctlRequest.SIOCGIFMTU, ref ifrMtu) < 0) { // it's not the end of the world if we don't get it } else mtu = ifrMtu.ifru_mtu; } AixNetworkInterface iface = null; // create interface if not already present if (!interfaces.TryGetValue (name, out iface)) { iface = new AixNetworkInterface (name, flags, mtu); interfaces.Add (name, iface); } // if a new address has been found, add it if (!address.Equals (IPAddress.None)) iface.AddAddress (address); // set link layer info, if iface has macaddress or is loopback device if (macAddress != null || type == NetworkInterfaceType.Loopback) iface.SetLinkLayerInfo (index, macAddress, type); } } finally { if (ifc.ifc_buf != IntPtr.Zero) Marshal.FreeHGlobal(ifc.ifc_buf); if (sockfd != -1) close (sockfd); } NetworkInterface [] result = new NetworkInterface [interfaces.Count]; int x = 0; foreach (NetworkInterface thisInterface in interfaces.Values) { result [x] = thisInterface; x++; } return result; } public override int GetLoopbackInterfaceIndex () { // XXX: "*LOOPBACK" on i return if_nametoindex ("lo0"); } public override IPAddress GetNetMask (IPAddress address) { AixStructs.ifconf ifc; ifc.ifc_len = 0; ifc.ifc_buf = IntPtr.Zero; int sockfd = -1; try { sockfd = socket (AixAddressFamily.AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) throw new SystemException ("socket for SIOCGIFCONF failed"); if (ioctl (sockfd, AixIoctlRequest.SIOCGSIZIFCONF, ref ifc.ifc_len) < 0 || ifc.ifc_len < 1) throw new SystemException ("ioctl for SIOCGSIZIFCONF failed"); ifc.ifc_buf = Marshal.AllocHGlobal(ifc.ifc_len); if (ioctl (sockfd, AixIoctlRequest.SIOCGIFCONF, ref ifc) < 0) throw new SystemException ("ioctl for SIOCGIFCONF failed"); // this is required because the buffer is an array of VARIABLE LENGTH structures, so sane marshalling is impossible AixStructs.ifreq ifr; var curPos = ifc.ifc_buf; var endPos = ifc.ifc_buf.ToInt64() + ifc.ifc_len; for (ifr = (AixStructs.ifreq)Marshal.PtrToStructure (curPos, typeof (AixStructs.ifreq)); curPos.ToInt64() < endPos; // name length + sockaddr length (SIOCGIFCONF only deals in those) curPos += (16 + ifr.ifru_addr.sa_len)) { // update the structure for next increment ifr = (AixStructs.ifreq)Marshal.PtrToStructure (curPos, typeof (AixStructs.ifreq)); if (Enum.IsDefined (typeof (AixAddressFamily), (int)ifr.ifru_addr.sa_family)) { switch ((AixAddressFamily)ifr.ifru_addr.sa_family) { case AixAddressFamily.AF_INET: AixStructs.sockaddr_in sockaddrin = (AixStructs.sockaddr_in)Marshal.PtrToStructure(curPos + 16, typeof (AixStructs.sockaddr_in)); var saddress = new IPAddress (sockaddrin.sin_addr); if (address.Equals (saddress)) { AixStructs.ifreq_addrin ifrMask = new AixStructs.ifreq_addrin (); unsafe { ByteArrayCopy (ifrMask.ifr_name, ifr.ifr_name, 16); } // there's an IPv6 version of it too, but Mac OS doesn't try this, so if (ioctl (sockfd, AixIoctlRequest.SIOCGIFNETMASK, ref ifrMask) < 0) return new IPAddress(ifrMask.ifru_addr.sin_addr); else throw new SystemException("ioctl for SIOCGIFNETMASK failed"); } break; default: break; } } } } finally { if (ifc.ifc_buf != IntPtr.Zero) Marshal.FreeHGlobal(ifc.ifc_buf); if (sockfd != -1) close (sockfd); } return null; } } sealed class AixNetworkInterface : UnixNetworkInterface { private uint _ifa_flags; private int _ifru_mtu; internal AixNetworkInterface (string name, uint ifa_flags, int ifru_mtu) : base (name) { _ifa_flags = ifa_flags; _ifru_mtu = ifru_mtu; } public override IPInterfaceProperties GetIPProperties () { if (ipproperties == null) ipproperties = new AixIPInterfaceProperties (this, addresses, _ifru_mtu); return ipproperties; } public override IPv4InterfaceStatistics GetIPv4Statistics () { if (ipv4stats == null) ipv4stats = new AixIPv4InterfaceStatistics (this); return ipv4stats; } public override OperationalStatus OperationalStatus { get { if(((AixInterfaceFlags)_ifa_flags & AixInterfaceFlags.IFF_UP) == AixInterfaceFlags.IFF_UP){ return OperationalStatus.Up; } return OperationalStatus.Unknown; } } public override bool SupportsMulticast { get { return ((AixInterfaceFlags)_ifa_flags & AixInterfaceFlags.IFF_MULTICAST) == AixInterfaceFlags.IFF_MULTICAST; } } } }