update GatewayAddresses() to return IPv6 address on Unix (dotnet/corefx#37132)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Fri, 26 Apr 2019 18:01:37 +0000 (11:01 -0700)
committerGitHub <noreply@github.com>
Fri, 26 Apr 2019 18:01:37 +0000 (11:01 -0700)
* process IPv6 for GatewayAddresses

* fix GatewayAddresses for OSX and FreeBSD

* feedback from review

* feedback from review

* add updated ipv6_route for tests

* feedback from review

Commit migrated from https://github.com/dotnet/corefx/commit/a612f42c3310f4c66abc0aafc9d6a6a55b07334a

src/libraries/Native/Unix/System.Native/pal_interfaceaddresses.c
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceProperties.cs
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdNetworkInterface.cs
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceProperties.cs
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Addresses.cs
src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs
src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AddressParsingTests.cs
src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/ipv6_route

index 3e5d045da1445480ceeb7178d6e8ea0fb2ad90d8..3213fc1b18b0af648e65b9151ce09036912e2489 100644 (file)
@@ -161,7 +161,8 @@ int32_t SystemNative_EnumerateInterfaceAddresses(IPv4AddressFound onIpv4Found,
 #if HAVE_RT_MSGHDR
 int32_t SystemNative_EnumerateGatewayAddressesForInterface(uint32_t interfaceIndex, GatewayAddressFound onGatewayFound)
 {
-    int routeDumpName[] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+    static struct in6_addr anyaddr = IN6ADDR_ANY_INIT;
+    int routeDumpName[] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_DUMP, 0};
 
     size_t byteCount;
 
@@ -195,15 +196,50 @@ int32_t SystemNative_EnumerateGatewayAddressesForInterface(uint32_t interfaceInd
         int isGateway = flags & RTF_GATEWAY;
         int gatewayPresent = hdr->rtm_addrs & RTA_GATEWAY;
 
-        if (isGateway && gatewayPresent)
+        if (isGateway && gatewayPresent && ((int)interfaceIndex == -1 || interfaceIndex == hdr->rtm_index))
         {
             IpAddressInfo iai;
+            struct sockaddr_storage* sock = (struct sockaddr_storage*)(hdr + 1);
             memset(&iai, 0, sizeof(IpAddressInfo));
-            iai.InterfaceIndex = interfaceIndex;
-            iai.NumAddressBytes = NUM_BYTES_IN_IPV4_ADDRESS;
-            struct sockaddr_in* sain = (struct sockaddr_in*)(hdr + 1);
-            sain = sain + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
-            memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain->sin_addr.s_addr, sizeof(sain->sin_addr.s_addr));
+            iai.InterfaceIndex = hdr->rtm_index;
+
+            if (sock->ss_family == AF_INET)
+            {
+                iai.NumAddressBytes = NUM_BYTES_IN_IPV4_ADDRESS;
+                struct sockaddr_in* sain = (struct sockaddr_in*)sock;
+                if (sain->sin_addr.s_addr != 0)
+                {
+                    // filter out normal routes.
+                    continue;
+                }
+
+                sain = sain + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
+                memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain->sin_addr.s_addr, sizeof(sain->sin_addr.s_addr));
+            }
+            else if (sock->ss_family == AF_INET6)
+            {
+                struct sockaddr_in6* sain6 = (struct sockaddr_in6*)sock;
+                iai.NumAddressBytes = NUM_BYTES_IN_IPV6_ADDRESS;
+                if (memcmp(&anyaddr, &sain6->sin6_addr, sizeof(sain6->sin6_addr)) != 0)
+                {
+                    // filter out normal routes.
+                    continue;
+                }
+
+                sain6 = sain6 + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
+                if ((sain6->sin6_addr.__u6_addr.__u6_addr16[0] & htons(0xfe80)) == htons(0xfe80))
+                {
+                    // clear embedded if index.
+                    sain6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
+                }
+
+                memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain6->sin6_addr, sizeof(sain6->sin6_addr));
+            }
+            else
+            {
+                // Ignore other address families.
+                continue;
+            }
             onGatewayFound(&iai);
         }
     }
