• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.system.OsConstants.AF_UNSPEC;
20 
21 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
22 import static com.android.net.module.util.netlink.NetlinkConstants.IFF_UP;
23 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_GETLINK;
24 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWLINK;
25 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST_ACK;
26 
27 import android.net.MacAddress;
28 import android.system.OsConstants;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.VisibleForTesting;
33 
34 import java.nio.ByteBuffer;
35 import java.nio.ByteOrder;
36 
37 /**
38  * A NetlinkMessage subclass for rtnetlink link messages.
39  *
40  * RtNetlinkLinkMessage.parse() must be called with a ByteBuffer that contains exactly one netlink
41  * message.
42  *
43  * see also:
44  *
45  *     include/uapi/linux/rtnetlink.h
46  *
47  * @hide
48  */
49 public class RtNetlinkLinkMessage extends NetlinkMessage {
50     public static final short IFLA_ADDRESS   = 1;
51     public static final short IFLA_IFNAME    = 3;
52     public static final short IFLA_MTU       = 4;
53     public static final short IFLA_INET6_ADDR_GEN_MODE = 8;
54     public static final short IFLA_AF_SPEC = 26;
55 
56     public static final short IN6_ADDR_GEN_MODE_NONE = 1;
57 
58     // The maximum buffer size to hold an interface name including the null-terminator '\0'.
59     private static final int IFNAMSIZ = 16;
60     // The default value of MTU, which means the MTU is unspecified.
61     private static final int DEFAULT_MTU = 0;
62 
63     @NonNull
64     private final StructIfinfoMsg mIfinfomsg;
65     private final int mMtu;
66     @Nullable
67     private final MacAddress mHardwareAddress;
68     @Nullable
69     private final String mInterfaceName;
70 
71     /**
72      * Creates an {@link RtNetlinkLinkMessage} instance.
73      *
74      * <p>This method validates the arguments and returns {@code null} if any of them are invalid.
75      * nlmsghdr's nlmsg_len will be updated to the correct length before creation.
76      *
77      * @param nlmsghdr The Netlink message header. Must not be {@code null}.
78      * @param ifinfomsg The interface information message. Must not be {@code null}.
79      * @param mtu The Maximum Transmission Unit (MTU) value for the link.
80      * @param hardwareAddress The hardware address (MAC address) of the link. May be {@code null}.
81      * @param interfaceName The name of the interface. May be {@code null}.
82      * @return A new {@link RtNetlinkLinkMessage} instance, or {@code null} if the input arguments
83      *         are invalid.
84      */
85     @Nullable
build(@onNull StructNlMsgHdr nlmsghdr, @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, @Nullable String interfaceName)86     public static RtNetlinkLinkMessage build(@NonNull StructNlMsgHdr nlmsghdr,
87             @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress,
88             @Nullable String interfaceName) {
89         if (mtu < 0) {
90             return null;
91         }
92         if (interfaceName != null
93                 && (interfaceName.isEmpty() || interfaceName.length() + 1 > IFNAMSIZ)) {
94             return null;
95         }
96 
97         nlmsghdr.nlmsg_len = calculateMessageLength(mtu, hardwareAddress, interfaceName);
98         return new RtNetlinkLinkMessage(nlmsghdr, ifinfomsg, mtu, hardwareAddress, interfaceName);
99     }
100 
RtNetlinkLinkMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, @Nullable String interfaceName)101     private RtNetlinkLinkMessage(@NonNull StructNlMsgHdr nlmsghdr,
102             @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress,
103             @Nullable String interfaceName) {
104         super(nlmsghdr);
105         mIfinfomsg = ifinfomsg;
106         mMtu = mtu;
107         mHardwareAddress = hardwareAddress;
108         mInterfaceName = interfaceName;
109     }
110 
getMtu()111     public int getMtu() {
112         return mMtu;
113     }
114 
115     @NonNull
getIfinfoHeader()116     public StructIfinfoMsg getIfinfoHeader() {
117         return mIfinfomsg;
118     }
119 
120     @Nullable
getHardwareAddress()121     public MacAddress getHardwareAddress() {
122         return mHardwareAddress;
123     }
124 
125     @Nullable
getInterfaceName()126     public String getInterfaceName() {
127         return mInterfaceName;
128     }
129 
130     /**
131      * Parse rtnetlink link message from {@link ByteBuffer}. This method must be called with a
132      * ByteBuffer that contains exactly one netlink message.
133      *
134      * @param header netlink message header.
135      * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
136      */
137     @Nullable
parse(@onNull final StructNlMsgHdr header, @NonNull final ByteBuffer byteBuffer)138     public static RtNetlinkLinkMessage parse(@NonNull final StructNlMsgHdr header,
139             @NonNull final ByteBuffer byteBuffer) {
140         final StructIfinfoMsg ifinfoMsg = StructIfinfoMsg.parse(byteBuffer);
141         if (ifinfoMsg == null) {
142             return null;
143         }
144 
145         // IFLA_MTU
146         int mtu = DEFAULT_MTU;
147         final int baseOffset = byteBuffer.position();
148         StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(IFLA_MTU, byteBuffer);
149         if (nlAttr != null) {
150             mtu = nlAttr.getValueAsInt(DEFAULT_MTU);
151         }
152 
153         // IFLA_ADDRESS
154         MacAddress hardwareAddress = null;
155         byteBuffer.position(baseOffset);
156         nlAttr = StructNlAttr.findNextAttrOfType(IFLA_ADDRESS, byteBuffer);
157         if (nlAttr != null) {
158             hardwareAddress = nlAttr.getValueAsMacAddress();
159         }
160 
161         // IFLA_IFNAME
162         String interfaceName = null;
163         byteBuffer.position(baseOffset);
164         nlAttr = StructNlAttr.findNextAttrOfType(IFLA_IFNAME, byteBuffer);
165         if (nlAttr != null) {
166             interfaceName = nlAttr.getValueAsString();
167         }
168 
169         return new RtNetlinkLinkMessage(header, ifinfoMsg, mtu, hardwareAddress, interfaceName);
170     }
171 
172     /**
173      *  Write a rtnetlink link message to {@link byte} array.
174      */
pack(ByteOrder order)175     public byte[] pack(ByteOrder order) {
176         byte[] bytes = new byte[mHeader.nlmsg_len];
177         ByteBuffer buffer = ByteBuffer.wrap(bytes).order(order);
178         pack(buffer);
179         return bytes;
180     }
181 
182     /**
183      * Write a rtnetlink link message to {@link ByteBuffer}.
184      */
185     @VisibleForTesting
pack(ByteBuffer byteBuffer)186     protected void pack(ByteBuffer byteBuffer) {
187         mHeader.pack(byteBuffer);
188         mIfinfomsg.pack(byteBuffer);
189 
190         if (mMtu != DEFAULT_MTU) {
191             final StructNlAttr mtu = new StructNlAttr(IFLA_MTU, mMtu);
192             mtu.pack(byteBuffer);
193         }
194         if (mHardwareAddress != null) {
195             final StructNlAttr hardwareAddress = new StructNlAttr(IFLA_ADDRESS, mHardwareAddress);
196             hardwareAddress.pack(byteBuffer);
197         }
198         if (mInterfaceName != null) {
199             final StructNlAttr ifname = new StructNlAttr(IFLA_IFNAME, mInterfaceName);
200             ifname.pack(byteBuffer);
201         }
202     }
203 
204     /**
205      *  Calculate the byte length of the packed buffer.
206      */
calculateMessageLength(int mtu, MacAddress hardwareAddress, String interfaceName)207     private static int calculateMessageLength(int mtu, MacAddress hardwareAddress,
208             String interfaceName) {
209         int length = StructNlMsgHdr.STRUCT_SIZE + StructIfinfoMsg.STRUCT_SIZE;
210 
211         if (mtu != DEFAULT_MTU) {
212             length += NetlinkConstants.alignedLengthOf(StructNlAttr.NLA_HEADERLEN + Integer.BYTES);
213         }
214         if (hardwareAddress != null) {
215             length += NetlinkConstants.alignedLengthOf(
216                     StructNlAttr.NLA_HEADERLEN + ETHER_ADDR_LEN);
217         }
218         if (interfaceName != null) {
219             length += NetlinkConstants.alignedLengthOf(
220                     // The string should be end with '\0', so the length should plus 1.
221                     StructNlAttr.NLA_HEADERLEN + interfaceName.length() + 1);
222         }
223 
224         return length;
225     }
226 
227     /**
228      * Create a link message to set the operational state (up or down) of a network interface.
229      *
230      * @param interfaceName  The network interface name.
231      * @param sequenceNumber The sequence number to use for the Netlink message.
232      * @param isUp           {@code true} to set the interface up, {@code false} to set it down.
233      * @return A `RtNetlinkLinkMessage` instance configured to set the link state.
234      */
235     @Nullable
createSetLinkStateMessage(@onNull String interfaceName, int sequenceNumber, boolean isUp)236     public static RtNetlinkLinkMessage createSetLinkStateMessage(@NonNull String interfaceName,
237             int sequenceNumber, boolean isUp) {
238         return createSetLinkStateMessage(interfaceName, sequenceNumber, isUp, new OsAccess());
239     }
240 
241     @VisibleForTesting
242     @Nullable
createSetLinkStateMessage(@onNull String interfaceName, int sequenceNumber, boolean isUp, OsAccess osAccess)243     protected static RtNetlinkLinkMessage createSetLinkStateMessage(@NonNull String interfaceName,
244             int sequenceNumber, boolean isUp, OsAccess osAccess) {
245         final int interfaceIndex = osAccess.if_nametoindex(interfaceName);
246         if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) {
247             return null;
248         }
249 
250         return RtNetlinkLinkMessage.build(
251                 new StructNlMsgHdr(0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber),
252                 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex,
253                                     isUp ? IFF_UP : 0, IFF_UP), DEFAULT_MTU, null, null);
254     }
255 
256     /**
257      * Create a link message to rename the network interface.
258      *
259      * @param interfaceName  The network interface name.
260      * @param sequenceNumber The sequence number to use for the Netlink message.
261      * @param newName        The new name of the network interface.
262      * @return A `RtNetlinkLinkMessage` instance configured to rename the network interface.
263      */
264     @Nullable
createSetLinkNameMessage(@onNull String interfaceName, int sequenceNumber, @NonNull String newName)265     public static RtNetlinkLinkMessage createSetLinkNameMessage(@NonNull String interfaceName,
266             int sequenceNumber, @NonNull String newName) {
267         return createSetLinkNameMessage(interfaceName, sequenceNumber, newName, new OsAccess());
268     }
269 
270     @VisibleForTesting
271     @Nullable
createSetLinkNameMessage(@onNull String interfaceName, int sequenceNumber, @NonNull String newName, OsAccess osAccess)272     protected static RtNetlinkLinkMessage createSetLinkNameMessage(@NonNull String interfaceName,
273             int sequenceNumber, @NonNull String newName, OsAccess osAccess) {
274         final int interfaceIndex = osAccess.if_nametoindex(interfaceName);
275         if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) {
276             return null;
277         }
278 
279         return RtNetlinkLinkMessage.build(
280                 new StructNlMsgHdr(0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber),
281                 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex, 0, 0),
282                 DEFAULT_MTU, null, newName);
283     }
284 
285     /**
286      * Creates an {@link RtNetlinkLinkMessage} instance that can be used to get the link information
287      * of a network interface.
288      *
289      * @param interfaceName The name of the network interface to query.
290      * @param sequenceNumber The sequence number for the Netlink message.
291      * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface.
292      */
293     @Nullable
createGetLinkMessage(@onNull String interfaceName, int sequenceNumber)294     public static RtNetlinkLinkMessage createGetLinkMessage(@NonNull String interfaceName,
295             int sequenceNumber) {
296         return createGetLinkMessage(interfaceName, sequenceNumber, new OsAccess());
297     }
298 
299     @VisibleForTesting
300     @Nullable
createGetLinkMessage(@onNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess)301     protected static RtNetlinkLinkMessage createGetLinkMessage(@NonNull String interfaceName,
302             int sequenceNumber, @NonNull OsAccess osAccess) {
303         final int interfaceIndex = osAccess.if_nametoindex(interfaceName);
304         if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) {
305             return null;
306         }
307 
308         return RtNetlinkLinkMessage.build(
309                 new StructNlMsgHdr(0, RTM_GETLINK, NLM_F_REQUEST_ACK, sequenceNumber),
310                 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex, 0, 0),
311                 DEFAULT_MTU, null, null);
312     }
313 
314     /**
315      * Creates an {@link RtNetlinkLinkMessage} instance that can be used to set the flags of a
316      * network interface.
317      *
318      * @param interfaceName The name of the network interface to query.
319      * @param sequenceNumber The sequence number for the Netlink message.
320      * @param flags power-of-two integer flags to set or unset. A flag to set should be passed as
321      *        is as a power-of-two value, and a flag to remove should be passed inversed as -1 with
322      *        a single bit down. For example: IFF_UP, ~IFF_BROADCAST...
323      * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface.
324      */
325     @Nullable
createSetFlagsMessage(@onNull String interfaceName, int sequenceNumber, int... flags)326     public static RtNetlinkLinkMessage createSetFlagsMessage(@NonNull String interfaceName,
327             int sequenceNumber, int... flags) {
328         return createSetFlagsMessage(
329                 interfaceName, sequenceNumber, new OsAccess(), flags);
330     }
331 
332     @VisibleForTesting
333     @Nullable
createSetFlagsMessage( @onNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess, int... flags)334     protected static RtNetlinkLinkMessage createSetFlagsMessage(
335             @NonNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess,
336             int... flags) {
337         final int interfaceIndex = osAccess.if_nametoindex(interfaceName);
338         if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) {
339             return null;
340         }
341 
342         int flagsBits = 0;
343         int changeBits = 0;
344         for (int f : flags) {
345             if (Integer.bitCount(f) == 1) {
346                 flagsBits |= f;
347                 changeBits |= f;
348             } else if (Integer.bitCount(~f) == 1) {
349                 flagsBits &= f;
350                 changeBits |= ~f;
351             } else {
352                 return null;
353             }
354         }
355         // RTM_NEWLINK is used here for create, modify, or notify changes about a internet
356         // interface, including change in administrative state. While RTM_SETLINK is used to
357         // modify an existing link rather than creating a new one.
358         return RtNetlinkLinkMessage.build(
359                 new StructNlMsgHdr(
360                         /*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber),
361                 new StructIfinfoMsg((short) AF_UNSPEC, /*type*/ 0, interfaceIndex,
362                         flagsBits, changeBits),
363                 DEFAULT_MTU, /*hardwareAddress*/ null, /*interfaceName*/ null);
364     }
365 
366     /**
367      * Creates an {@link RtNetlinkLinkMessage} instance that can be used to set the MTU of a
368      * network interface.
369      *
370      * @param interfaceName The name of the network interface to query.
371      * @param sequenceNumber The sequence number for the Netlink message.
372      * @param mtu MTU value to set for the interface.
373      * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface.
374      */
375     @Nullable
createSetMtuMessage(@onNull String interfaceName, int sequenceNumber, int mtu)376     public static RtNetlinkLinkMessage createSetMtuMessage(@NonNull String interfaceName,
377             int sequenceNumber, int mtu) {
378         return createSetMtuMessage(
379             interfaceName, sequenceNumber, mtu, new OsAccess());
380     }
381 
382     @VisibleForTesting
383     @Nullable
createSetMtuMessage(@onNull String interfaceName, int sequenceNumber, int mtu, @NonNull OsAccess osAccess)384     protected static RtNetlinkLinkMessage createSetMtuMessage(@NonNull String interfaceName,
385             int sequenceNumber, int mtu, @NonNull OsAccess osAccess) {
386         final int interfaceIndex = osAccess.if_nametoindex(interfaceName);
387         if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) {
388             return null;
389         }
390         return RtNetlinkLinkMessage.build(
391             new StructNlMsgHdr(/*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST_ACK , sequenceNumber),
392             new StructIfinfoMsg((short) AF_UNSPEC, /*type*/ 0, interfaceIndex,
393                 /*flags*/ 0, /*change*/ 0),
394             mtu, /*hardwareAddress*/ null, /*interfaceName*/ null);
395     }
396 
397     @Override
toString()398     public String toString() {
399         return "RtNetlinkLinkMessage{ "
400                 + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
401                 + "Ifinfomsg{" + mIfinfomsg + "}, "
402                 + "Hardware Address{" + mHardwareAddress + "}, "
403                 + "MTU{" + mMtu + "}, "
404                 + "Ifname{" + mInterfaceName + "} "
405                 + "}";
406     }
407 }
408