Loading services/core/java/com/android/server/connectivity/tethering/OffloadController.java +135 −14 Original line number Diff line number Diff line Loading @@ -30,6 +30,10 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; import android.net.util.IpUtils; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; Loading @@ -37,10 +41,12 @@ import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; import android.text.TextUtils; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import com.android.internal.util.IndentingPrintWriter; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import java.net.Inet4Address; import java.net.Inet6Address; Loading @@ -63,6 +69,7 @@ import java.util.concurrent.TimeUnit; */ public class OffloadController { private static final String TAG = OffloadController.class.getSimpleName(); private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); Loading Loading @@ -96,6 +103,9 @@ public class OffloadController { // includes upstream interfaces that have a quota set. private HashMap<String, Long> mInterfaceQuotas = new HashMap<>(); private int mNatUpdateCallbacksReceived; private int mNatUpdateNetlinkErrors; public OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) { mHandler = h; Loading @@ -115,12 +125,12 @@ public class OffloadController { } } public void start() { if (started()) return; public boolean start() { if (started()) return true; if (isOffloadDisabled()) { mLog.i("tethering offload disabled"); return; return false; } if (!mConfigInitialized) { Loading @@ -128,11 +138,14 @@ public class OffloadController { if (!mConfigInitialized) { mLog.i("tethering offload config not supported"); stop(); return; return false; } } mControlInitialized = mHwInterface.initOffloadControl( // OffloadHardwareInterface guarantees that these callback // methods are called on the handler passed to it, which is the // same as mHandler, as coordinated by the setup in Tethering. new OffloadHardwareInterface.ControlCallback() { @Override public void onStarted() { Loading Loading @@ -203,15 +216,20 @@ public class OffloadController { String srcAddr, int srcPort, String dstAddr, int dstPort) { if (!started()) return; mLog.log(String.format("NAT timeout update: %s (%s,%s) -> (%s,%s)", proto, srcAddr, srcPort, dstAddr, dstPort)); updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); } }); if (!mControlInitialized) { final boolean isStarted = started(); if (!isStarted) { mLog.i("tethering offload control not supported"); stop(); } } else { mLog.log("tethering offload started"); mNatUpdateCallbacksReceived = 0; mNatUpdateNetlinkErrors = 0; } return isStarted; } public void stop() { Loading @@ -227,6 +245,10 @@ public class OffloadController { if (wasStarted) mLog.log("tethering offload stopped"); } private boolean started() { return mConfigInitialized && mControlInitialized; } private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { @Override public NetworkStats getTetherStats(int how) { Loading Loading @@ -402,10 +424,6 @@ public class OffloadController { mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); } private boolean started() { return mConfigInitialized && mControlInitialized; } private boolean pushUpstreamParameters(String prevUpstream) { final String iface = currentUpstreamInterface(); Loading Loading @@ -516,10 +534,113 @@ public class OffloadController { pw.println("Offload disabled"); return; } pw.println("Offload HALs " + (started() ? "started" : "not started")); final boolean isStarted = started(); pw.println("Offload HALs " + (isStarted ? "started" : "not started")); LinkProperties lp = mUpstreamLinkProperties; String upstream = (lp != null) ? lp.getInterfaceName() : null; pw.println("Current upstream: " + upstream); pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); pw.println("NAT timeout update callbacks received during the " + (isStarted ? "current" : "last") + " offload session: " + mNatUpdateCallbacksReceived); pw.println("NAT timeout update netlink errors during the " + (isStarted ? "current" : "last") + " offload session: " + mNatUpdateNetlinkErrors); } private void updateNatTimeout( int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { final String protoName = protoNameFor(proto); if (protoName == null) { mLog.e("Unknown NAT update callback protocol: " + proto); return; } final Inet4Address src = parseIPv4Address(srcAddr); if (src == null) { mLog.e("Failed to parse IPv4 address: " + srcAddr); return; } if (!IpUtils.isValidUdpOrTcpPort(srcPort)) { mLog.e("Invalid src port: " + srcPort); return; } final Inet4Address dst = parseIPv4Address(dstAddr); if (dst == null) { mLog.e("Failed to parse IPv4 address: " + dstAddr); return; } if (!IpUtils.isValidUdpOrTcpPort(dstPort)) { mLog.e("Invalid dst port: " + dstPort); return; } mNatUpdateCallbacksReceived++; if (DBG) { mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)", protoName, srcAddr, srcPort, dstAddr, dstPort)); } final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( proto, src, srcPort, dst, dstPort, timeoutSec); try { NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); } catch (ErrnoException e) { mNatUpdateNetlinkErrors++; mLog.e("Error updating NAT conntrack entry: " + e + ", msg: " + NetlinkConstants.hexify(msg)); mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); } } private static Inet4Address parseIPv4Address(String addrString) { try { final InetAddress ip = InetAddress.parseNumericAddress(addrString); // TODO: Consider other sanitization steps here, including perhaps: // not eql to 0.0.0.0 // not within 169.254.0.0/16 // not within ::ffff:0.0.0.0/96 // not within ::/96 // et cetera. if (ip instanceof Inet4Address) { return (Inet4Address) ip; } } catch (IllegalArgumentException iae) {} return null; } private static String protoNameFor(int proto) { // OsConstants values are not constant expressions; no switch statement. if (proto == OsConstants.IPPROTO_UDP) { return "UDP"; } else if (proto == OsConstants.IPPROTO_TCP) { return "TCP"; } return null; } private static int connectionTimeoutUpdateSecondsFor(int proto) { // TODO: Replace this with more thoughtful work, perhaps reading from // and maybe writing to any required // // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} // // entries. TBD. if (proto == OsConstants.IPPROTO_TCP) { // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established return 432000; } else { // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream return 180; } } } services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +14 −1 Original line number Diff line number Diff line Loading @@ -21,10 +21,12 @@ import static com.android.internal.util.BitUtils.uint16; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; import android.os.Handler; import android.os.RemoteException; import android.net.util.SharedLog; import android.system.OsConstants; import java.util.ArrayList; Loading Loading @@ -327,13 +329,24 @@ public class OffloadHardwareInterface { public void updateTimeout(NatTimeoutUpdate params) { handler.post(() -> { controlCb.onNatTimeoutUpdate( params.proto, networkProtocolToOsConstant(params.proto), params.src.addr, uint16(params.src.port), params.dst.addr, uint16(params.dst.port)); }); } } private static int networkProtocolToOsConstant(int proto) { switch (proto) { case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; default: // The caller checks this value and will log an error. Just make // sure it won't collide with valid OsContants.IPPROTO_* values. return -Math.abs(proto); } } private static class CbResults { boolean success; String errMsg; Loading services/net/java/android/net/ip/IpReachabilityMonitor.java +6 −36 Original line number Diff line number Diff line Loading @@ -183,44 +183,14 @@ public class IpReachabilityMonitor { final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); int errno = -OsConstants.EPROTO; try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) { final long IO_TIMEOUT = 300L; nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && (((NetlinkErrorMessage) response).getNlMsgError() != null)) { errno = ((NetlinkErrorMessage) response).getNlMsgError().error; if (errno != 0) { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + response.toString()); } } else { String errmsg; if (response == null) { bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { errmsg = response.toString(); } Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg); } try { NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); } catch (ErrnoException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -e.errno; } catch (InterruptedIOException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -OsConstants.ETIMEDOUT; } catch (SocketException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -OsConstants.EIO; Log.e(TAG, "Error " + msgSnippet + ": " + e); return -e.errno; } return errno; return 0; } public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) { Loading services/net/java/android/net/netlink/ConntrackMessage.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.net.netlink.NetlinkConstants.alignedLengthOf; import static android.net.netlink.StructNlAttr.makeNestedType; import static android.net.netlink.StructNlAttr.NLA_HEADERLEN; import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK; import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE; import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; import static android.net.util.NetworkConstants.IPV4_ADDR_LEN; import static java.nio.ByteOrder.BIG_ENDIAN; import android.system.OsConstants; import android.util.Log; import libcore.io.SizeOf; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A NetlinkMessage subclass for netlink conntrack messages. * * see also: <linux_src>/include/uapi/linux/netfilter/nfnetlink_conntrack.h * * @hide */ public class ConntrackMessage extends NetlinkMessage { public static final int STRUCT_SIZE = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; public static final short NFNL_SUBSYS_CTNETLINK = 1; public static final short IPCTNL_MSG_CT_NEW = 0; // enum ctattr_type public static final short CTA_TUPLE_ORIG = 1; public static final short CTA_TUPLE_REPLY = 2; public static final short CTA_TIMEOUT = 7; // enum ctattr_tuple public static final short CTA_TUPLE_IP = 1; public static final short CTA_TUPLE_PROTO = 2; // enum ctattr_ip public static final short CTA_IP_V4_SRC = 1; public static final short CTA_IP_V4_DST = 2; // enum ctattr_l4proto public static final short CTA_PROTO_NUM = 1; public static final short CTA_PROTO_SRC_PORT = 2; public static final short CTA_PROTO_DST_PORT = 3; public static byte[] newIPv4TimeoutUpdateRequest( int proto, Inet4Address src, int sport, Inet4Address dst, int dport, int timeoutSec) { // *** STYLE WARNING *** // // Code below this point uses extra block indentation to highlight the // packing of nested tuple netlink attribute types. final StructNlAttr ctaTupleOrig = new StructNlAttr(CTA_TUPLE_ORIG, new StructNlAttr(CTA_TUPLE_IP, new StructNlAttr(CTA_IP_V4_SRC, src), new StructNlAttr(CTA_IP_V4_DST, dst)), new StructNlAttr(CTA_TUPLE_PROTO, new StructNlAttr(CTA_PROTO_NUM, (byte) proto), new StructNlAttr(CTA_PROTO_SRC_PORT, (short) sport, BIG_ENDIAN), new StructNlAttr(CTA_PROTO_DST_PORT, (short) dport, BIG_ENDIAN))); final StructNlAttr ctaTimeout = new StructNlAttr(CTA_TIMEOUT, timeoutSec, BIG_ENDIAN); final int payloadLength = ctaTupleOrig.getAlignedLength() + ctaTimeout.getAlignedLength(); final byte[] bytes = new byte[STRUCT_SIZE + payloadLength]; final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); byteBuffer.order(ByteOrder.nativeOrder()); final ConntrackMessage ctmsg = new ConntrackMessage(); ctmsg.mHeader.nlmsg_len = bytes.length; ctmsg.mHeader.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; ctmsg.mHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; ctmsg.mHeader.nlmsg_seq = 1; ctmsg.pack(byteBuffer); ctaTupleOrig.pack(byteBuffer); ctaTimeout.pack(byteBuffer); return bytes; } protected StructNfGenMsg mNfGenMsg; private ConntrackMessage() { super(new StructNlMsgHdr()); mNfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET); } public void pack(ByteBuffer byteBuffer) { mHeader.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); } } services/net/java/android/net/netlink/NetlinkSocket.java +41 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,47 @@ public class NetlinkSocket implements Closeable { private long mLastRecvTimeoutMs; private long mLastSendTimeoutMs; public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) { final long IO_TIMEOUT = 300L; nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && (((NetlinkErrorMessage) response).getNlMsgError() != null)) { final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error; if (errno != 0) { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. Log.e(TAG, errPrefix + ", errmsg=" + response.toString()); // Note: convert kernel errnos (negative) into userspace errnos (positive). throw new ErrnoException(response.toString(), Math.abs(errno)); } } else { final String errmsg; if (response == null) { bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { errmsg = response.toString(); } Log.e(TAG, errPrefix + ", errmsg=" + errmsg); throw new ErrnoException(errmsg, OsConstants.EPROTO); } } catch (InterruptedIOException e) { Log.e(TAG, errPrefix, e); throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e); } catch (SocketException e) { Log.e(TAG, errPrefix, e); throw new ErrnoException(errPrefix, OsConstants.EIO, e); } } public NetlinkSocket(int nlProto) throws ErrnoException { mDescriptor = Os.socket( OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); Loading Loading
services/core/java/com/android/server/connectivity/tethering/OffloadController.java +135 −14 Original line number Diff line number Diff line Loading @@ -30,6 +30,10 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; import android.net.util.IpUtils; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; Loading @@ -37,10 +41,12 @@ import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; import android.text.TextUtils; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import com.android.internal.util.IndentingPrintWriter; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import java.net.Inet4Address; import java.net.Inet6Address; Loading @@ -63,6 +69,7 @@ import java.util.concurrent.TimeUnit; */ public class OffloadController { private static final String TAG = OffloadController.class.getSimpleName(); private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); Loading Loading @@ -96,6 +103,9 @@ public class OffloadController { // includes upstream interfaces that have a quota set. private HashMap<String, Long> mInterfaceQuotas = new HashMap<>(); private int mNatUpdateCallbacksReceived; private int mNatUpdateNetlinkErrors; public OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) { mHandler = h; Loading @@ -115,12 +125,12 @@ public class OffloadController { } } public void start() { if (started()) return; public boolean start() { if (started()) return true; if (isOffloadDisabled()) { mLog.i("tethering offload disabled"); return; return false; } if (!mConfigInitialized) { Loading @@ -128,11 +138,14 @@ public class OffloadController { if (!mConfigInitialized) { mLog.i("tethering offload config not supported"); stop(); return; return false; } } mControlInitialized = mHwInterface.initOffloadControl( // OffloadHardwareInterface guarantees that these callback // methods are called on the handler passed to it, which is the // same as mHandler, as coordinated by the setup in Tethering. new OffloadHardwareInterface.ControlCallback() { @Override public void onStarted() { Loading Loading @@ -203,15 +216,20 @@ public class OffloadController { String srcAddr, int srcPort, String dstAddr, int dstPort) { if (!started()) return; mLog.log(String.format("NAT timeout update: %s (%s,%s) -> (%s,%s)", proto, srcAddr, srcPort, dstAddr, dstPort)); updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); } }); if (!mControlInitialized) { final boolean isStarted = started(); if (!isStarted) { mLog.i("tethering offload control not supported"); stop(); } } else { mLog.log("tethering offload started"); mNatUpdateCallbacksReceived = 0; mNatUpdateNetlinkErrors = 0; } return isStarted; } public void stop() { Loading @@ -227,6 +245,10 @@ public class OffloadController { if (wasStarted) mLog.log("tethering offload stopped"); } private boolean started() { return mConfigInitialized && mControlInitialized; } private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { @Override public NetworkStats getTetherStats(int how) { Loading Loading @@ -402,10 +424,6 @@ public class OffloadController { mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); } private boolean started() { return mConfigInitialized && mControlInitialized; } private boolean pushUpstreamParameters(String prevUpstream) { final String iface = currentUpstreamInterface(); Loading Loading @@ -516,10 +534,113 @@ public class OffloadController { pw.println("Offload disabled"); return; } pw.println("Offload HALs " + (started() ? "started" : "not started")); final boolean isStarted = started(); pw.println("Offload HALs " + (isStarted ? "started" : "not started")); LinkProperties lp = mUpstreamLinkProperties; String upstream = (lp != null) ? lp.getInterfaceName() : null; pw.println("Current upstream: " + upstream); pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); pw.println("NAT timeout update callbacks received during the " + (isStarted ? "current" : "last") + " offload session: " + mNatUpdateCallbacksReceived); pw.println("NAT timeout update netlink errors during the " + (isStarted ? "current" : "last") + " offload session: " + mNatUpdateNetlinkErrors); } private void updateNatTimeout( int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { final String protoName = protoNameFor(proto); if (protoName == null) { mLog.e("Unknown NAT update callback protocol: " + proto); return; } final Inet4Address src = parseIPv4Address(srcAddr); if (src == null) { mLog.e("Failed to parse IPv4 address: " + srcAddr); return; } if (!IpUtils.isValidUdpOrTcpPort(srcPort)) { mLog.e("Invalid src port: " + srcPort); return; } final Inet4Address dst = parseIPv4Address(dstAddr); if (dst == null) { mLog.e("Failed to parse IPv4 address: " + dstAddr); return; } if (!IpUtils.isValidUdpOrTcpPort(dstPort)) { mLog.e("Invalid dst port: " + dstPort); return; } mNatUpdateCallbacksReceived++; if (DBG) { mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)", protoName, srcAddr, srcPort, dstAddr, dstPort)); } final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( proto, src, srcPort, dst, dstPort, timeoutSec); try { NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); } catch (ErrnoException e) { mNatUpdateNetlinkErrors++; mLog.e("Error updating NAT conntrack entry: " + e + ", msg: " + NetlinkConstants.hexify(msg)); mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); } } private static Inet4Address parseIPv4Address(String addrString) { try { final InetAddress ip = InetAddress.parseNumericAddress(addrString); // TODO: Consider other sanitization steps here, including perhaps: // not eql to 0.0.0.0 // not within 169.254.0.0/16 // not within ::ffff:0.0.0.0/96 // not within ::/96 // et cetera. if (ip instanceof Inet4Address) { return (Inet4Address) ip; } } catch (IllegalArgumentException iae) {} return null; } private static String protoNameFor(int proto) { // OsConstants values are not constant expressions; no switch statement. if (proto == OsConstants.IPPROTO_UDP) { return "UDP"; } else if (proto == OsConstants.IPPROTO_TCP) { return "TCP"; } return null; } private static int connectionTimeoutUpdateSecondsFor(int proto) { // TODO: Replace this with more thoughtful work, perhaps reading from // and maybe writing to any required // // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} // // entries. TBD. if (proto == OsConstants.IPPROTO_TCP) { // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established return 432000; } else { // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream return 180; } } }
services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +14 −1 Original line number Diff line number Diff line Loading @@ -21,10 +21,12 @@ import static com.android.internal.util.BitUtils.uint16; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; import android.os.Handler; import android.os.RemoteException; import android.net.util.SharedLog; import android.system.OsConstants; import java.util.ArrayList; Loading Loading @@ -327,13 +329,24 @@ public class OffloadHardwareInterface { public void updateTimeout(NatTimeoutUpdate params) { handler.post(() -> { controlCb.onNatTimeoutUpdate( params.proto, networkProtocolToOsConstant(params.proto), params.src.addr, uint16(params.src.port), params.dst.addr, uint16(params.dst.port)); }); } } private static int networkProtocolToOsConstant(int proto) { switch (proto) { case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; default: // The caller checks this value and will log an error. Just make // sure it won't collide with valid OsContants.IPPROTO_* values. return -Math.abs(proto); } } private static class CbResults { boolean success; String errMsg; Loading
services/net/java/android/net/ip/IpReachabilityMonitor.java +6 −36 Original line number Diff line number Diff line Loading @@ -183,44 +183,14 @@ public class IpReachabilityMonitor { final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); int errno = -OsConstants.EPROTO; try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) { final long IO_TIMEOUT = 300L; nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && (((NetlinkErrorMessage) response).getNlMsgError() != null)) { errno = ((NetlinkErrorMessage) response).getNlMsgError().error; if (errno != 0) { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + response.toString()); } } else { String errmsg; if (response == null) { bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { errmsg = response.toString(); } Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg); } try { NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); } catch (ErrnoException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -e.errno; } catch (InterruptedIOException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -OsConstants.ETIMEDOUT; } catch (SocketException e) { Log.e(TAG, "Error " + msgSnippet, e); errno = -OsConstants.EIO; Log.e(TAG, "Error " + msgSnippet + ": " + e); return -e.errno; } return errno; return 0; } public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) { Loading
services/net/java/android/net/netlink/ConntrackMessage.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.net.netlink.NetlinkConstants.alignedLengthOf; import static android.net.netlink.StructNlAttr.makeNestedType; import static android.net.netlink.StructNlAttr.NLA_HEADERLEN; import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK; import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE; import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; import static android.net.util.NetworkConstants.IPV4_ADDR_LEN; import static java.nio.ByteOrder.BIG_ENDIAN; import android.system.OsConstants; import android.util.Log; import libcore.io.SizeOf; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A NetlinkMessage subclass for netlink conntrack messages. * * see also: <linux_src>/include/uapi/linux/netfilter/nfnetlink_conntrack.h * * @hide */ public class ConntrackMessage extends NetlinkMessage { public static final int STRUCT_SIZE = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; public static final short NFNL_SUBSYS_CTNETLINK = 1; public static final short IPCTNL_MSG_CT_NEW = 0; // enum ctattr_type public static final short CTA_TUPLE_ORIG = 1; public static final short CTA_TUPLE_REPLY = 2; public static final short CTA_TIMEOUT = 7; // enum ctattr_tuple public static final short CTA_TUPLE_IP = 1; public static final short CTA_TUPLE_PROTO = 2; // enum ctattr_ip public static final short CTA_IP_V4_SRC = 1; public static final short CTA_IP_V4_DST = 2; // enum ctattr_l4proto public static final short CTA_PROTO_NUM = 1; public static final short CTA_PROTO_SRC_PORT = 2; public static final short CTA_PROTO_DST_PORT = 3; public static byte[] newIPv4TimeoutUpdateRequest( int proto, Inet4Address src, int sport, Inet4Address dst, int dport, int timeoutSec) { // *** STYLE WARNING *** // // Code below this point uses extra block indentation to highlight the // packing of nested tuple netlink attribute types. final StructNlAttr ctaTupleOrig = new StructNlAttr(CTA_TUPLE_ORIG, new StructNlAttr(CTA_TUPLE_IP, new StructNlAttr(CTA_IP_V4_SRC, src), new StructNlAttr(CTA_IP_V4_DST, dst)), new StructNlAttr(CTA_TUPLE_PROTO, new StructNlAttr(CTA_PROTO_NUM, (byte) proto), new StructNlAttr(CTA_PROTO_SRC_PORT, (short) sport, BIG_ENDIAN), new StructNlAttr(CTA_PROTO_DST_PORT, (short) dport, BIG_ENDIAN))); final StructNlAttr ctaTimeout = new StructNlAttr(CTA_TIMEOUT, timeoutSec, BIG_ENDIAN); final int payloadLength = ctaTupleOrig.getAlignedLength() + ctaTimeout.getAlignedLength(); final byte[] bytes = new byte[STRUCT_SIZE + payloadLength]; final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); byteBuffer.order(ByteOrder.nativeOrder()); final ConntrackMessage ctmsg = new ConntrackMessage(); ctmsg.mHeader.nlmsg_len = bytes.length; ctmsg.mHeader.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW; ctmsg.mHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; ctmsg.mHeader.nlmsg_seq = 1; ctmsg.pack(byteBuffer); ctaTupleOrig.pack(byteBuffer); ctaTimeout.pack(byteBuffer); return bytes; } protected StructNfGenMsg mNfGenMsg; private ConntrackMessage() { super(new StructNlMsgHdr()); mNfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET); } public void pack(ByteBuffer byteBuffer) { mHeader.pack(byteBuffer); mNfGenMsg.pack(byteBuffer); } }
services/net/java/android/net/netlink/NetlinkSocket.java +41 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,47 @@ public class NetlinkSocket implements Closeable { private long mLastRecvTimeoutMs; private long mLastSendTimeoutMs; public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) { final long IO_TIMEOUT = 300L; nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); // recvMessage() guaranteed to not return null if it did not throw. final NetlinkMessage response = NetlinkMessage.parse(bytes); if (response != null && response instanceof NetlinkErrorMessage && (((NetlinkErrorMessage) response).getNlMsgError() != null)) { final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error; if (errno != 0) { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. Log.e(TAG, errPrefix + ", errmsg=" + response.toString()); // Note: convert kernel errnos (negative) into userspace errnos (positive). throw new ErrnoException(response.toString(), Math.abs(errno)); } } else { final String errmsg; if (response == null) { bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { errmsg = response.toString(); } Log.e(TAG, errPrefix + ", errmsg=" + errmsg); throw new ErrnoException(errmsg, OsConstants.EPROTO); } } catch (InterruptedIOException e) { Log.e(TAG, errPrefix, e); throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e); } catch (SocketException e) { Log.e(TAG, errPrefix, e); throw new ErrnoException(errPrefix, OsConstants.EIO, e); } } public NetlinkSocket(int nlProto) throws ErrnoException { mDescriptor = Os.socket( OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); Loading