diff options
| -rw-r--r-- | api/system-current.txt | 12 | ||||
| -rw-r--r-- | core/java/android/net/metrics/RaEvent.java | 95 | ||||
| -rw-r--r-- | services/net/java/android/net/apf/ApfFilter.java | 77 |
3 files changed, 161 insertions, 23 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 41ca0e684b29..769f8678f608 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -26153,6 +26153,18 @@ package android.net.metrics { field public final int netId; } + public final class RaEvent implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.metrics.RaEvent> CREATOR; + field public final long dnsslLifetime; + field public final long prefixPreferredLifetime; + field public final long prefixValidLifetime; + field public final long rdnssLifetime; + field public final long routeInfoLifetime; + field public final long routerLifetime; + } + public final class ValidationProbeEvent implements android.os.Parcelable { method public int describeContents(); method public static void logEvent(int, long, int, int); diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java new file mode 100644 index 000000000000..69013c0de637 --- /dev/null +++ b/core/java/android/net/metrics/RaEvent.java @@ -0,0 +1,95 @@ +/* + * 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.metrics; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An event logged when the APF packet socket receives an RA packet. + * {@hide} + */ +@SystemApi +public final class RaEvent implements Parcelable { + + // Lifetime in seconds of options found in a single RA packet. + // When an option is not set, the value of the associated field is -1; + public final long routerLifetime; + public final long prefixValidLifetime; + public final long prefixPreferredLifetime; + public final long routeInfoLifetime; + public final long rdnssLifetime; + public final long dnsslLifetime; + + /** {@hide} */ + public RaEvent(long routerLifetime, long prefixValidLifetime, long prefixPreferredLifetime, + long routeInfoLifetime, long rdnssLifetime, long dnsslLifetime) { + this.routerLifetime = routerLifetime; + this.prefixValidLifetime = prefixValidLifetime; + this.prefixPreferredLifetime = prefixPreferredLifetime; + this.routeInfoLifetime = routeInfoLifetime; + this.rdnssLifetime = rdnssLifetime; + this.dnsslLifetime = dnsslLifetime; + } + + private RaEvent(Parcel in) { + routerLifetime = in.readLong(); + prefixValidLifetime = in.readLong(); + prefixPreferredLifetime = in.readLong(); + routeInfoLifetime = in.readLong(); + rdnssLifetime = in.readLong(); + dnsslLifetime = in.readLong(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(routerLifetime); + out.writeLong(prefixValidLifetime); + out.writeLong(prefixPreferredLifetime); + out.writeLong(routeInfoLifetime); + out.writeLong(rdnssLifetime); + out.writeLong(dnsslLifetime); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return new StringBuilder("RaEvent(lifetimes: ") + .append(String.format("router=%ds, ", routerLifetime)) + .append(String.format("prefix_valid=%ds, ", prefixValidLifetime)) + .append(String.format("prefix_preferred=%ds, ", prefixPreferredLifetime)) + .append(String.format("route_info=%ds, ", routeInfoLifetime)) + .append(String.format("rdnss=%ds, ", rdnssLifetime)) + .append(String.format("dnssl=%ds)", dnsslLifetime)) + .toString(); + } + + public static final Parcelable.Creator<RaEvent> CREATOR = new Parcelable.Creator<RaEvent>() { + public RaEvent createFromParcel(Parcel in) { + return new RaEvent(in); + } + + public RaEvent[] newArray(int size) { + return new RaEvent[size]; + } + }; +} diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 6d1a4eb40021..0ef9d7abaa38 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -28,6 +28,7 @@ import android.net.ip.IpManager; import android.net.metrics.ApfProgramEvent; import android.net.metrics.ApfStats; import android.net.metrics.IpConnectivityLog; +import android.net.metrics.RaEvent; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; @@ -357,7 +358,7 @@ public class ApfFilter { } // Can't be static because it's in a non-static inner class. - // TODO: Make this final once RA is its own class. + // TODO: Make this static once RA is its own class. private int uint8(byte b) { return b & 0xff; } @@ -366,8 +367,8 @@ public class ApfFilter { return s & 0xffff; } - private long uint32(int s) { - return s & 0xffffffff; + private long uint32(int i) { + return i & 0xffffffffL; } private void prefixOptionToString(StringBuffer sb, int offset) { @@ -427,6 +428,11 @@ public class ApfFilter { return lifetimeOffset + lifetimeLength; } + private int addNonLifetimeU32(int lastNonLifetimeStart) { + return addNonLifetime(lastNonLifetimeStart, + ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN); + } + // Note that this parses RA and may throw IllegalArgumentException (from // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with @@ -446,11 +452,20 @@ public class ApfFilter { ICMP6_RA_ROUTER_LIFETIME_OFFSET, ICMP6_RA_ROUTER_LIFETIME_LEN); + long routerLifetime = uint16(mPacket.getShort( + ICMP6_RA_ROUTER_LIFETIME_OFFSET + mPacket.position())); + long prefixValidLifetime = -1L; + long prefixPreferredLifetime = -1L; + long routeInfoLifetime = -1L; + long dnsslLifetime = - 1L; + long rdnssLifetime = -1L; + // Ensures that the RA is not truncated. mPacket.position(ICMP6_RA_OPTION_OFFSET); while (mPacket.hasRemaining()) { - int optionType = ((int)mPacket.get(mPacket.position())) & 0xff; - int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8; + final int position = mPacket.position(); + final int optionType = uint8(mPacket.get(position)); + final int optionLength = uint8(mPacket.get(position + 1)) * 8; switch (optionType) { case ICMP6_PREFIX_OPTION_TYPE: // Parse valid lifetime @@ -461,19 +476,29 @@ public class ApfFilter { lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN); - mPrefixOptionOffsets.add(mPacket.position()); + mPrefixOptionOffsets.add(position); + prefixValidLifetime = uint32(mPacket.getInt( + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET + position)); + prefixPreferredLifetime = uint32(mPacket.getInt( + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET + position)); break; - // These three options have the same lifetime offset and size, so process - // together: + // These three options have the same lifetime offset and size, and + // are processed with the same specialized addNonLifetime4B: case ICMP6_RDNSS_OPTION_TYPE: - mRdnssOptionOffsets.add(mPacket.position()); - // Fall through. + mRdnssOptionOffsets.add(position); + lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); + rdnssLifetime = + uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position)); + break; case ICMP6_ROUTE_INFO_OPTION_TYPE: + lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); + routeInfoLifetime = + uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position)); + break; case ICMP6_DNSSL_OPTION_TYPE: - // Parse lifetime - lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, - ICMP6_4_BYTE_LIFETIME_OFFSET, - ICMP6_4_BYTE_LIFETIME_LEN); + lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); + dnsslLifetime = + uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position)); break; default: // RFC4861 section 4.2 dictates we ignore unknown options for fowards @@ -484,11 +509,14 @@ public class ApfFilter { throw new IllegalArgumentException(String.format( "Invalid option length opt=%d len=%d", optionType, optionLength)); } - mPacket.position(mPacket.position() + optionLength); + mPacket.position(position + optionLength); } // Mark non-lifetime bytes since last lifetime. addNonLifetime(lastNonLifetimeStart, 0, 0); mMinLifetime = minLifetime(packet, length); + // TODO: record per-option minimum lifetimes instead of last seen lifetimes + mMetricsLog.log(new RaEvent(routerLifetime, prefixValidLifetime, + prefixPreferredLifetime, routeInfoLifetime, rdnssLifetime, dnsslLifetime)); } // Ignoring lifetimes (which may change) does {@code packet} match this RA? @@ -517,16 +545,19 @@ public class ApfFilter { continue; } - int lifetimeLength = mNonLifetimes.get(i+1).first - offset; - long val; + final int lifetimeLength = mNonLifetimes.get(i+1).first - offset; + final long optionLifetime; switch (lifetimeLength) { - case 2: val = byteBuffer.getShort(offset); break; - case 4: val = byteBuffer.getInt(offset); break; - default: throw new IllegalStateException("bogus lifetime size " + length); + case 2: + optionLifetime = uint16(byteBuffer.getShort(offset)); + break; + case 4: + optionLifetime = uint32(byteBuffer.getInt(offset)); + break; + default: + throw new IllegalStateException("bogus lifetime size " + lifetimeLength); } - // Mask to size, converting signed to unsigned - val &= (1L << (lifetimeLength * 8)) - 1; - minLifetime = Math.min(minLifetime, val); + minLifetime = Math.min(minLifetime, optionLifetime); } return minLifetime; } |