1 /*
2  * Copyright (C) 2019 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 android.system.OsConstants;
20 
21 import java.nio.ByteBuffer;
22 
23 /**
24  * struct ndmsg
25  *
26  * see: <linux_src>/include/uapi/linux/neighbour.h
27  *
28  * @hide
29  */
30 public class StructNdMsg {
31     // Already aligned.
32     public static final int STRUCT_SIZE = 12;
33 
34     // Neighbor Cache Entry States
35     public static final short NUD_NONE        = 0x00;
36     public static final short NUD_INCOMPLETE  = 0x01;
37     public static final short NUD_REACHABLE   = 0x02;
38     public static final short NUD_STALE       = 0x04;
39     public static final short NUD_DELAY       = 0x08;
40     public static final short NUD_PROBE       = 0x10;
41     public static final short NUD_FAILED      = 0x20;
42     public static final short NUD_NOARP       = 0x40;
43     public static final short NUD_PERMANENT   = 0x80;
44 
45     /**
46      * Convert neighbor cache entry state integer to string.
47      */
stringForNudState(short nudState)48     public static String stringForNudState(short nudState) {
49         switch (nudState) {
50             case NUD_NONE: return "NUD_NONE";
51             case NUD_INCOMPLETE: return "NUD_INCOMPLETE";
52             case NUD_REACHABLE: return "NUD_REACHABLE";
53             case NUD_STALE: return "NUD_STALE";
54             case NUD_DELAY: return "NUD_DELAY";
55             case NUD_PROBE: return "NUD_PROBE";
56             case NUD_FAILED: return "NUD_FAILED";
57             case NUD_NOARP: return "NUD_NOARP";
58             case NUD_PERMANENT: return "NUD_PERMANENT";
59             default:
60                 return "unknown NUD state: " + String.valueOf(nudState);
61         }
62     }
63 
64     /**
65      * Check whether a neighbor is connected or not.
66      */
isNudStateConnected(short nudState)67     public static boolean isNudStateConnected(short nudState) {
68         return ((nudState & (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)) != 0);
69     }
70 
71     /**
72      * Check whether a neighbor is in the valid NUD state or not.
73      */
isNudStateValid(short nudState)74     public static boolean isNudStateValid(short nudState) {
75         return (isNudStateConnected(nudState)
76                 || ((nudState & (NUD_PROBE | NUD_STALE | NUD_DELAY)) != 0));
77     }
78 
79     // Neighbor Cache Entry Flags
80     public static byte NTF_USE       = (byte) 0x01;
81     public static byte NTF_SELF      = (byte) 0x02;
82     public static byte NTF_MASTER    = (byte) 0x04;
83     public static byte NTF_PROXY     = (byte) 0x08;
84     public static byte NTF_ROUTER    = (byte) 0x80;
85 
stringForNudFlags(byte flags)86     private static String stringForNudFlags(byte flags) {
87         final StringBuilder sb = new StringBuilder();
88         if ((flags & NTF_USE) != 0) {
89             sb.append("NTF_USE");
90         }
91         if ((flags & NTF_SELF) != 0) {
92             if (sb.length() > 0) {
93                 sb.append("|");
94             }
95             sb.append("NTF_SELF");
96         }
97         if ((flags & NTF_MASTER) != 0) {
98             if (sb.length() > 0) {
99                 sb.append("|");
100             }
101             sb.append("NTF_MASTER");
102         }
103         if ((flags & NTF_PROXY) != 0) {
104             if (sb.length() > 0) {
105                 sb.append("|");
106             }
107             sb.append("NTF_PROXY");
108         }
109         if ((flags & NTF_ROUTER) != 0) {
110             if (sb.length() > 0) {
111                 sb.append("|");
112             }
113             sb.append("NTF_ROUTER");
114         }
115         return sb.toString();
116     }
117 
hasAvailableSpace(ByteBuffer byteBuffer)118     private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
119         return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
120     }
121 
122     /**
123      * Parse a neighbor discovery netlink message header from a {@link ByteBuffer}.
124      *
125      * @param byteBuffer The buffer from which to parse the nd netlink message header.
126      * @return the parsed nd netlink message header, or {@code null} if the nd netlink message
127      *         header could not be parsed successfully (for example, if it was truncated).
128      */
parse(ByteBuffer byteBuffer)129     public static StructNdMsg parse(ByteBuffer byteBuffer) {
130         if (!hasAvailableSpace(byteBuffer)) return null;
131 
132         // The ByteOrder must have already been set by the caller.  In most
133         // cases ByteOrder.nativeOrder() is correct, with the possible
134         // exception of usage within unittests.
135         final StructNdMsg struct = new StructNdMsg();
136         struct.ndm_family = byteBuffer.get();
137         final byte pad1 = byteBuffer.get();
138         final short pad2 = byteBuffer.getShort();
139         struct.ndm_ifindex = byteBuffer.getInt();
140         struct.ndm_state = byteBuffer.getShort();
141         struct.ndm_flags = byteBuffer.get();
142         struct.ndm_type = byteBuffer.get();
143         return struct;
144     }
145 
146     public byte ndm_family;
147     public int ndm_ifindex;
148     public short ndm_state;
149     public byte ndm_flags;
150     public byte ndm_type;
151 
StructNdMsg()152     public StructNdMsg() {
153         ndm_family = (byte) OsConstants.AF_UNSPEC;
154     }
155 
156     /**
157      * Write the neighbor discovery message header to {@link ByteBuffer}.
158      */
pack(ByteBuffer byteBuffer)159     public void pack(ByteBuffer byteBuffer) {
160         // The ByteOrder must have already been set by the caller.  In most
161         // cases ByteOrder.nativeOrder() is correct, with the exception
162         // of usage within unittests.
163         byteBuffer.put(ndm_family);
164         byteBuffer.put((byte) 0);         // pad1
165         byteBuffer.putShort((short) 0);   // pad2
166         byteBuffer.putInt(ndm_ifindex);
167         byteBuffer.putShort(ndm_state);
168         byteBuffer.put(ndm_flags);
169         byteBuffer.put(ndm_type);
170     }
171 
172     /**
173      * Check whether a neighbor is connected or not.
174      */
nudConnected()175     public boolean nudConnected() {
176         return isNudStateConnected(ndm_state);
177     }
178 
179     /**
180      * Check whether a neighbor is in the valid NUD state or not.
181      */
nudValid()182     public boolean nudValid() {
183         return isNudStateValid(ndm_state);
184     }
185 
186     @Override
toString()187     public String toString() {
188         final String stateStr = "" + ndm_state + " (" + stringForNudState(ndm_state) + ")";
189         final String flagsStr = "" + ndm_flags + " (" + stringForNudFlags(ndm_flags) + ")";
190         return "StructNdMsg{ "
191                 + "family{" + NetlinkConstants.stringForAddressFamily((int) ndm_family) + "}, "
192                 + "ifindex{" + ndm_ifindex + "}, "
193                 + "state{" + stateStr + "}, "
194                 + "flags{" + flagsStr + "}, "
195                 + "type{" + ndm_type + "} "
196                 + "}";
197     }
198 }
199