Record events for RA option lifetimes

This patch defines a new android.net.metrics.RaEvent class carrying
lifetime values contained in RA packets. RaEvent are recorded when
ApfFilter processes a new RA for which there is no match.

Example:
ConnectivityMetricsEvent(15:39:39.808, 0, 0): RaEvent(lifetimes: router=3600s, prefix_valid=2592000s, prefix_preferred=604800s, route_info=-1s, dnssl=-1s, rdnss=3600s)

Change-Id: Ia28652e03ed442d5f2a686ef5b3fafbcb77c503a
This commit is contained in:
Hugo Benichi
2016-06-23 10:41:30 +09:00
parent 647c86d70a
commit cfbf7414a1
3 changed files with 161 additions and 23 deletions

View File

@@ -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);

View File

@@ -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];
}
};
}

View File

@@ -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;
}