• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.net.module.util.netlink;
18 
19 import static android.os.Process.INVALID_UID;
20 import static android.system.OsConstants.AF_INET;
21 import static android.system.OsConstants.AF_INET6;
22 import static android.system.OsConstants.ENOENT;
23 import static android.system.OsConstants.IPPROTO_TCP;
24 import static android.system.OsConstants.IPPROTO_UDP;
25 import static android.system.OsConstants.NETLINK_INET_DIAG;
26 
27 import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
28 import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DESTROY;
29 import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
30 import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
31 import static com.android.net.module.util.netlink.NetlinkConstants.stringForAddressFamily;
32 import static com.android.net.module.util.netlink.NetlinkConstants.stringForProtocol;
33 import static com.android.net.module.util.netlink.NetlinkUtils.DEFAULT_RECV_BUFSIZE;
34 import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
35 import static com.android.net.module.util.netlink.NetlinkUtils.TCP_ALIVE_STATE_FILTER;
36 import static com.android.net.module.util.netlink.NetlinkUtils.connectSocketToNetlink;
37 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
38 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
39 
40 import android.net.util.SocketUtils;
41 import android.os.Process;
42 import android.os.SystemClock;
43 import android.system.ErrnoException;
44 import android.util.Log;
45 import android.util.Range;
46 
47 import androidx.annotation.NonNull;
48 import androidx.annotation.Nullable;
49 import androidx.annotation.VisibleForTesting;
50 
51 import java.io.FileDescriptor;
52 import java.io.IOException;
53 import java.io.InterruptedIOException;
54 import java.net.Inet4Address;
55 import java.net.Inet6Address;
56 import java.net.InetAddress;
57 import java.net.InetSocketAddress;
58 import java.net.SocketException;
59 import java.net.UnknownHostException;
60 import java.nio.ByteBuffer;
61 import java.nio.ByteOrder;
62 import java.util.List;
63 import java.util.Set;
64 import java.util.function.Predicate;
65 
66 /**
67  * A NetlinkMessage subclass for netlink inet_diag messages.
68  *
69  * see also: <linux_src>/include/uapi/linux/inet_diag.h
70  *
71  * @hide
72  */
73 public class InetDiagMessage extends NetlinkMessage {
74     public static final String TAG = "InetDiagMessage";
75     private static final int TIMEOUT_MS = 500;
76 
77     /**
78      * Construct an inet_diag_req_v2 message. This method will throw
79      * {@link IllegalArgumentException} if local and remote are not both null or both non-null.
80      */
inetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote, int family, short flags)81     public static byte[] inetDiagReqV2(int protocol, InetSocketAddress local,
82             InetSocketAddress remote, int family, short flags) {
83         return inetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
84                 0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
85     }
86 
87     /**
88      * Construct an inet_diag_req_v2 message. This method will throw
89      * {@code IllegalArgumentException} if local and remote are not both null or both non-null.
90      *
91      * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
92      *                 IPPROTO_UDP, or IPPROTO_UDPLITE.
93      * @param local local socket address of the target socket. This will be packed into a
94      *              {@link StructInetDiagSockId}. Request to diagnose for all sockets if both of
95      *              local or remote address is null.
96      * @param remote remote socket address of the target socket. This will be packed into a
97      *              {@link StructInetDiagSockId}. Request to diagnose for all sockets if both of
98      *              local or remote address is null.
99      * @param family the ip family of the request message. This should be set to either AF_INET or
100      *               AF_INET6 for IPv4 or IPv6 sockets respectively.
101      * @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h.
102      * @param pad for raw socket protocol specification.
103      * @param idiagExt a set of flags defining what kind of extended information to report.
104      * @param state a bit mask that defines a filter of socket states.
105      *
106      * @return bytes array representation of the message
107      */
inetDiagReqV2(int protocol, @Nullable InetSocketAddress local, @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt, int state)108     public static byte[] inetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
109             @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
110             int state) throws IllegalArgumentException {
111         // Request for all sockets if no specific socket is requested. Specify the local and remote
112         // socket address information for target request socket.
113         if ((local == null) != (remote == null)) {
114             throw new IllegalArgumentException(
115                     "Local and remote must be both null or both non-null");
116         }
117         final StructInetDiagSockId id = ((local != null && remote != null)
118                 ? new StructInetDiagSockId(local, remote) : null);
119         return inetDiagReqV2(protocol, id, family,
120                 SOCK_DIAG_BY_FAMILY, flags, pad, idiagExt, state);
121     }
122 
123     /**
124      * Construct an inet_diag_req_v2 message.
125      *
126      * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
127      *                 IPPROTO_UDP, or IPPROTO_UDPLITE.
128      * @param id inet_diag_sockid. See {@link StructInetDiagSockId}
129      * @param family the ip family of the request message. This should be set to either AF_INET or
130      *               AF_INET6 for IPv4 or IPv6 sockets respectively.
131      * @param type message types.
132      * @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h.
133      * @param pad for raw socket protocol specification.
134      * @param idiagExt a set of flags defining what kind of extended information to report.
135      * @param state a bit mask that defines a filter of socket states.
136      * @return bytes array representation of the message
137      */
inetDiagReqV2(int protocol, @Nullable StructInetDiagSockId id, int family, short type, short flags, int pad, int idiagExt, int state)138     public static byte[] inetDiagReqV2(int protocol, @Nullable StructInetDiagSockId id, int family,
139             short type, short flags, int pad, int idiagExt, int state) {
140         final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
141         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
142         byteBuffer.order(ByteOrder.nativeOrder());
143 
144         final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
145         nlMsgHdr.nlmsg_len = bytes.length;
146         nlMsgHdr.nlmsg_type = type;
147         nlMsgHdr.nlmsg_flags = flags;
148         nlMsgHdr.pack(byteBuffer);
149         final StructInetDiagReqV2 inetDiagReqV2 =
150                 new StructInetDiagReqV2(protocol, id, family, pad, idiagExt, state);
151 
152         inetDiagReqV2.pack(byteBuffer);
153         return bytes;
154     }
155 
156     public StructInetDiagMsg inetDiagMsg;
157 
158     @VisibleForTesting
InetDiagMessage(@onNull StructNlMsgHdr header)159     public InetDiagMessage(@NonNull StructNlMsgHdr header) {
160         super(header);
161         inetDiagMsg = new StructInetDiagMsg();
162     }
163 
164     /**
165      * Parse an inet_diag_req_v2 message from buffer.
166      */
167     @Nullable
parse(@onNull StructNlMsgHdr header, @NonNull ByteBuffer byteBuffer)168     public static InetDiagMessage parse(@NonNull StructNlMsgHdr header,
169             @NonNull ByteBuffer byteBuffer) {
170         final InetDiagMessage msg = new InetDiagMessage(header);
171         msg.inetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
172         if (msg.inetDiagMsg == null) {
173             return null;
174         }
175         return msg;
176     }
177 
closeSocketQuietly(final FileDescriptor fd)178     private static void closeSocketQuietly(final FileDescriptor fd) {
179         try {
180             SocketUtils.closeSocket(fd);
181         } catch (IOException ignored) {
182         }
183     }
184 
lookupUidByFamily(int protocol, InetSocketAddress local, InetSocketAddress remote, int family, short flags, FileDescriptor fd)185     private static int lookupUidByFamily(int protocol, InetSocketAddress local,
186                                          InetSocketAddress remote, int family, short flags,
187                                          FileDescriptor fd)
188             throws ErrnoException, InterruptedIOException {
189         byte[] msg = inetDiagReqV2(protocol, local, remote, family, flags);
190         NetlinkUtils.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
191         ByteBuffer response = NetlinkUtils.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
192 
193         final NetlinkMessage nlMsg = NetlinkMessage.parse(response, NETLINK_INET_DIAG);
194         if (nlMsg == null) {
195             return INVALID_UID;
196         }
197         final StructNlMsgHdr hdr = nlMsg.getHeader();
198         if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
199             return INVALID_UID;
200         }
201         if (nlMsg instanceof InetDiagMessage) {
202             return ((InetDiagMessage) nlMsg).inetDiagMsg.idiag_uid;
203         }
204         return INVALID_UID;
205     }
206 
207     private static final int[] FAMILY = {AF_INET6, AF_INET};
208 
lookupUid(int protocol, InetSocketAddress local, InetSocketAddress remote, FileDescriptor fd)209     private static int lookupUid(int protocol, InetSocketAddress local,
210                                  InetSocketAddress remote, FileDescriptor fd)
211             throws ErrnoException, InterruptedIOException {
212         int uid;
213 
214         for (int family : FAMILY) {
215             /**
216              * For exact match lookup, swap local and remote for UDP lookups due to kernel
217              * bug which will not be fixed. See aosp/755889 and
218              * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
219              */
220             if (protocol == IPPROTO_UDP) {
221                 uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
222             } else {
223                 uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
224             }
225             if (uid != INVALID_UID) {
226                 return uid;
227             }
228         }
229 
230         /**
231          * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
232          * socket is not connected (and even if the socket is connected to a different destination).
233          * If we want this API to work for such packets, then on miss we need to do a second lookup
234          * with only the local address and port filled in.
235          * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
236          */
237         if (protocol == IPPROTO_UDP) {
238             try {
239                 InetSocketAddress wildcard = new InetSocketAddress(
240                         Inet6Address.getByName("::"), 0);
241                 uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
242                         (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
243                 if (uid != INVALID_UID) {
244                     return uid;
245                 }
246                 wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
247                 uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
248                         (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
249                 if (uid != INVALID_UID) {
250                     return uid;
251                 }
252             } catch (UnknownHostException e) {
253                 Log.e(TAG, e.toString());
254             }
255         }
256         return INVALID_UID;
257     }
258 
259     /**
260      * Use an inet_diag socket to look up the UID associated with the input local and remote
261      * address/port and protocol of a connection.
262      */
getConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote)263     public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
264                                             InetSocketAddress remote) {
265         int uid = INVALID_UID;
266         FileDescriptor fd = null;
267         try {
268             fd = NetlinkUtils.netlinkSocketForProto(NETLINK_INET_DIAG);
269             NetlinkUtils.connectSocketToNetlink(fd);
270             uid = lookupUid(protocol, local, remote, fd);
271         } catch (ErrnoException | SocketException | IllegalArgumentException
272                 | InterruptedIOException e) {
273             Log.e(TAG, e.toString());
274         } finally {
275             closeSocketQuietly(fd);
276         }
277         return uid;
278     }
279 
280     /**
281      * Construct an inet_diag_req_v2 message for querying alive TCP sockets from kernel.
282      */
buildInetDiagReqForAliveTcpSockets(int family)283     public static byte[] buildInetDiagReqForAliveTcpSockets(int family) {
284         return inetDiagReqV2(IPPROTO_TCP,
285                 null /* local addr */,
286                 null /* remote addr */,
287                 family,
288                 (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP) /* flag */,
289                 0 /* pad */,
290                 1 << NetlinkConstants.INET_DIAG_MEMINFO /* idiagExt */,
291                 TCP_ALIVE_STATE_FILTER);
292     }
293 
sendNetlinkDestroyRequest(FileDescriptor fd, int proto, InetDiagMessage diagMsg)294     private static void sendNetlinkDestroyRequest(FileDescriptor fd, int proto,
295             InetDiagMessage diagMsg) throws InterruptedIOException, ErrnoException {
296         final byte[] destroyMsg = InetDiagMessage.inetDiagReqV2(
297                 proto,
298                 diagMsg.inetDiagMsg.id,
299                 diagMsg.inetDiagMsg.idiag_family,
300                 SOCK_DESTROY,
301                 (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK),
302                 0 /* pad */,
303                 0 /* idiagExt */,
304                 1 << diagMsg.inetDiagMsg.idiag_state
305         );
306         NetlinkUtils.sendMessage(fd, destroyMsg, 0, destroyMsg.length, IO_TIMEOUT_MS);
307         NetlinkUtils.receiveNetlinkAck(fd);
308     }
309 
sendNetlinkDumpRequest(FileDescriptor fd, int proto, int states, int family)310     private static void sendNetlinkDumpRequest(FileDescriptor fd, int proto, int states, int family)
311             throws InterruptedIOException, ErrnoException {
312         final byte[] dumpMsg = InetDiagMessage.inetDiagReqV2(
313                 proto,
314                 null /* id */,
315                 family,
316                 SOCK_DIAG_BY_FAMILY,
317                 (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP),
318                 0 /* pad */,
319                 0 /* idiagExt */,
320                 states);
321         NetlinkUtils.sendMessage(fd, dumpMsg, 0, dumpMsg.length, IO_TIMEOUT_MS);
322     }
323 
processNetlinkDumpAndDestroySockets(FileDescriptor dumpFd, FileDescriptor destroyFd, int proto, Predicate<InetDiagMessage> filter)324     private static int processNetlinkDumpAndDestroySockets(FileDescriptor dumpFd,
325             FileDescriptor destroyFd, int proto, Predicate<InetDiagMessage> filter)
326             throws InterruptedIOException, ErrnoException {
327         int destroyedSockets = 0;
328 
329         while (true) {
330             final ByteBuffer buf = NetlinkUtils.recvMessage(
331                     dumpFd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
332 
333             while (buf.remaining() > 0) {
334                 final int position = buf.position();
335                 final NetlinkMessage nlMsg = NetlinkMessage.parse(buf, NETLINK_INET_DIAG);
336                 if (nlMsg == null) {
337                     // Move to the position where parse started for error log.
338                     buf.position(position);
339                     Log.e(TAG, "Failed to parse netlink message: " + hexify(buf));
340                     break;
341                 }
342 
343                 if (nlMsg.getHeader().nlmsg_type == NLMSG_DONE) {
344                     return destroyedSockets;
345                 }
346 
347                 if (!(nlMsg instanceof InetDiagMessage)) {
348                     Log.wtf(TAG, "Received unexpected netlink message: " + nlMsg);
349                     continue;
350                 }
351 
352                 final InetDiagMessage diagMsg = (InetDiagMessage) nlMsg;
353                 if (filter.test(diagMsg)) {
354                     try {
355                         sendNetlinkDestroyRequest(destroyFd, proto, diagMsg);
356                         destroyedSockets++;
357                     } catch (InterruptedIOException | ErrnoException e) {
358                         if (!(e instanceof ErrnoException
359                                 && ((ErrnoException) e).errno == ENOENT)) {
360                             Log.e(TAG, "Failed to destroy socket: diagMsg=" + diagMsg + ", " + e);
361                         }
362                     }
363                 }
364             }
365         }
366     }
367 
368     /**
369      * Returns whether the InetDiagMessage is for adb socket or not
370      */
371     @VisibleForTesting
isAdbSocket(final InetDiagMessage msg)372     public static boolean isAdbSocket(final InetDiagMessage msg) {
373         // This is inaccurate since adb could run with ROOT_UID or other services can run with
374         // SHELL_UID. But this check covers most cases and enough.
375         // Note that getting service.adb.tcp.port system property is prohibited by sepolicy
376         // TODO: skip the socket only if there is a listen socket owned by SHELL_UID with the same
377         // source port as this socket
378         return msg.inetDiagMsg.idiag_uid == Process.SHELL_UID;
379     }
380 
381     /**
382      * Returns whether the range contains the uid in the InetDiagMessage or not
383      */
384     @VisibleForTesting
containsUid(InetDiagMessage msg, Set<Range<Integer>> ranges)385     public static boolean containsUid(InetDiagMessage msg, Set<Range<Integer>> ranges) {
386         for (final Range<Integer> range: ranges) {
387             if (range.contains(msg.inetDiagMsg.idiag_uid)) {
388                 return true;
389             }
390         }
391         return false;
392     }
393 
isLoopbackAddress(InetAddress addr)394     private static boolean isLoopbackAddress(InetAddress addr) {
395         if (addr.isLoopbackAddress()) return true;
396         if (!(addr instanceof Inet6Address)) return false;
397 
398         // Following check is for v4-mapped v6 address. StructInetDiagSockId contains v4-mapped v6
399         // address as Inet6Address, See StructInetDiagSockId#parse
400         final byte[] addrBytes = addr.getAddress();
401         for (int i = 0; i < 10; i++) {
402             if (addrBytes[i] != 0) return false;
403         }
404         return addrBytes[10] == (byte) 0xff
405                 && addrBytes[11] == (byte) 0xff
406                 && addrBytes[12] == 127;
407     }
408 
409     /**
410      * Returns whether the socket address in the InetDiagMessage is loopback or not
411      */
412     @VisibleForTesting
isLoopback(InetDiagMessage msg)413     public static boolean isLoopback(InetDiagMessage msg) {
414         final InetAddress srcAddr = msg.inetDiagMsg.id.locSocketAddress.getAddress();
415         final InetAddress dstAddr = msg.inetDiagMsg.id.remSocketAddress.getAddress();
416         return isLoopbackAddress(srcAddr)
417                 || isLoopbackAddress(dstAddr)
418                 || srcAddr.equals(dstAddr);
419     }
420 
destroySockets(int proto, int states, Predicate<InetDiagMessage> filter)421     private static void destroySockets(int proto, int states, Predicate<InetDiagMessage> filter)
422             throws ErrnoException, SocketException, InterruptedIOException {
423         FileDescriptor dumpFd = null;
424         FileDescriptor destroyFd = null;
425 
426         try {
427             dumpFd = NetlinkUtils.createNetLinkInetDiagSocket();
428             destroyFd = NetlinkUtils.createNetLinkInetDiagSocket();
429             connectSocketToNetlink(dumpFd);
430             connectSocketToNetlink(destroyFd);
431 
432             for (int family : List.of(AF_INET, AF_INET6)) {
433                 try {
434                     sendNetlinkDumpRequest(dumpFd, proto, states, family);
435                 } catch (InterruptedIOException | ErrnoException e) {
436                     Log.e(TAG, "Failed to send netlink dump request: " + e);
437                     continue;
438                 }
439                 final int destroyedSockets = processNetlinkDumpAndDestroySockets(
440                         dumpFd, destroyFd, proto, filter);
441                 Log.d(TAG, "Destroyed " + destroyedSockets + " sockets"
442                         + ", proto=" + stringForProtocol(proto)
443                         + ", family=" + stringForAddressFamily(family)
444                         + ", states=" + states);
445             }
446         } finally {
447             closeSocketQuietly(dumpFd);
448             closeSocketQuietly(destroyFd);
449         }
450     }
451 
452     /**
453      * Close tcp sockets that match the following condition
454      *  1. TCP status is one of TCP_ESTABLISHED, TCP_SYN_SENT, and TCP_SYN_RECV
455      *  2. Owner uid of socket is not in the exemptUids
456      *  3. Owner uid of socket is in the ranges
457      *  4. Socket is not loopback
458      *  5. Socket is not adb socket
459      *
460      * @param ranges target uid ranges
461      * @param exemptUids uids to skip close socket
462      */
destroyLiveTcpSockets(Set<Range<Integer>> ranges, Set<Integer> exemptUids)463     public static void destroyLiveTcpSockets(Set<Range<Integer>> ranges, Set<Integer> exemptUids)
464             throws SocketException, InterruptedIOException, ErrnoException {
465         final long startTimeMs = SystemClock.elapsedRealtime();
466         destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
467                 (diagMsg) -> !exemptUids.contains(diagMsg.inetDiagMsg.idiag_uid)
468                         && containsUid(diagMsg, ranges)
469                         && !isLoopback(diagMsg)
470                         && !isAdbSocket(diagMsg));
471         final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
472         Log.d(TAG, "Destroyed live tcp sockets for uids=" + ranges + " exemptUids=" + exemptUids
473                 + " in " + durationMs + "ms");
474     }
475 
476     /**
477      * Close tcp sockets that match the following condition
478      *  1. TCP status is one of TCP_ESTABLISHED, TCP_SYN_SENT, and TCP_SYN_RECV
479      *  2. Owner uid of socket is in the targetUids
480      *  3. Socket is not loopback
481      *  4. Socket is not adb socket
482      *
483      * @param ownerUids target uids to close sockets
484      */
destroyLiveTcpSocketsByOwnerUids(Set<Integer> ownerUids)485     public static void destroyLiveTcpSocketsByOwnerUids(Set<Integer> ownerUids)
486             throws SocketException, InterruptedIOException, ErrnoException {
487         final long startTimeMs = SystemClock.elapsedRealtime();
488         destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
489                 (diagMsg) -> ownerUids.contains(diagMsg.inetDiagMsg.idiag_uid)
490                         && !isLoopback(diagMsg)
491                         && !isAdbSocket(diagMsg));
492         final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
493         Log.d(TAG, "Destroyed live tcp sockets for uids=" + ownerUids + " in " + durationMs + "ms");
494     }
495 
496     @Override
toString()497     public String toString() {
498         return "InetDiagMessage{ "
499                 + "nlmsghdr{"
500                 + (mHeader == null ? "" : mHeader.toString(NETLINK_INET_DIAG)) + "}, "
501                 + "inet_diag_msg{"
502                 + (inetDiagMsg == null ? "" : inetDiagMsg.toString()) + "} "
503                 + "}";
504     }
505 }
506