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 static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK; 20 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP; 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 29 import java.net.Inet6Address; 30 import java.net.InetAddress; 31 import java.nio.ByteBuffer; 32 import java.nio.ByteOrder; 33 34 /** 35 * A NetlinkMessage subclass for rtnetlink neighbor messages. 36 * 37 * see also: <linux_src>/include/uapi/linux/neighbour.h 38 * 39 * @hide 40 */ 41 public class RtNetlinkNeighborMessage extends NetlinkMessage { 42 public static final short NDA_UNSPEC = 0; 43 public static final short NDA_DST = 1; 44 public static final short NDA_LLADDR = 2; 45 public static final short NDA_CACHEINFO = 3; 46 public static final short NDA_PROBES = 4; 47 public static final short NDA_VLAN = 5; 48 public static final short NDA_PORT = 6; 49 public static final short NDA_VNI = 7; 50 public static final short NDA_IFINDEX = 8; 51 public static final short NDA_MASTER = 9; 52 53 /** 54 * Parse routing socket netlink neighbor message from ByteBuffer. 55 * 56 * @param header netlink message header. 57 * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. 58 */ 59 @Nullable parse(@onNull StructNlMsgHdr header, @NonNull ByteBuffer byteBuffer)60 public static RtNetlinkNeighborMessage parse(@NonNull StructNlMsgHdr header, 61 @NonNull ByteBuffer byteBuffer) { 62 final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header); 63 64 neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer); 65 if (neighMsg.mNdmsg == null) { 66 return null; 67 } 68 69 // Some of these are message-type dependent, and not always present. 70 final int baseOffset = byteBuffer.position(); 71 StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(NDA_DST, byteBuffer); 72 if (nlAttr != null) { 73 neighMsg.mDestination = nlAttr.getValueAsInetAddress(); 74 } 75 76 byteBuffer.position(baseOffset); 77 nlAttr = StructNlAttr.findNextAttrOfType(NDA_LLADDR, byteBuffer); 78 if (nlAttr != null) { 79 neighMsg.mLinkLayerAddr = nlAttr.nla_value; 80 } 81 82 byteBuffer.position(baseOffset); 83 nlAttr = StructNlAttr.findNextAttrOfType(NDA_PROBES, byteBuffer); 84 if (nlAttr != null) { 85 neighMsg.mNumProbes = nlAttr.getValueAsInt(0); 86 } 87 88 byteBuffer.position(baseOffset); 89 nlAttr = StructNlAttr.findNextAttrOfType(NDA_CACHEINFO, byteBuffer); 90 if (nlAttr != null) { 91 neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer()); 92 } 93 94 final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; 95 final int kAdditionalSpace = NetlinkConstants.alignedLengthOf( 96 neighMsg.mHeader.nlmsg_len - kMinConsumed); 97 if (byteBuffer.remaining() < kAdditionalSpace) { 98 byteBuffer.position(byteBuffer.limit()); 99 } else { 100 byteBuffer.position(baseOffset + kAdditionalSpace); 101 } 102 103 return neighMsg; 104 } 105 106 /** 107 * A convenience method to create an RTM_GETNEIGH request message. 108 */ newGetNeighborsRequest(int seqNo)109 public static byte[] newGetNeighborsRequest(int seqNo) { 110 final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; 111 final byte[] bytes = new byte[length]; 112 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 113 byteBuffer.order(ByteOrder.nativeOrder()); 114 115 final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); 116 nlmsghdr.nlmsg_len = length; 117 nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH; 118 nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 119 nlmsghdr.nlmsg_seq = seqNo; 120 nlmsghdr.pack(byteBuffer); 121 122 final StructNdMsg ndmsg = new StructNdMsg(); 123 ndmsg.pack(byteBuffer); 124 125 return bytes; 126 } 127 128 /** 129 * A convenience method to create an RTM_NEWNEIGH message, to modify 130 * the kernel's state information for a specific neighbor. 131 */ newNewNeighborMessage( int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr)132 public static byte[] newNewNeighborMessage( 133 int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) { 134 final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); 135 nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH; 136 nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; 137 nlmsghdr.nlmsg_seq = seqNo; 138 139 final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr); 140 msg.mNdmsg = new StructNdMsg(); 141 msg.mNdmsg.ndm_family = 142 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET); 143 msg.mNdmsg.ndm_ifindex = ifIndex; 144 msg.mNdmsg.ndm_state = nudState; 145 msg.mDestination = ip; 146 msg.mLinkLayerAddr = llAddr; // might be null 147 148 final byte[] bytes = new byte[msg.getRequiredSpace()]; 149 nlmsghdr.nlmsg_len = bytes.length; 150 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 151 byteBuffer.order(ByteOrder.nativeOrder()); 152 msg.pack(byteBuffer); 153 return bytes; 154 } 155 156 private StructNdMsg mNdmsg; 157 private InetAddress mDestination; 158 private byte[] mLinkLayerAddr; 159 private int mNumProbes; 160 private StructNdaCacheInfo mCacheInfo; 161 RtNetlinkNeighborMessage(@onNull StructNlMsgHdr header)162 private RtNetlinkNeighborMessage(@NonNull StructNlMsgHdr header) { 163 super(header); 164 mNdmsg = null; 165 mDestination = null; 166 mLinkLayerAddr = null; 167 mNumProbes = 0; 168 mCacheInfo = null; 169 } 170 getNdHeader()171 public StructNdMsg getNdHeader() { 172 return mNdmsg; 173 } 174 getDestination()175 public InetAddress getDestination() { 176 return mDestination; 177 } 178 getLinkLayerAddress()179 public byte[] getLinkLayerAddress() { 180 return mLinkLayerAddr; 181 } 182 getProbes()183 public int getProbes() { 184 return mNumProbes; 185 } 186 getCacheInfo()187 public StructNdaCacheInfo getCacheInfo() { 188 return mCacheInfo; 189 } 190 getRequiredSpace()191 private int getRequiredSpace() { 192 int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; 193 if (mDestination != null) { 194 spaceRequired += NetlinkConstants.alignedLengthOf( 195 StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length); 196 } 197 if (mLinkLayerAddr != null) { 198 spaceRequired += NetlinkConstants.alignedLengthOf( 199 StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length); 200 } 201 // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO 202 // attributes appended. Fix later, if necessary. 203 return spaceRequired; 204 } 205 packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer)206 private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) { 207 final StructNlAttr nlAttr = new StructNlAttr(); 208 nlAttr.nla_type = nlType; 209 nlAttr.nla_value = nlValue; 210 nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length); 211 nlAttr.pack(byteBuffer); 212 } 213 214 /** 215 * Write a neighbor discovery netlink message to {@link ByteBuffer}. 216 */ pack(ByteBuffer byteBuffer)217 public void pack(ByteBuffer byteBuffer) { 218 getHeader().pack(byteBuffer); 219 mNdmsg.pack(byteBuffer); 220 221 if (mDestination != null) { 222 packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer); 223 } 224 if (mLinkLayerAddr != null) { 225 packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer); 226 } 227 } 228 229 @Override toString()230 public String toString() { 231 final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress(); 232 return "RtNetlinkNeighborMessage{ " 233 + "nlmsghdr{" 234 + (mHeader == null ? "" : mHeader.toString(OsConstants.NETLINK_ROUTE)) + "}, " 235 + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, " 236 + "destination{" + ipLiteral + "} " 237 + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} " 238 + "probes{" + mNumProbes + "} " 239 + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} " 240 + "}"; 241 } 242 } 243