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 com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress;
20 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
21 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE;
22 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
23 
24 import android.system.OsConstants;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.VisibleForTesting;
29 
30 import com.android.net.module.util.HexDump;
31 
32 import java.net.Inet4Address;
33 import java.net.Inet6Address;
34 import java.net.InetAddress;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.util.Objects;
38 
39 /**
40  * A NetlinkMessage subclass for rtnetlink address messages.
41  *
42  * RtNetlinkAddressMessage.parse() must be called with a ByteBuffer that contains exactly one
43  * netlink message.
44  *
45  * see also:
46  *
47  *     include/uapi/linux/rtnetlink.h
48  *
49  * @hide
50  */
51 public class RtNetlinkAddressMessage extends NetlinkMessage {
52     public static final short IFA_ADDRESS        = 1;
53     public static final short IFA_LOCAL          = 2;
54     public static final short IFA_BROADCAST      = 4;
55     public static final short IFA_CACHEINFO      = 6;
56     public static final short IFA_FLAGS          = 8;
57 
58     private int mFlags;
59     @NonNull
60     private StructIfaddrMsg mIfaddrmsg;
61     @NonNull
62     private InetAddress mIpAddress;
63     @Nullable
64     private StructIfacacheInfo mIfacacheInfo;
65 
66     @VisibleForTesting
RtNetlinkAddressMessage(@onNull final StructNlMsgHdr header, @NonNull final StructIfaddrMsg ifaddrMsg, @NonNull final InetAddress ipAddress, @Nullable final StructIfacacheInfo structIfacacheInfo, int flags)67     public RtNetlinkAddressMessage(@NonNull final StructNlMsgHdr header,
68             @NonNull final StructIfaddrMsg ifaddrMsg,
69             @NonNull final InetAddress ipAddress,
70             @Nullable final StructIfacacheInfo structIfacacheInfo,
71             int flags) {
72         super(header);
73         mIfaddrmsg = ifaddrMsg;
74         mIpAddress = ipAddress;
75         mIfacacheInfo = structIfacacheInfo;
76         mFlags = flags;
77     }
78 
RtNetlinkAddressMessage(@onNull StructNlMsgHdr header)79     private RtNetlinkAddressMessage(@NonNull StructNlMsgHdr header) {
80         this(header, null, null, null, 0);
81     }
82 
getFlags()83     public int getFlags() {
84         return mFlags;
85     }
86 
87     @NonNull
getIfaddrHeader()88     public StructIfaddrMsg getIfaddrHeader() {
89         return mIfaddrmsg;
90     }
91 
92     @NonNull
getIpAddress()93     public InetAddress getIpAddress() {
94         return mIpAddress;
95     }
96 
97     @Nullable
getIfacacheInfo()98     public StructIfacacheInfo getIfacacheInfo() {
99         return mIfacacheInfo;
100     }
101 
102     /**
103      * Parse rtnetlink address message from {@link ByteBuffer}. This method must be called with a
104      * ByteBuffer that contains exactly one netlink message.
105      *
106      * @param header netlink message header.
107      * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
108      */
109     @Nullable
parse(@onNull final StructNlMsgHdr header, @NonNull final ByteBuffer byteBuffer)110     public static RtNetlinkAddressMessage parse(@NonNull final StructNlMsgHdr header,
111             @NonNull final ByteBuffer byteBuffer) {
112         final RtNetlinkAddressMessage addrMsg = new RtNetlinkAddressMessage(header);
113 
114         addrMsg.mIfaddrmsg = StructIfaddrMsg.parse(byteBuffer);
115         if (addrMsg.mIfaddrmsg == null) return null;
116 
117         // IFA_ADDRESS
118         final int baseOffset = byteBuffer.position();
119         StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(IFA_ADDRESS, byteBuffer);
120         if (nlAttr == null) return null;
121         addrMsg.mIpAddress = nlAttr.getValueAsInetAddress();
122         if (addrMsg.mIpAddress == null) return null;
123 
124         // IFA_CACHEINFO
125         byteBuffer.position(baseOffset);
126         nlAttr = StructNlAttr.findNextAttrOfType(IFA_CACHEINFO, byteBuffer);
127         if (nlAttr != null) {
128             addrMsg.mIfacacheInfo = StructIfacacheInfo.parse(nlAttr.getValueAsByteBuffer());
129         }
130 
131         // The first 8 bits of flags are in the ifaddrmsg.
132         addrMsg.mFlags = addrMsg.mIfaddrmsg.flags;
133         // IFA_FLAGS. All the flags are in the IF_FLAGS attribute. This should always be present,
134         // and will overwrite the flags set above.
135         byteBuffer.position(baseOffset);
136         nlAttr = StructNlAttr.findNextAttrOfType(IFA_FLAGS, byteBuffer);
137         if (nlAttr == null) return null;
138         final Integer value = nlAttr.getValueAsInteger();
139         if (value == null) return null;
140         addrMsg.mFlags = value;
141 
142         return addrMsg;
143     }
144 
145     /**
146      * Write a rtnetlink address message to {@link ByteBuffer}.
147      */
148     @VisibleForTesting
pack(ByteBuffer byteBuffer)149     protected void pack(ByteBuffer byteBuffer) {
150         getHeader().pack(byteBuffer);
151         mIfaddrmsg.pack(byteBuffer);
152 
153         final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, mIpAddress);
154         address.pack(byteBuffer);
155 
156         if (mIfacacheInfo != null) {
157             final StructNlAttr cacheInfo = new StructNlAttr(IFA_CACHEINFO,
158                     mIfacacheInfo.writeToBytes());
159             cacheInfo.pack(byteBuffer);
160         }
161 
162         // If IFA_FLAGS attribute isn't present on the wire at parsing netlink message, it will
163         // still be packed to ByteBuffer even if the flag is 0.
164         final StructNlAttr flags = new StructNlAttr(IFA_FLAGS, mFlags);
165         flags.pack(byteBuffer);
166 
167         // Add the required IFA_LOCAL and IFA_BROADCAST attributes for IPv4 addresses. The IFA_LOCAL
168         // attribute represents the local address, which is equivalent to IFA_ADDRESS on a normally
169         // configured broadcast interface, however, for PPP interfaces, IFA_ADDRESS indicates the
170         // destination address and the local address is provided in the IFA_LOCAL attribute. If the
171         // IFA_LOCAL attribute is not present in the RTM_NEWADDR message, the kernel replies with an
172         // error netlink message with invalid parameters. IFA_BROADCAST is also required, otherwise
173         // the broadcast on the interface is 0.0.0.0. See include/uapi/linux/if_addr.h for details.
174         // For IPv6 addresses, the IFA_ADDRESS attribute applies and introduces no ambiguity.
175         if (mIpAddress instanceof Inet4Address) {
176             final StructNlAttr localAddress = new StructNlAttr(IFA_LOCAL, mIpAddress);
177             localAddress.pack(byteBuffer);
178 
179             final Inet4Address broadcast =
180                     getBroadcastAddress((Inet4Address) mIpAddress, mIfaddrmsg.prefixLen);
181             final StructNlAttr broadcastAddress = new StructNlAttr(IFA_BROADCAST, broadcast);
182             broadcastAddress.pack(byteBuffer);
183         }
184     }
185 
186     /**
187      * A convenience method to create a RTM_NEWADDR message.
188      */
newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip, short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid)189     public static byte[] newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip,
190             short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid) {
191         Objects.requireNonNull(ip, "IP address to be set via netlink message cannot be null");
192 
193         final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
194         nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWADDR;
195         nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK;
196         nlmsghdr.nlmsg_seq = seqNo;
197 
198         final RtNetlinkAddressMessage msg = new RtNetlinkAddressMessage(nlmsghdr);
199         final byte family =
200                 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
201         // IFA_FLAGS attribute is always present within this method, just set flags from
202         // ifaddrmsg to 0. kernel will prefer the flags from IFA_FLAGS attribute.
203         msg.mIfaddrmsg =
204                 new StructIfaddrMsg(family, prefixlen, (short) 0 /* flags */, scope, ifIndex);
205         msg.mIpAddress = ip;
206         msg.mIfacacheInfo = new StructIfacacheInfo(preferred, valid, 0 /* cstamp */,
207                 0 /* tstamp */);
208         msg.mFlags = flags;
209 
210         final byte[] bytes = new byte[msg.getRequiredSpace(family)];
211         nlmsghdr.nlmsg_len = bytes.length;
212         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
213         byteBuffer.order(ByteOrder.nativeOrder());
214         msg.pack(byteBuffer);
215         return bytes;
216     }
217 
218     /**
219      * A convenience method to create a RTM_DELADDR message.
220      */
newRtmDelAddressMessage(int seqNo, @NonNull final InetAddress ip, short prefixlen, int ifIndex)221     public static byte[] newRtmDelAddressMessage(int seqNo, @NonNull final InetAddress ip,
222             short prefixlen, int ifIndex) {
223         Objects.requireNonNull(ip, "IP address to be deleted via netlink message cannot be null");
224 
225         final int ifaAddrAttrLength = NetlinkConstants.alignedLengthOf(
226                 StructNlAttr.NLA_HEADERLEN + ip.getAddress().length);
227         final int length = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE
228                 + ifaAddrAttrLength;
229         final byte[] bytes = new byte[length];
230         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
231         byteBuffer.order(ByteOrder.nativeOrder());
232 
233         final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
234         nlmsghdr.nlmsg_len = length;
235         nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR;
236         nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
237         nlmsghdr.nlmsg_seq = seqNo;
238         nlmsghdr.pack(byteBuffer);
239 
240         final byte family =
241                 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
242         // Actually kernel ignores scope and flags(only deal with IFA_F_MANAGETEMPADDR, it
243         // indicates that all relevant IPv6 temporary addresses should be deleted as well when
244         // user space intends to delete a global IPv6 address with IFA_F_MANAGETEMPADDR), so
245         // far IFA_F_MANAGETEMPADDR flag isn't used in user space, it's fine to ignore it.
246         // However, we need to add IFA_FLAGS attribute in RTM_DELADDR if flags parsing should
247         // be supported in the future.
248         final StructIfaddrMsg ifaddrmsg = new StructIfaddrMsg(family, prefixlen,
249                 (short) 0 /* flags */, (short) 0 /* scope */, ifIndex);
250         ifaddrmsg.pack(byteBuffer);
251 
252         final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, ip);
253         address.pack(byteBuffer);
254 
255         return bytes;
256     }
257 
258     // This function helper gives the required buffer size for IFA_ADDRESS, IFA_CACHEINFO and
259     // IFA_FLAGS attributes encapsulation. However, that's not a mandatory requirement for all
260     // RtNetlinkAddressMessage, e.g. RTM_DELADDR sent from user space to kernel to delete an
261     // IP address only requires IFA_ADDRESS attribute. The caller should check if these attributes
262     // are necessary to carry when constructing a RtNetlinkAddressMessage.
getRequiredSpace(int family)263     private int getRequiredSpace(int family) {
264         int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE;
265         // IFA_ADDRESS attr
266         spaceRequired += NetlinkConstants.alignedLengthOf(
267                 StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length);
268         // IFA_CACHEINFO attr
269         spaceRequired += NetlinkConstants.alignedLengthOf(
270                 StructNlAttr.NLA_HEADERLEN + StructIfacacheInfo.STRUCT_SIZE);
271         // IFA_FLAGS "u32" attr
272         spaceRequired += StructNlAttr.NLA_HEADERLEN + 4;
273         if (family == OsConstants.AF_INET) {
274             // IFA_LOCAL attr
275             spaceRequired += NetlinkConstants.alignedLengthOf(
276                     StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length);
277             // IFA_BROADCAST attr
278             spaceRequired += NetlinkConstants.alignedLengthOf(
279                     StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length);
280         }
281         return spaceRequired;
282     }
283 
284     @Override
toString()285     public String toString() {
286         return "RtNetlinkAddressMessage{ "
287                 + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
288                 + "Ifaddrmsg{" + mIfaddrmsg.toString() + "}, "
289                 + "IP Address{" + mIpAddress.getHostAddress() + "}, "
290                 + "IfacacheInfo{" + (mIfacacheInfo == null ? "" : mIfacacheInfo.toString()) + "}, "
291                 + "Address Flags{" + HexDump.toHexString(mFlags) + "} "
292                 + "}";
293     }
294 }
295