index d4efea31620b080103347c4bdbd79a22126e5365..dc53e28dee75ec4945dd8691090dfa7a101b1cd1 100644 (file)
@@ -54,6 +54,11 @@ namespace System.Net.NetworkInformation
                         Buffer.MemoryCopy(gatewayAddressInfo->AddressBytes, ipArrayPtr, ipBytes.Length, ipBytes.Length);
                     }
                     IPAddress ipAddress = new IPAddress(ipBytes);
+                    if (ipAddress.IsIPv6LinkLocal)
+                    {
+                        // For Link-Local addresses add ScopeId as that is not part of the route entry.
+                        ipAddress.ScopeId = interfaceIndex;
+                    }
                     addressSet.Add(ipAddress);
                 }) == -1)
             {
index 63d83857c10663506ee52d5d1354ccd02d80d151..25f8867f9b78173e7325caa4e2b1be9b7d8a6be5 100644 (file)
@@ -14,8 +14,9 @@ namespace System.Net.NetworkInformation
         private readonly bool _supportsMulticast;
         private readonly long _speed;
 
-        protected unsafe BsdNetworkInterface(string name) : base(name)
+        protected unsafe BsdNetworkInterface(string name, int index) : base(name)
         {
+            _index = index;
             Interop.Sys.NativeIPInterfaceStatistics nativeStats;
             if (Interop.Sys.GetNativeIPInterfaceStatistics(name, out nativeStats) == -1)
             {
@@ -51,7 +52,7 @@ namespace System.Net.NetworkInformation
                     {
                         try
                         {
-                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
                             oni.ProcessIpv4Address(ipAddr, maskAddr);
                         }
                         catch (Exception e)
@@ -67,7 +68,7 @@ namespace System.Net.NetworkInformation
                     {
                         try
                         {
-                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
                             oni.ProcessIpv6Address(ipAddr, *scopeId);
                         }
                         catch (Exception e)
@@ -83,7 +84,7 @@ namespace System.Net.NetworkInformation
                     {
                         try
                         {
-                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+                            BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, llAddr->InterfaceIndex);
                             oni.ProcessLinkLayerAddress(llAddr);
                         }
                         catch (Exception e)
@@ -118,13 +119,14 @@ namespace System.Net.NetworkInformation
         /// </summary>
         /// <param name="interfaces">The Dictionary of existing interfaces.</param>
         /// <param name="name">The name of the interface.</param>
+        /// <param name="index">Interface index of the interface.</param>
         /// <returns>The cached or new BsdNetworkInterface with the given name.</returns>
-        private static BsdNetworkInterface GetOrCreate(Dictionary<string, BsdNetworkInterface> interfaces, string name)
+        private static BsdNetworkInterface GetOrCreate(Dictionary<string, BsdNetworkInterface> interfaces, string name, int index)
         {
             BsdNetworkInterface oni;
             if (!interfaces.TryGetValue(name, out oni))
             {
-                oni = new BsdNetworkInterface(name);
+                oni = new BsdNetworkInterface(name, index);
                 interfaces.Add(name, oni);
             }
 
index ba7f5e0b2f5b1fa33752664db79c0f8a495ab00c..d18953caea363ba1fd8b622308696cdfd47594b3 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections.Generic;
+using System.IO;
 
 namespace System.Net.NetworkInformation
 {
@@ -50,9 +51,19 @@ namespace System.Net.NetworkInformation
         // and separates the information about by each interface.
         public GatewayIPAddressInformationCollection GetGatewayAddresses()
         {
-            List<GatewayIPAddressInformation> innerCollection
-                = StringParsingHelpers.ParseGatewayAddressesFromRouteFile(NetworkFiles.Ipv4RouteFile, _linuxNetworkInterface.Name);
-            return new GatewayIPAddressInformationCollection(innerCollection);
+            List<GatewayIPAddressInformation> collection = new List<GatewayIPAddressInformation>();
+
+            if (File.Exists(NetworkFiles.Ipv4RouteFile))
+            {
+                StringParsingHelpers.ParseIPv4GatewayAddressesFromRouteFile(collection, NetworkFiles.Ipv4RouteFile, _linuxNetworkInterface.Name);
+            }
+
+            if (File.Exists(NetworkFiles.Ipv6RouteFile))
+            {
+                StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(collection, NetworkFiles.Ipv6RouteFile, _linuxNetworkInterface.Name, _linuxNetworkInterface.Index);
+            }
+
+            return new GatewayIPAddressInformationCollection(collection);
         }
 
         private IPAddressCollection GetDhcpServerAddresses()
index 0d9dc1d7e6888d4c676c8419e89714830218ef13..dcd9c75f1c5f40829cb91a704d3f82874f35a2ed 100644 (file)
@@ -18,8 +18,9 @@ namespace System.Net.NetworkInformation
         private readonly long? _speed;
         private readonly LinuxIPInterfaceProperties _ipProperties;
 
-        internal LinuxNetworkInterface(string name) : base(name)
+        internal LinuxNetworkInterface(string name, int index) : base(name)
         {
+            _index = index;
             _operationalStatus = GetOperationalStatus(name);
             _supportsMulticast = GetSupportsMulticast(name);
             _speed = GetSpeed(name);
@@ -31,17 +32,18 @@ namespace System.Net.NetworkInformation
             Dictionary<string, LinuxNetworkInterface> interfacesByName = new Dictionary<string, LinuxNetworkInterface>();
             List<Exception> exceptions = null;
             const int MaxTries = 3;
+
             for (int attempt = 0; attempt < MaxTries; attempt++)
             {
                 // Because these callbacks are executed in a reverse-PInvoke, we do not want any exceptions
-                // to propogate out, because they will not be catchable. Instead, we track all the exceptions
+                // to propagate out, because they will not be catchable. Instead, we track all the exceptions
                 // that are thrown in these callbacks, and aggregate them at the end.
                 int result = Interop.Sys.EnumerateInterfaceAddresses(
                     (name, ipAddr, maskAddr) =>
                     {
                         try
                         {
-                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
                             lni.ProcessIpv4Address(ipAddr, maskAddr);
                         }
                         catch (Exception e)
@@ -57,7 +59,7 @@ namespace System.Net.NetworkInformation
                     {
                         try
                         {
-                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
                             lni.ProcessIpv6Address(ipAddr, *scopeId);
                         }
                         catch (Exception e)
@@ -73,7 +75,7 @@ namespace System.Net.NetworkInformation
                     {
                         try
                         {
-                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+                            LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, llAddr->InterfaceIndex);
                             lni.ProcessLinkLayerAddress(llAddr);
                         }
                         catch (Exception e)
@@ -108,13 +110,14 @@ namespace System.Net.NetworkInformation
         /// </summary>
         /// <param name="interfaces">The Dictionary of existing interfaces.</param>
         /// <param name="name">The name of the interface.</param>
+        /// <param name="index">Interafce index of the interface.</param>
         /// <returns>The cached or new LinuxNetworkInterface with the given name.</returns>
-        private static LinuxNetworkInterface GetOrCreate(Dictionary<string, LinuxNetworkInterface> interfaces, string name)
+        private static LinuxNetworkInterface GetOrCreate(Dictionary<string, LinuxNetworkInterface> interfaces, string name, int index)
         {
             LinuxNetworkInterface lni;
             if (!interfaces.TryGetValue(name, out lni))
             {
-                lni = new LinuxNetworkInterface(name);
+                lni = new LinuxNetworkInterface(name, index);
                 interfaces.Add(name, lni);
             }
 
index ee8fa790739141bc1e92cb9e6ab87bcfedd1875d..7dfcf1e91b43daa3ab359148ad73d1778e2dab62 100644 (file)
@@ -9,16 +9,11 @@ namespace System.Net.NetworkInformation
 {
     internal static partial class StringParsingHelpers
     {
+        private static char[] s_delimiter = new char[1] { ' ' };
         // /proc/net/route contains some information about gateway addresses,
         // and separates the information about by each interface.
-        internal static List<GatewayIPAddressInformation> ParseGatewayAddressesFromRouteFile(string filePath, string interfaceName)
+        internal static List<GatewayIPAddressInformation> ParseIPv4GatewayAddressesFromRouteFile(List<GatewayIPAddressInformation> collection, string filePath, string interfaceName)
         {
-            if (!File.Exists(filePath))
-            {
-                throw ExceptionHelper.CreateForInformationUnavailable();
-            }
-
-            List<GatewayIPAddressInformation> collection = new List<GatewayIPAddressInformation>();
             // Columns are as follows (first-line header):
             // Iface  Destination  Gateway  Flags  RefCnt  Use  Metric  Mask  MTU  Window  IRTT
             string[] fileLines = File.ReadAllLines(filePath);
@@ -31,14 +26,61 @@ namespace System.Net.NetworkInformation
                     parser.MoveNextOrFail();
                     string gatewayIPHex = parser.MoveAndExtractNext();
                     long addressValue = Convert.ToInt64(gatewayIPHex, 16);
-                    IPAddress address = new IPAddress(addressValue);
-                    collection.Add(new SimpleGatewayIPAddressInformation(address));
+                    if (addressValue != 0)
+                    {
+                        // Skip device routes without valid NextHop IP address.
+                        IPAddress address = new IPAddress(addressValue);
+                        collection.Add(new SimpleGatewayIPAddressInformation(address));
+                    }
                 }
             }
 
             return collection;
         }
 
+        internal static void ParseIPv6GatewayAddressesFromRouteFile(List<GatewayIPAddressInformation> collection, string filePath, string interfaceName, long scopeId)
+        {
+            // Columns are as follows (first-line header):
+            // 00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 00000001 00200200 lo
+            // +------------------------------+ ++ +------------------------------+ ++ +------------------------------+ +------+ +------+ +------+ +------+ ++
+            // |                                |  |                                |  |                                |        |        |        |        |
+            // 0                                1  2                                3  4                                5        6        7        8        9
+            //
+            // 0. IPv6 destination network displayed in 32 hexadecimal chars without colons as separator
+            // 1. IPv6 destination prefix length in hexadecimal
+            // 2. IPv6 source network displayed in 32 hexadecimal chars without colons as separator
+            // 3. IPv6 source prefix length in hexadecimal
+            // 4. IPv6 next hop displayed in 32 hexadecimal chars without colons as separator
+            // 5. Metric in hexadecimal
+            // 6. Reference counter
+            // 7. Use counter
+            // 8. Flags
+            // 9. Interface name
+            string[] fileLines = File.ReadAllLines(filePath);
+            foreach (string line in fileLines)
+            {
+                if (line.StartsWith("00000000000000000000000000000000"))
+                {
+                   string[] token = line.Split(s_delimiter, StringSplitOptions.RemoveEmptyEntries);
+                   if (token.Length > 9 && token[4] != "00000000000000000000000000000000")
+                   {
+                        if (!string.IsNullOrEmpty(interfaceName) && interfaceName != token[9])
+                        {
+                            continue;
+                        }
+
+                        IPAddress address = ParseIPv6HexString(token[4], isNetworkOrder: true);
+                        if (address.IsIPv6LinkLocal)
+                        {
+                            // For Link-Local addresses add ScopeId as that is not part of the route entry.
+                            address.ScopeId = scopeId;
+                        }
+                        collection.Add(new SimpleGatewayIPAddressInformation(address));
+                    }
+                }
+            }
+        }
+
         internal static List<IPAddress> ParseDhcpServerAddressesFromLeasesFile(string filePath, string name)
         {
             // Parse the /var/lib/dhcp/dhclient.leases file, if it exists.
index 7dd40105cebb7444fb6f1efc8d0a3065c42f0212..edbdcb96ec692971d9498958e0c7324fffa22900 100644 (file)
@@ -287,24 +287,36 @@ namespace System.Net.NetworkInformation
         }
 
         // Parses a 128-bit IPv6 Address stored as 4 concatenated 32-bit hex numbers.
+        // If isSequence is true it assumes that hexAddress is in sequence of IPv6 bytes.
         // First number corresponds to lower address part
         // E.g. IP-address:                           fe80::215:5dff:fe00:402
         //      It's bytes in direct order:           FE-80-00-00  00-00-00-00  02-15-5D-FF  FE-00-04-02
         //      It's represenation in /proc/net/tcp6: 00-00-80-FE  00-00-00-00  FF-5D-15-02  02-04-00-FE
         //                                             (dashes and spaces added above for readability)
         // Strings passed to this must be 32 characters in length.
-        private static IPAddress ParseIPv6HexString(string hexAddress)
+        private static IPAddress ParseIPv6HexString(string hexAddress, bool isNetworkOrder = false)
         {
             Debug.Assert(hexAddress.Length == 32);
             byte[] addressBytes = new byte[16];
-            for (int i = 0; i < 4; i++)
+            if (isNetworkOrder)
             {
-                for (int j = 0; j < 4; j++)
+                for (int i = 0; i < 16; i++)
                 {
-                    int srcIndex = i * 4 + 3 - j;
-                    int targetIndex = i * 4 + j;
-                    addressBytes[targetIndex] = (byte)(HexToByte(hexAddress[srcIndex * 2]) * 16
-                                                     + HexToByte(hexAddress[srcIndex * 2 + 1]));
+                    addressBytes[i] = (byte)(HexToByte(hexAddress[(i * 2)]) * 16
+                                           + HexToByte(hexAddress[(i * 2) + 1]));
+                }
+            }
+            else
+            {
+                for (int i = 0; i < 4; i++)
+                {
+                    for (int j = 0; j < 4; j++)
+                    {
+                        int srcIndex = i * 4 + 3 - j;
+                        int targetIndex = i * 4 + j;
+                        addressBytes[targetIndex] = (byte)(HexToByte(hexAddress[srcIndex * 2]) * 16
+                                                         + HexToByte(hexAddress[srcIndex * 2 + 1]));
+                    }
                 }
             }
 
index 9ab0494d9bfeb1946a18333fb095b2474f1763a4..9c305b464a1a47a6c3dff30e1dcf20b4b3d7210f 100644 (file)
@@ -25,11 +25,12 @@ namespace System.Net.NetworkInformation.Tests
         }
 
         [Fact]
-        public void GatewayAddressParsing()
+        public void IPv4GatewayAddressParsing()
         {
             string fileName = GetTestFilePath();
             FileUtil.NormalizeLineEndings("NetworkFiles/route", fileName);
-            List<GatewayIPAddressInformation> gatewayAddresses = StringParsingHelpers.ParseGatewayAddressesFromRouteFile(fileName, "wlan0");
+            List<GatewayIPAddressInformation> gatewayAddresses = new List<GatewayIPAddressInformation>();
+            StringParsingHelpers.ParseIPv4GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "wlan0");
             Assert.Equal(3, gatewayAddresses.Count);
 
             Assert.Equal(IPAddress.Parse("10.105.128.1"), gatewayAddresses[0].Address);
@@ -37,6 +38,33 @@ namespace System.Net.NetworkInformation.Tests
             Assert.Equal(IPAddress.Parse("152.186.220.254"), gatewayAddresses[2].Address);
         }
 
+        [Fact]
+        public void IPv6GatewayAddressParsing()
+        {
+            string fileName = GetTestFilePath();
+            FileUtil.NormalizeLineEndings("NetworkFiles/ipv6_route", fileName);
+            List<GatewayIPAddressInformation> gatewayAddresses = new List<GatewayIPAddressInformation>();
+            StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "lo", 42);
+            Assert.Equal(0, gatewayAddresses.Count);
+
+            StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "foo", 42);
+            Assert.Equal(0, gatewayAddresses.Count);
+
+            StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "enp0s5", 42);
+            Assert.Equal(2, gatewayAddresses.Count);
+
+            Assert.Equal(IPAddress.Parse("2002:2c26:f4e4:0:21c:42ff:fe20:4636"), gatewayAddresses[0].Address);
+            Assert.Equal(IPAddress.Parse("fe80::21c:42ff:fe00:18%42"), gatewayAddresses[1].Address);
+
+            gatewayAddresses = new List<GatewayIPAddressInformation>();
+            StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "wlan0", 21);
+            Assert.Equal(IPAddress.Parse("fe80::21c:42ff:fe00:18%21"), gatewayAddresses[0].Address);
+
+            gatewayAddresses = new List<GatewayIPAddressInformation>();
+            StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, null, 0);
+            Assert.Equal(3, gatewayAddresses.Count);
+        }
+
         [Fact]
         public void DhcpServerAddressParsing()
         {
index 7c274818fd2532fd20ec54bbb20241e2a6c809a0..c75533a044370d33eb99c3be7a867eb7557cb81f 100644 (file)
@@ -4,3 +4,6 @@ fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000
 fe80000000000000aefdcefffe7ca67d 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000001 00000000 80200001       lo
 ff000000000000000000000000000000 08 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001    wlan0
 00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0000000f 00200200       lo
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 20022c26f4e40000021c42fffe204636 00000400 00000002 00000087 00450003   enp0s5
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021c42fffe000018 00000400 00000002 00000087 00450003 enp0s5
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021c42fffe000018 00000400 00000002 00000087 00450003      wlan0