Loading core/java/android/net/NetworkUtils.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -51,6 +51,17 @@ public class NetworkUtils { */ */ public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; /** * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity. * * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges. * * @param fd the socket's {@link FileDescriptor}. * @param packetType the hardware address type, one of ARPHRD_*. */ public native static void attachControlPacketFilter(FileDescriptor fd, int packetType) throws SocketException; /** /** * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * @param fd the socket's {@link FileDescriptor}. * @param fd the socket's {@link FileDescriptor}. Loading core/jni/android_net_NetUtils.cpp +92 −15 Original line number Original line Diff line number Diff line Loading @@ -44,28 +44,33 @@ int ifc_disable(const char *ifname); namespace android { namespace android { static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type); static const uint32_t kEtherHeaderLen = sizeof(ether_header); static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol); static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off); static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt); static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr); static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source); static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest); static const uint16_t kDhcpClientPort = 68; static const uint16_t kDhcpClientPort = 68; static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) { { uint32_t ip_offset = sizeof(ether_header); uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol); uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off); uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest); struct sock_filter filter_code[] = { struct sock_filter filter_code[] = { // Check the protocol is UDP. // Check the protocol is UDP. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, proto_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), // Check this is not a fragment. // Check this is not a fragment. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, flags_offset), BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 4, 0), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0), // Get the IP header length. // Get the IP header length. BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, ip_offset), BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), // Check the destination port. // Check the destination port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, dport_indirect_offset), BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1), // Accept or reject. // Accept or reject. Loading Loading @@ -93,17 +98,13 @@ static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject return; return; } } uint32_t ipv6_offset = sizeof(ether_header); uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt); uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr); uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type); struct sock_filter filter_code[] = { struct sock_filter filter_code[] = { // Check IPv6 Next Header is ICMPv6. // Check IPv6 Next Header is ICMPv6. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), // Check ICMPv6 type is Router Advertisement. // Check ICMPv6 type is Router Advertisement. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), // Accept or reject. // Accept or reject. Loading @@ -122,6 +123,81 @@ static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject } } } } // TODO: Move all this filter code into libnetutils. static void android_net_utils_attachControlPacketFilter( JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) { if (hardwareAddressType != ARPHRD_ETHER) { jniThrowExceptionFmt(env, "java/net/SocketException", "attachControlPacketFilter only supports ARPHRD_ETHER"); return; } // Capture all: // - ARPs // - DHCPv4 packets // - Router Advertisements & Solicitations // - Neighbor Advertisements & Solicitations // // tcpdump: // arp or // '(ip and udp port 68)' or // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)' struct sock_filter filter_code[] = { // Load the link layer next payload field. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset), // Accept all ARP. // TODO: Figure out how to better filter ARPs on noisy networks. BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0), // If IPv4: BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9), // Check the protocol is UDP. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14), // Check this is not a fragment. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0), // Get the IP header length. BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), // Check the source port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0), // Check the destination port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7), // IPv6 ... BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6), // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ... BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4), // ... and check the ICMPv6 type is one of RS/RA/NS/NA. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2), BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0), // Accept or reject. BPF_STMT(BPF_RET | BPF_K, 0xffff), BPF_STMT(BPF_RET | BPF_K, 0) }; struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); } } static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) jint ifIndex) { { Loading Loading @@ -263,6 +339,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, }; }; Loading services/net/java/android/net/dhcp/DhcpPacket.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -26,8 +26,10 @@ import java.util.List; * Defines basic data and operations needed to build and use packets for the * Defines basic data and operations needed to build and use packets for the * DHCP protocol. Subclasses create the specific packets used at each * DHCP protocol. Subclasses create the specific packets used at each * stage of the negotiation. * stage of the negotiation. * * @hide */ */ abstract class DhcpPacket { public abstract class DhcpPacket { protected static final String TAG = "DhcpPacket"; protected static final String TAG = "DhcpPacket"; // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the Loading services/net/java/android/net/ip/ConnectivityPacketTracker.java 0 → 100644 +148 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2016 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.system.OsConstants.*; import android.net.NetworkUtils; import android.net.util.BlockingSocketReader; import android.net.util.ConnectivityPacketSummary; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; import android.util.Log; import android.util.LocalLog; import libcore.io.IoBridge; import libcore.util.HexEncoding; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.io.IOException; import java.net.NetworkInterface; import java.net.SocketException; /** * Critical connectivity packet tracking daemon. * * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. * * This class's constructor, start() and stop() methods must only be called * from the same thread on which the passed in |log| is accessed. * * Log lines include a hexdump of the packet, which can be decoded via: * * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /' * | text2pcap - - * | tcpdump -n -vv -e -r - * * @hide */ public class ConnectivityPacketTracker { private static final String TAG = ConnectivityPacketTracker.class.getSimpleName(); private static final boolean DBG = false; private static final String MARK_START = "--- START ---"; private static final String MARK_STOP = "--- STOP ---"; private final String mTag; private final Handler mHandler; private final LocalLog mLog; private final BlockingSocketReader mPacketListener; public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) { final String ifname; final int ifindex; final byte[] hwaddr; final int mtu; try { ifname = netif.getName(); ifindex = netif.getIndex(); hwaddr = netif.getHardwareAddress(); mtu = netif.getMTU(); } catch (NullPointerException|SocketException e) { throw new IllegalArgumentException("bad network interface", e); } mTag = TAG + "." + ifname; mHandler = new Handler(); mLog = log; mPacketListener = new PacketListener(ifindex, hwaddr, mtu); } public void start() { mLog.log(MARK_START); mPacketListener.start(); } public void stop() { mPacketListener.stop(); mLog.log(MARK_STOP); } private final class PacketListener extends BlockingSocketReader { private final int mIfIndex; private final byte mHwAddr[]; PacketListener(int ifindex, byte[] hwaddr, int mtu) { super(mtu); mIfIndex = ifindex; mHwAddr = hwaddr; } @Override protected FileDescriptor createSocket() { FileDescriptor s = null; try { // TODO: Evaluate switching to SOCK_DGRAM and changing the // BlockingSocketReader's read() to recvfrom(), so that this // might work on non-ethernet-like links (via SLL). s = Os.socket(AF_PACKET, SOCK_RAW, 0); NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER); Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex)); } catch (ErrnoException | IOException e) { logError("Failed to create packet tracking socket: ", e); closeSocket(s); return null; } return s; } @Override protected void handlePacket(byte[] recvbuf, int length) { final String summary = ConnectivityPacketSummary.summarize( mHwAddr, recvbuf, length); if (summary == null) return; if (DBG) Log.d(mTag, summary); addLogEntry(summary + "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]"); } @Override protected void logError(String msg, Exception e) { Log.e(mTag, msg, e); addLogEntry(msg + e); } private void addLogEntry(String entry) { mHandler.post(() -> mLog.log(entry)); } } } services/net/java/android/net/ip/IpManager.java +27 −1 Original line number Original line Diff line number Diff line Loading @@ -374,6 +374,7 @@ public class IpManager extends StateMachine { private static final int EVENT_DHCPACTION_TIMEOUT = 10; private static final int EVENT_DHCPACTION_TIMEOUT = 10; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_PACKET_RECORDS = 100; private static final boolean NO_CALLBACKS = false; private static final boolean NO_CALLBACKS = false; private static final boolean SEND_CALLBACKS = true; private static final boolean SEND_CALLBACKS = true; Loading @@ -399,6 +400,7 @@ public class IpManager extends StateMachine { private final WakeupMessage mDhcpActionTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final AvoidBadWifiTracker mAvoidBadWifiTracker; private final AvoidBadWifiTracker mAvoidBadWifiTracker; private final LocalLog mLocalLog; private final LocalLog mLocalLog; private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); Loading Loading @@ -439,6 +441,7 @@ public class IpManager extends StateMachine { mNwService = nwService; mNwService = nwService; mLocalLog = new LocalLog(MAX_LOG_RECORDS); mLocalLog = new LocalLog(MAX_LOG_RECORDS); mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS); mMsgStateLogger = new MessageHandlingLogger(); mMsgStateLogger = new MessageHandlingLogger(); mNetlinkTracker = new NetlinkTracker( mNetlinkTracker = new NetlinkTracker( Loading Loading @@ -609,7 +612,7 @@ public class IpManager extends StateMachine { } } IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println("APF dump:"); pw.println(mTag + " APF dump:"); pw.increaseIndent(); pw.increaseIndent(); // Thread-unsafe access to mApfFilter but just used for debugging. // Thread-unsafe access to mApfFilter but just used for debugging. ApfFilter apfFilter = mApfFilter; ApfFilter apfFilter = mApfFilter; Loading @@ -625,6 +628,12 @@ public class IpManager extends StateMachine { pw.increaseIndent(); pw.increaseIndent(); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); pw.decreaseIndent(); pw.println(); pw.println(mTag + " connectivity packet log:"); pw.increaseIndent(); mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); } } Loading Loading @@ -1220,6 +1229,7 @@ public class IpManager extends StateMachine { } } class RunningState extends State { class RunningState extends State { private ConnectivityPacketTracker mPacketTracker; private boolean mDhcpActionInFlight; private boolean mDhcpActionInFlight; @Override @Override Loading @@ -1232,6 +1242,9 @@ public class IpManager extends StateMachine { mCallback.setFallbackMulticastFilter(mMulticastFiltering); mCallback.setFallbackMulticastFilter(mMulticastFiltering); } } mPacketTracker = createPacketTracker(); if (mPacketTracker != null) mPacketTracker.start(); if (mConfiguration.mEnableIPv6 && !startIPv6()) { if (mConfiguration.mEnableIPv6 && !startIPv6()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); transitionTo(mStoppingState); transitionTo(mStoppingState); Loading Loading @@ -1266,6 +1279,11 @@ public class IpManager extends StateMachine { mDhcpClient.doQuit(); mDhcpClient.doQuit(); } } if (mPacketTracker != null) { mPacketTracker.stop(); mPacketTracker = null; } if (mApfFilter != null) { if (mApfFilter != null) { mApfFilter.shutdown(); mApfFilter.shutdown(); mApfFilter = null; mApfFilter = null; Loading @@ -1274,6 +1292,14 @@ public class IpManager extends StateMachine { resetLinkProperties(); resetLinkProperties(); } } private ConnectivityPacketTracker createPacketTracker() { try { return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog); } catch (IllegalArgumentException e) { return null; } } private void ensureDhcpAction() { private void ensureDhcpAction() { if (!mDhcpActionInFlight) { if (!mDhcpActionInFlight) { mCallback.onPreDhcpAction(); mCallback.onPreDhcpAction(); Loading Loading
core/java/android/net/NetworkUtils.java +11 −0 Original line number Original line Diff line number Diff line Loading @@ -51,6 +51,17 @@ public class NetworkUtils { */ */ public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException; /** * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity. * * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges. * * @param fd the socket's {@link FileDescriptor}. * @param packetType the hardware address type, one of ARPHRD_*. */ public native static void attachControlPacketFilter(FileDescriptor fd, int packetType) throws SocketException; /** /** * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * @param fd the socket's {@link FileDescriptor}. * @param fd the socket's {@link FileDescriptor}. Loading
core/jni/android_net_NetUtils.cpp +92 −15 Original line number Original line Diff line number Diff line Loading @@ -44,28 +44,33 @@ int ifc_disable(const char *ifname); namespace android { namespace android { static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type); static const uint32_t kEtherHeaderLen = sizeof(ether_header); static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol); static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off); static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt); static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr); static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source); static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest); static const uint16_t kDhcpClientPort = 68; static const uint16_t kDhcpClientPort = 68; static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) { { uint32_t ip_offset = sizeof(ether_header); uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol); uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off); uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest); struct sock_filter filter_code[] = { struct sock_filter filter_code[] = { // Check the protocol is UDP. // Check the protocol is UDP. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, proto_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), // Check this is not a fragment. // Check this is not a fragment. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, flags_offset), BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 4, 0), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0), // Get the IP header length. // Get the IP header length. BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, ip_offset), BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), // Check the destination port. // Check the destination port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, dport_indirect_offset), BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1), // Accept or reject. // Accept or reject. Loading Loading @@ -93,17 +98,13 @@ static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject return; return; } } uint32_t ipv6_offset = sizeof(ether_header); uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt); uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr); uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type); struct sock_filter filter_code[] = { struct sock_filter filter_code[] = { // Check IPv6 Next Header is ICMPv6. // Check IPv6 Next Header is ICMPv6. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), // Check ICMPv6 type is Router Advertisement. // Check ICMPv6 type is Router Advertisement. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset), BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), // Accept or reject. // Accept or reject. Loading @@ -122,6 +123,81 @@ static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject } } } } // TODO: Move all this filter code into libnetutils. static void android_net_utils_attachControlPacketFilter( JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) { if (hardwareAddressType != ARPHRD_ETHER) { jniThrowExceptionFmt(env, "java/net/SocketException", "attachControlPacketFilter only supports ARPHRD_ETHER"); return; } // Capture all: // - ARPs // - DHCPv4 packets // - Router Advertisements & Solicitations // - Neighbor Advertisements & Solicitations // // tcpdump: // arp or // '(ip and udp port 68)' or // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)' struct sock_filter filter_code[] = { // Load the link layer next payload field. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset), // Accept all ARP. // TODO: Figure out how to better filter ARPs on noisy networks. BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0), // If IPv4: BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9), // Check the protocol is UDP. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14), // Check this is not a fragment. BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0), // Get the IP header length. BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), // Check the source port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0), // Check the destination port. BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7), // IPv6 ... BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6), // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ... BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4), // ... and check the ICMPv6 type is one of RS/RA/NS/NA. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2), BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0), // Accept or reject. BPF_STMT(BPF_RET | BPF_K, 0xffff), BPF_STMT(BPF_RET | BPF_K, 0) }; struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); } } static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) jint ifIndex) { { Loading Loading @@ -263,6 +339,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter }, { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, }; }; Loading
services/net/java/android/net/dhcp/DhcpPacket.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -26,8 +26,10 @@ import java.util.List; * Defines basic data and operations needed to build and use packets for the * Defines basic data and operations needed to build and use packets for the * DHCP protocol. Subclasses create the specific packets used at each * DHCP protocol. Subclasses create the specific packets used at each * stage of the negotiation. * stage of the negotiation. * * @hide */ */ abstract class DhcpPacket { public abstract class DhcpPacket { protected static final String TAG = "DhcpPacket"; protected static final String TAG = "DhcpPacket"; // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the Loading
services/net/java/android/net/ip/ConnectivityPacketTracker.java 0 → 100644 +148 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2016 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.system.OsConstants.*; import android.net.NetworkUtils; import android.net.util.BlockingSocketReader; import android.net.util.ConnectivityPacketSummary; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; import android.util.Log; import android.util.LocalLog; import libcore.io.IoBridge; import libcore.util.HexEncoding; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.io.IOException; import java.net.NetworkInterface; import java.net.SocketException; /** * Critical connectivity packet tracking daemon. * * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. * * This class's constructor, start() and stop() methods must only be called * from the same thread on which the passed in |log| is accessed. * * Log lines include a hexdump of the packet, which can be decoded via: * * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /' * | text2pcap - - * | tcpdump -n -vv -e -r - * * @hide */ public class ConnectivityPacketTracker { private static final String TAG = ConnectivityPacketTracker.class.getSimpleName(); private static final boolean DBG = false; private static final String MARK_START = "--- START ---"; private static final String MARK_STOP = "--- STOP ---"; private final String mTag; private final Handler mHandler; private final LocalLog mLog; private final BlockingSocketReader mPacketListener; public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) { final String ifname; final int ifindex; final byte[] hwaddr; final int mtu; try { ifname = netif.getName(); ifindex = netif.getIndex(); hwaddr = netif.getHardwareAddress(); mtu = netif.getMTU(); } catch (NullPointerException|SocketException e) { throw new IllegalArgumentException("bad network interface", e); } mTag = TAG + "." + ifname; mHandler = new Handler(); mLog = log; mPacketListener = new PacketListener(ifindex, hwaddr, mtu); } public void start() { mLog.log(MARK_START); mPacketListener.start(); } public void stop() { mPacketListener.stop(); mLog.log(MARK_STOP); } private final class PacketListener extends BlockingSocketReader { private final int mIfIndex; private final byte mHwAddr[]; PacketListener(int ifindex, byte[] hwaddr, int mtu) { super(mtu); mIfIndex = ifindex; mHwAddr = hwaddr; } @Override protected FileDescriptor createSocket() { FileDescriptor s = null; try { // TODO: Evaluate switching to SOCK_DGRAM and changing the // BlockingSocketReader's read() to recvfrom(), so that this // might work on non-ethernet-like links (via SLL). s = Os.socket(AF_PACKET, SOCK_RAW, 0); NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER); Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex)); } catch (ErrnoException | IOException e) { logError("Failed to create packet tracking socket: ", e); closeSocket(s); return null; } return s; } @Override protected void handlePacket(byte[] recvbuf, int length) { final String summary = ConnectivityPacketSummary.summarize( mHwAddr, recvbuf, length); if (summary == null) return; if (DBG) Log.d(mTag, summary); addLogEntry(summary + "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]"); } @Override protected void logError(String msg, Exception e) { Log.e(mTag, msg, e); addLogEntry(msg + e); } private void addLogEntry(String entry) { mHandler.post(() -> mLog.log(entry)); } } }
services/net/java/android/net/ip/IpManager.java +27 −1 Original line number Original line Diff line number Diff line Loading @@ -374,6 +374,7 @@ public class IpManager extends StateMachine { private static final int EVENT_DHCPACTION_TIMEOUT = 10; private static final int EVENT_DHCPACTION_TIMEOUT = 10; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_PACKET_RECORDS = 100; private static final boolean NO_CALLBACKS = false; private static final boolean NO_CALLBACKS = false; private static final boolean SEND_CALLBACKS = true; private static final boolean SEND_CALLBACKS = true; Loading @@ -399,6 +400,7 @@ public class IpManager extends StateMachine { private final WakeupMessage mDhcpActionTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final AvoidBadWifiTracker mAvoidBadWifiTracker; private final AvoidBadWifiTracker mAvoidBadWifiTracker; private final LocalLog mLocalLog; private final LocalLog mLocalLog; private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); Loading Loading @@ -439,6 +441,7 @@ public class IpManager extends StateMachine { mNwService = nwService; mNwService = nwService; mLocalLog = new LocalLog(MAX_LOG_RECORDS); mLocalLog = new LocalLog(MAX_LOG_RECORDS); mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS); mMsgStateLogger = new MessageHandlingLogger(); mMsgStateLogger = new MessageHandlingLogger(); mNetlinkTracker = new NetlinkTracker( mNetlinkTracker = new NetlinkTracker( Loading Loading @@ -609,7 +612,7 @@ public class IpManager extends StateMachine { } } IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println("APF dump:"); pw.println(mTag + " APF dump:"); pw.increaseIndent(); pw.increaseIndent(); // Thread-unsafe access to mApfFilter but just used for debugging. // Thread-unsafe access to mApfFilter but just used for debugging. ApfFilter apfFilter = mApfFilter; ApfFilter apfFilter = mApfFilter; Loading @@ -625,6 +628,12 @@ public class IpManager extends StateMachine { pw.increaseIndent(); pw.increaseIndent(); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); mLocalLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); pw.decreaseIndent(); pw.println(); pw.println(mTag + " connectivity packet log:"); pw.increaseIndent(); mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args); pw.decreaseIndent(); } } Loading Loading @@ -1220,6 +1229,7 @@ public class IpManager extends StateMachine { } } class RunningState extends State { class RunningState extends State { private ConnectivityPacketTracker mPacketTracker; private boolean mDhcpActionInFlight; private boolean mDhcpActionInFlight; @Override @Override Loading @@ -1232,6 +1242,9 @@ public class IpManager extends StateMachine { mCallback.setFallbackMulticastFilter(mMulticastFiltering); mCallback.setFallbackMulticastFilter(mMulticastFiltering); } } mPacketTracker = createPacketTracker(); if (mPacketTracker != null) mPacketTracker.start(); if (mConfiguration.mEnableIPv6 && !startIPv6()) { if (mConfiguration.mEnableIPv6 && !startIPv6()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); transitionTo(mStoppingState); transitionTo(mStoppingState); Loading Loading @@ -1266,6 +1279,11 @@ public class IpManager extends StateMachine { mDhcpClient.doQuit(); mDhcpClient.doQuit(); } } if (mPacketTracker != null) { mPacketTracker.stop(); mPacketTracker = null; } if (mApfFilter != null) { if (mApfFilter != null) { mApfFilter.shutdown(); mApfFilter.shutdown(); mApfFilter = null; mApfFilter = null; Loading @@ -1274,6 +1292,14 @@ public class IpManager extends StateMachine { resetLinkProperties(); resetLinkProperties(); } } private ConnectivityPacketTracker createPacketTracker() { try { return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog); } catch (IllegalArgumentException e) { return null; } } private void ensureDhcpAction() { private void ensureDhcpAction() { if (!mDhcpActionInFlight) { if (!mDhcpActionInFlight) { mCallback.onPreDhcpAction(); mCallback.onPreDhcpAction(); Loading