Loading common/moduleutils/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ filegroup { srcs: [ "src/android/net/ip/InterfaceController.java", "src/android/net/ip/IpNeighborMonitor.java", "src/android/net/ip/NetlinkMonitor.java", "src/android/net/netlink/*.java", "src/android/net/shared/NetdUtils.java", "src/android/net/shared/RouteUtils.java", Loading common/moduleutils/src/android/net/ip/IpNeighborMonitor.java +7 −80 Original line number Diff line number Diff line Loading @@ -19,35 +19,20 @@ package android.net.ip; import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; import static android.net.netlink.NetlinkConstants.hexify; import static android.net.netlink.NetlinkConstants.stringForNlMsgType; import static android.net.util.SocketUtils.makeNetlinkSocketAddress; import static android.system.OsConstants.AF_NETLINK; import static android.system.OsConstants.NETLINK_ROUTE; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import android.net.MacAddress; import android.net.netlink.NetlinkErrorMessage; import android.net.netlink.NetlinkMessage; import android.net.netlink.NetlinkSocket; import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdMsg; import android.net.util.PacketReader; import android.net.util.SharedLog; import android.net.util.SocketUtils; import android.os.Handler; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.InetAddress; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.StringJoiner; Loading @@ -61,7 +46,7 @@ import java.util.StringJoiner; * * @hide */ public class IpNeighborMonitor extends PacketReader { public class IpNeighborMonitor extends NetlinkMonitor { private static final String TAG = IpNeighborMonitor.class.getSimpleName(); private static final boolean DBG = false; private static final boolean VDBG = false; Loading Loading @@ -129,85 +114,27 @@ public class IpNeighborMonitor extends PacketReader { } } // TODO: move NetworkStackUtils.closeSocketQuietly to somewhere accessible to this file. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } public interface NeighborEventConsumer { // Every neighbor event received on the netlink socket is passed in // here. Subclasses should filter for events of interest. public void accept(NeighborEvent event); } private final SharedLog mLog; private final NeighborEventConsumer mConsumer; public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(TAG); super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH); mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; } @Override protected FileDescriptor createFd() { FileDescriptor fd = null; try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE); Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)); NetlinkSocket.connectToKernel(fd); if (VDBG) { final SocketAddress nlAddr = Os.getsockname(fd); Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}"); } } catch (ErrnoException|SocketException e) { logError("Failed to create rtnetlink socket", e); closeSocketQuietly(fd); return null; } return fd; } @Override protected void handlePacket(byte[] recvbuf, int length) { final long whenMs = SystemClock.elapsedRealtime(); final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); byteBuffer.order(ByteOrder.nativeOrder()); parseNetlinkMessageBuffer(byteBuffer, whenMs); } private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { while (byteBuffer.remaining() > 0) { final int position = byteBuffer.position(); final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); if (nlMsg == null || nlMsg.getHeader() == null) { byteBuffer.position(position); mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); break; } if (nlMsg instanceof NetlinkErrorMessage) { mLog.e("netlink error: " + nlMsg); continue; } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { mLog.i("non-rtnetlink neighbor msg: " + nlMsg); continue; } evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); } public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) { if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { mLog.e("non-rtnetlink neighbor msg: " + nlMsg); return; } private void evaluateRtNetlinkNeighborMessage( RtNetlinkNeighborMessage neighMsg, long whenMs) { final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg; final short msgType = neighMsg.getHeader().nlmsg_type; final StructNdMsg ndMsg = neighMsg.getNdHeader(); if (ndMsg == null) { Loading common/moduleutils/src/android/net/ip/NetlinkMonitor.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.ip; import static android.net.netlink.NetlinkConstants.hexify; import static android.net.util.SocketUtils.makeNetlinkSocketAddress; import static android.system.OsConstants.AF_NETLINK; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import android.annotation.NonNull; import android.net.netlink.NetlinkErrorMessage; import android.net.netlink.NetlinkMessage; import android.net.netlink.NetlinkSocket; import android.net.util.PacketReader; import android.net.util.SharedLog; import android.net.util.SocketUtils; import android.os.Handler; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A simple base class to listen for netlink broadcasts. * * Opens a netlink socket of the given family and binds to the specified groups. Polls the socket * from the event loop of the passed-in {@link Handler}, and calls the subclass-defined * {@link #processNetlinkMessage} method on the handler thread for each netlink message that * arrives. Currently ignores all netlink errors. */ public class NetlinkMonitor extends PacketReader { protected final SharedLog mLog; protected final String mTag; private final int mFamily; private final int mBindGroups; private static final boolean DBG = false; /** * Constructs a new {@code NetlinkMonitor} instance. * * @param h The Handler on which to poll for messages and on which to call * {@link #processNetlinkMessage}. * @param log A SharedLog to log to. * @param tag The log tag to use for log messages. * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}. * @param bindGroups the netlink groups to bind to. */ public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, int family, int bindGroups) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(tag); mTag = tag; mFamily = family; mBindGroups = bindGroups; } @Override protected FileDescriptor createFd() { FileDescriptor fd = null; try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily); Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups)); NetlinkSocket.connectToKernel(fd); if (DBG) { final SocketAddress nlAddr = Os.getsockname(fd); Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}"); } } catch (ErrnoException | SocketException e) { logError("Failed to create rtnetlink socket", e); closeSocketQuietly(fd); return null; } return fd; } @Override protected void handlePacket(byte[] recvbuf, int length) { final long whenMs = SystemClock.elapsedRealtime(); final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); byteBuffer.order(ByteOrder.nativeOrder()); while (byteBuffer.remaining() > 0) { final int position = byteBuffer.position(); final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); if (nlMsg == null || nlMsg.getHeader() == null) { byteBuffer.position(position); mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); break; } if (nlMsg instanceof NetlinkErrorMessage) { mLog.e("netlink error: " + nlMsg); continue; } processNetlinkMessage(nlMsg, whenMs); } } // TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } /** * Processes one netlink message. Must be overridden by subclasses. * @param nlMsg the message to process. * @param whenMs the timestamp, as measured by {@link SystemClock#elapsedRealtime}, when the * message was received. */ protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { } } common/netlinkclient/src/android/net/netlink/NdOption.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.netlink; import java.nio.ByteBuffer; /** * Base class for IPv6 neighbour discovery options. */ public class NdOption { public static final int STRUCT_SIZE = 2; /** The option type. */ public final byte type; /** The length of the option in 8-byte units. Actually an unsigned 8-bit integer */ public final int length; /** Constructs a new NdOption. */ public NdOption(byte type, int length) { this.type = type; this.length = length; } /** * Parses a neighbour discovery option. * * Parses (and consumes) the option if it is of a known type. If the option is of an unknown * type, advances the buffer (so the caller can continue parsing if desired) and returns * {@link #UNKNOWN}. If the option claims a length of 0, returns null because parsing cannot * continue. * * No checks are performed on the length other than ensuring it is not 0, so if a caller wants * to deal with options that might overflow the structure that contains them, it must explicitly * set the buffer's limit to the position at which that structure ends. * * @param buf the buffer to parse. * @return a subclass of {@link NdOption}, or {@code null} for an unknown or malformed option. */ public static NdOption parse(ByteBuffer buf) { if (buf == null || buf.remaining() < STRUCT_SIZE) return null; // Peek the type without advancing the buffer. byte type = buf.get(buf.position()); int length = Byte.toUnsignedInt(buf.get(buf.position() + 1)); if (length == 0) return null; switch (type) { case StructNdOptPref64.TYPE: return StructNdOptPref64.parse(buf); default: int newPosition = Math.min(buf.limit(), buf.position() + length * 8); buf.position(newPosition); return UNKNOWN; } } @Override public String toString() { return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length); } public static final NdOption UNKNOWN = new NdOption((byte) 0, 0); } common/netlinkclient/src/android/net/netlink/NduseroptMessage.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.netlink; import static android.system.OsConstants.AF_INET6; import androidx.annotation.NonNull; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages. */ public class NduseroptMessage extends NetlinkMessage { public static final int STRUCT_SIZE = 16; static final int NDUSEROPT_SRCADDR = 1; /** The address family. Presumably always AF_INET6. */ public final byte family; /** * The total length in bytes of the options that follow this structure. * Actually a 16-bit unsigned integer. */ public final int opts_len; /** The interface index on which the options were received. */ public final int ifindex; /** The ICMP type of the packet that contained the options. */ public final byte icmp_type; /** The ICMP code of the packet that contained the options. */ public final byte icmp_code; /** * ND option that was in this message. * Even though the length field is called "opts_len", the kernel only ever sends one option per * message. It is unlikely that this will ever change as it would break existing userspace code. * But if it does, we can simply update this code, since userspace is typically newer than the * kernel. */ public final NdOption option; /** The IP address that sent the packet containing the option. */ public final InetAddress srcaddr; NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) throws UnknownHostException { super(header); // The structure itself. buf.order(ByteOrder.nativeOrder()); family = buf.get(); buf.get(); // Skip 1 byte of padding. opts_len = Short.toUnsignedInt(buf.getShort()); ifindex = buf.getInt(); icmp_type = buf.get(); icmp_code = buf.get(); buf.order(ByteOrder.BIG_ENDIAN); buf.position(buf.position() + 6); // Skip 6 bytes of padding. // The ND option. // Ensure we don't read past opts_len even if the option length is invalid. // Note that this check is not really necessary since if the option length is not valid, // this struct won't be very useful to the caller. int oldLimit = buf.limit(); buf.limit(STRUCT_SIZE + opts_len); try { option = NdOption.parse(buf); } finally { buf.limit(oldLimit); } // The source address. int newPosition = STRUCT_SIZE + opts_len; if (newPosition >= buf.limit()) { throw new IllegalArgumentException("ND options extend past end of buffer"); } buf.position(newPosition); StructNlAttr nla = StructNlAttr.parse(buf); if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) { throw new IllegalArgumentException("Invalid source address in ND useropt"); } if (family == AF_INET6) { // InetAddress.getByAddress only looks at the ifindex if the address type needs one. srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex); } else { srcaddr = InetAddress.getByAddress(nla.nla_value); } } /** * Parses a StructNduseroptmsg from a {@link ByteBuffer}. * * @param header the netlink message header. * @param buf The buffer from which to parse the option. The buffer's byte order must be * {@link java.nio.ByteOrder#BIG_ENDIAN}. * @return the parsed option, or {@code null} if the option could not be parsed successfully * (for example, if it was truncated, or if the prefix length code was wrong). */ public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) { if (buf == null || buf.remaining() < STRUCT_SIZE) return null; try { return new NduseroptMessage(header, buf); } catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) { // Not great, but better than throwing an exception that might crash the caller. // Convention in this package is that null indicates that the option was truncated, so // callers must already handle it. return null; } } @Override public String toString() { return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)", family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type), Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress()); } } Loading
common/moduleutils/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ filegroup { srcs: [ "src/android/net/ip/InterfaceController.java", "src/android/net/ip/IpNeighborMonitor.java", "src/android/net/ip/NetlinkMonitor.java", "src/android/net/netlink/*.java", "src/android/net/shared/NetdUtils.java", "src/android/net/shared/RouteUtils.java", Loading
common/moduleutils/src/android/net/ip/IpNeighborMonitor.java +7 −80 Original line number Diff line number Diff line Loading @@ -19,35 +19,20 @@ package android.net.ip; import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; import static android.net.netlink.NetlinkConstants.hexify; import static android.net.netlink.NetlinkConstants.stringForNlMsgType; import static android.net.util.SocketUtils.makeNetlinkSocketAddress; import static android.system.OsConstants.AF_NETLINK; import static android.system.OsConstants.NETLINK_ROUTE; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import android.net.MacAddress; import android.net.netlink.NetlinkErrorMessage; import android.net.netlink.NetlinkMessage; import android.net.netlink.NetlinkSocket; import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdMsg; import android.net.util.PacketReader; import android.net.util.SharedLog; import android.net.util.SocketUtils; import android.os.Handler; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.InetAddress; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.StringJoiner; Loading @@ -61,7 +46,7 @@ import java.util.StringJoiner; * * @hide */ public class IpNeighborMonitor extends PacketReader { public class IpNeighborMonitor extends NetlinkMonitor { private static final String TAG = IpNeighborMonitor.class.getSimpleName(); private static final boolean DBG = false; private static final boolean VDBG = false; Loading Loading @@ -129,85 +114,27 @@ public class IpNeighborMonitor extends PacketReader { } } // TODO: move NetworkStackUtils.closeSocketQuietly to somewhere accessible to this file. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } public interface NeighborEventConsumer { // Every neighbor event received on the netlink socket is passed in // here. Subclasses should filter for events of interest. public void accept(NeighborEvent event); } private final SharedLog mLog; private final NeighborEventConsumer mConsumer; public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(TAG); super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH); mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; } @Override protected FileDescriptor createFd() { FileDescriptor fd = null; try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE); Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)); NetlinkSocket.connectToKernel(fd); if (VDBG) { final SocketAddress nlAddr = Os.getsockname(fd); Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}"); } } catch (ErrnoException|SocketException e) { logError("Failed to create rtnetlink socket", e); closeSocketQuietly(fd); return null; } return fd; } @Override protected void handlePacket(byte[] recvbuf, int length) { final long whenMs = SystemClock.elapsedRealtime(); final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); byteBuffer.order(ByteOrder.nativeOrder()); parseNetlinkMessageBuffer(byteBuffer, whenMs); } private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { while (byteBuffer.remaining() > 0) { final int position = byteBuffer.position(); final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); if (nlMsg == null || nlMsg.getHeader() == null) { byteBuffer.position(position); mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); break; } if (nlMsg instanceof NetlinkErrorMessage) { mLog.e("netlink error: " + nlMsg); continue; } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { mLog.i("non-rtnetlink neighbor msg: " + nlMsg); continue; } evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); } public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) { if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { mLog.e("non-rtnetlink neighbor msg: " + nlMsg); return; } private void evaluateRtNetlinkNeighborMessage( RtNetlinkNeighborMessage neighMsg, long whenMs) { final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg; final short msgType = neighMsg.getHeader().nlmsg_type; final StructNdMsg ndMsg = neighMsg.getNdHeader(); if (ndMsg == null) { Loading
common/moduleutils/src/android/net/ip/NetlinkMonitor.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.ip; import static android.net.netlink.NetlinkConstants.hexify; import static android.net.util.SocketUtils.makeNetlinkSocketAddress; import static android.system.OsConstants.AF_NETLINK; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import android.annotation.NonNull; import android.net.netlink.NetlinkErrorMessage; import android.net.netlink.NetlinkMessage; import android.net.netlink.NetlinkSocket; import android.net.util.PacketReader; import android.net.util.SharedLog; import android.net.util.SocketUtils; import android.os.Handler; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A simple base class to listen for netlink broadcasts. * * Opens a netlink socket of the given family and binds to the specified groups. Polls the socket * from the event loop of the passed-in {@link Handler}, and calls the subclass-defined * {@link #processNetlinkMessage} method on the handler thread for each netlink message that * arrives. Currently ignores all netlink errors. */ public class NetlinkMonitor extends PacketReader { protected final SharedLog mLog; protected final String mTag; private final int mFamily; private final int mBindGroups; private static final boolean DBG = false; /** * Constructs a new {@code NetlinkMonitor} instance. * * @param h The Handler on which to poll for messages and on which to call * {@link #processNetlinkMessage}. * @param log A SharedLog to log to. * @param tag The log tag to use for log messages. * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}. * @param bindGroups the netlink groups to bind to. */ public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, int family, int bindGroups) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(tag); mTag = tag; mFamily = family; mBindGroups = bindGroups; } @Override protected FileDescriptor createFd() { FileDescriptor fd = null; try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily); Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups)); NetlinkSocket.connectToKernel(fd); if (DBG) { final SocketAddress nlAddr = Os.getsockname(fd); Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}"); } } catch (ErrnoException | SocketException e) { logError("Failed to create rtnetlink socket", e); closeSocketQuietly(fd); return null; } return fd; } @Override protected void handlePacket(byte[] recvbuf, int length) { final long whenMs = SystemClock.elapsedRealtime(); final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); byteBuffer.order(ByteOrder.nativeOrder()); while (byteBuffer.remaining() > 0) { final int position = byteBuffer.position(); final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); if (nlMsg == null || nlMsg.getHeader() == null) { byteBuffer.position(position); mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); break; } if (nlMsg instanceof NetlinkErrorMessage) { mLog.e("netlink error: " + nlMsg); continue; } processNetlinkMessage(nlMsg, whenMs); } } // TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } /** * Processes one netlink message. Must be overridden by subclasses. * @param nlMsg the message to process. * @param whenMs the timestamp, as measured by {@link SystemClock#elapsedRealtime}, when the * message was received. */ protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { } }
common/netlinkclient/src/android/net/netlink/NdOption.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.netlink; import java.nio.ByteBuffer; /** * Base class for IPv6 neighbour discovery options. */ public class NdOption { public static final int STRUCT_SIZE = 2; /** The option type. */ public final byte type; /** The length of the option in 8-byte units. Actually an unsigned 8-bit integer */ public final int length; /** Constructs a new NdOption. */ public NdOption(byte type, int length) { this.type = type; this.length = length; } /** * Parses a neighbour discovery option. * * Parses (and consumes) the option if it is of a known type. If the option is of an unknown * type, advances the buffer (so the caller can continue parsing if desired) and returns * {@link #UNKNOWN}. If the option claims a length of 0, returns null because parsing cannot * continue. * * No checks are performed on the length other than ensuring it is not 0, so if a caller wants * to deal with options that might overflow the structure that contains them, it must explicitly * set the buffer's limit to the position at which that structure ends. * * @param buf the buffer to parse. * @return a subclass of {@link NdOption}, or {@code null} for an unknown or malformed option. */ public static NdOption parse(ByteBuffer buf) { if (buf == null || buf.remaining() < STRUCT_SIZE) return null; // Peek the type without advancing the buffer. byte type = buf.get(buf.position()); int length = Byte.toUnsignedInt(buf.get(buf.position() + 1)); if (length == 0) return null; switch (type) { case StructNdOptPref64.TYPE: return StructNdOptPref64.parse(buf); default: int newPosition = Math.min(buf.limit(), buf.position() + length * 8); buf.position(newPosition); return UNKNOWN; } } @Override public String toString() { return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length); } public static final NdOption UNKNOWN = new NdOption((byte) 0, 0); }
common/netlinkclient/src/android/net/netlink/NduseroptMessage.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 android.net.netlink; import static android.system.OsConstants.AF_INET6; import androidx.annotation.NonNull; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages. */ public class NduseroptMessage extends NetlinkMessage { public static final int STRUCT_SIZE = 16; static final int NDUSEROPT_SRCADDR = 1; /** The address family. Presumably always AF_INET6. */ public final byte family; /** * The total length in bytes of the options that follow this structure. * Actually a 16-bit unsigned integer. */ public final int opts_len; /** The interface index on which the options were received. */ public final int ifindex; /** The ICMP type of the packet that contained the options. */ public final byte icmp_type; /** The ICMP code of the packet that contained the options. */ public final byte icmp_code; /** * ND option that was in this message. * Even though the length field is called "opts_len", the kernel only ever sends one option per * message. It is unlikely that this will ever change as it would break existing userspace code. * But if it does, we can simply update this code, since userspace is typically newer than the * kernel. */ public final NdOption option; /** The IP address that sent the packet containing the option. */ public final InetAddress srcaddr; NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) throws UnknownHostException { super(header); // The structure itself. buf.order(ByteOrder.nativeOrder()); family = buf.get(); buf.get(); // Skip 1 byte of padding. opts_len = Short.toUnsignedInt(buf.getShort()); ifindex = buf.getInt(); icmp_type = buf.get(); icmp_code = buf.get(); buf.order(ByteOrder.BIG_ENDIAN); buf.position(buf.position() + 6); // Skip 6 bytes of padding. // The ND option. // Ensure we don't read past opts_len even if the option length is invalid. // Note that this check is not really necessary since if the option length is not valid, // this struct won't be very useful to the caller. int oldLimit = buf.limit(); buf.limit(STRUCT_SIZE + opts_len); try { option = NdOption.parse(buf); } finally { buf.limit(oldLimit); } // The source address. int newPosition = STRUCT_SIZE + opts_len; if (newPosition >= buf.limit()) { throw new IllegalArgumentException("ND options extend past end of buffer"); } buf.position(newPosition); StructNlAttr nla = StructNlAttr.parse(buf); if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) { throw new IllegalArgumentException("Invalid source address in ND useropt"); } if (family == AF_INET6) { // InetAddress.getByAddress only looks at the ifindex if the address type needs one. srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex); } else { srcaddr = InetAddress.getByAddress(nla.nla_value); } } /** * Parses a StructNduseroptmsg from a {@link ByteBuffer}. * * @param header the netlink message header. * @param buf The buffer from which to parse the option. The buffer's byte order must be * {@link java.nio.ByteOrder#BIG_ENDIAN}. * @return the parsed option, or {@code null} if the option could not be parsed successfully * (for example, if it was truncated, or if the prefix length code was wrong). */ public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) { if (buf == null || buf.remaining() < STRUCT_SIZE) return null; try { return new NduseroptMessage(header, buf); } catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) { // Not great, but better than throwing an exception that might crash the caller. // Convention in this package is that null indicates that the option was truncated, so // callers must already handle it. return null; } } @Override public String toString() { return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)", family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type), Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress()); } }