blob: f7b0d02fb7d3a1772dba2eeda7275cdae374a326 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.net.module.util.netlink;
import android.system.OsConstants;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.net.module.util.HexDump;
import java.net.InetAddress;
import java.nio.ByteBuffer;
/**
* A NetlinkMessage subclass for rtnetlink address messages.
*
* RtNetlinkAddressMessage.parse() must be called with a ByteBuffer that contains exactly one
* netlink message.
*
* see also:
*
* include/uapi/linux/rtnetlink.h
*
* @hide
*/
public class RtNetlinkAddressMessage extends NetlinkMessage {
public static final short IFA_ADDRESS = 1;
public static final short IFA_CACHEINFO = 6;
public static final short IFA_FLAGS = 8;
private int mFlags;
@NonNull
private StructIfaddrMsg mIfaddrmsg;
@NonNull
private InetAddress mIpAddress;
@Nullable
private StructIfacacheInfo mIfacacheInfo;
private RtNetlinkAddressMessage(@NonNull StructNlMsgHdr header) {
super(header);
mIfaddrmsg = null;
mIpAddress = null;
mIfacacheInfo = null;
mFlags = 0;
}
public int getFlags() {
return mFlags;
}
@NonNull
public StructIfaddrMsg getIfaddrHeader() {
return mIfaddrmsg;
}
@NonNull
public InetAddress getIpAddress() {
return mIpAddress;
}
@Nullable
public StructIfacacheInfo getIfacacheInfo() {
return mIfacacheInfo;
}
/**
* Parse rtnetlink address message from {@link ByteBuffer}. This method must be called with a
* ByteBuffer that contains exactly one netlink message.
*
* @param header netlink message header.
* @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
*/
@Nullable
public static RtNetlinkAddressMessage parse(@NonNull final StructNlMsgHdr header,
@NonNull final ByteBuffer byteBuffer) {
final RtNetlinkAddressMessage addrMsg = new RtNetlinkAddressMessage(header);
addrMsg.mIfaddrmsg = StructIfaddrMsg.parse(byteBuffer);
if (addrMsg.mIfaddrmsg == null) return null;
// IFA_ADDRESS
final int baseOffset = byteBuffer.position();
StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(IFA_ADDRESS, byteBuffer);
if (nlAttr == null) return null;
addrMsg.mIpAddress = nlAttr.getValueAsInetAddress();
if (addrMsg.mIpAddress == null) return null;
// IFA_CACHEINFO
byteBuffer.position(baseOffset);
nlAttr = StructNlAttr.findNextAttrOfType(IFA_CACHEINFO, byteBuffer);
if (nlAttr != null) {
addrMsg.mIfacacheInfo = StructIfacacheInfo.parse(nlAttr.getValueAsByteBuffer());
}
// The first 8 bits of flags are in the ifaddrmsg.
addrMsg.mFlags = addrMsg.mIfaddrmsg.flags;
// IFA_FLAGS. All the flags are in the IF_FLAGS attribute. This should always be present,
// and will overwrite the flags set above.
byteBuffer.position(baseOffset);
nlAttr = StructNlAttr.findNextAttrOfType(IFA_FLAGS, byteBuffer);
if (nlAttr == null) return null;
final Integer value = nlAttr.getValueAsInteger();
if (value == null) return null;
addrMsg.mFlags = value;
return addrMsg;
}
/**
* Write a rtnetlink address message to {@link ByteBuffer}.
*/
@VisibleForTesting
protected void pack(ByteBuffer byteBuffer) {
getHeader().pack(byteBuffer);
mIfaddrmsg.pack(byteBuffer);
final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, mIpAddress);
address.pack(byteBuffer);
if (mIfacacheInfo != null) {
final StructNlAttr cacheInfo = new StructNlAttr(IFA_CACHEINFO,
mIfacacheInfo.writeToBytes());
cacheInfo.pack(byteBuffer);
}
// If IFA_FLAGS attribute isn't present on the wire at parsing netlink message, it will
// still be packed to ByteBuffer even if the flag is 0.
final StructNlAttr flags = new StructNlAttr(IFA_FLAGS, mFlags);
flags.pack(byteBuffer);
}
@Override
public String toString() {
return "RtNetlinkAddressMessage{ "
+ "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
+ "Ifaddrmsg{" + mIfaddrmsg.toString() + "}, "
+ "IP Address{" + mIpAddress.getHostAddress() + "}, "
+ "IfacacheInfo{" + (mIfacacheInfo == null ? "" : mIfacacheInfo.toString()) + "}, "
+ "Address Flags{" + HexDump.toHexString(mFlags) + "} "
+ "}";
}
}