Merge "IpConn metrics: correctly read RA lifetimes" into nyc-mr1-dev
This commit is contained in:
@@ -27,6 +27,9 @@ import android.os.Parcelable;
|
||||
@SystemApi
|
||||
public final class RaEvent implements Parcelable {
|
||||
|
||||
/** {@hide} */
|
||||
public static final long NO_LIFETIME = -1L;
|
||||
|
||||
// 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;
|
||||
@@ -92,4 +95,60 @@ public final class RaEvent implements Parcelable {
|
||||
return new RaEvent[size];
|
||||
}
|
||||
};
|
||||
|
||||
/** {@hide} */
|
||||
public static class Builder {
|
||||
|
||||
long routerLifetime = NO_LIFETIME;
|
||||
long prefixValidLifetime = NO_LIFETIME;
|
||||
long prefixPreferredLifetime = NO_LIFETIME;
|
||||
long routeInfoLifetime = NO_LIFETIME;
|
||||
long rdnssLifetime = NO_LIFETIME;
|
||||
long dnsslLifetime = NO_LIFETIME;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public RaEvent build() {
|
||||
return new RaEvent(routerLifetime, prefixValidLifetime, prefixPreferredLifetime,
|
||||
routeInfoLifetime, rdnssLifetime, dnsslLifetime);
|
||||
}
|
||||
|
||||
public Builder updateRouterLifetime(long lifetime) {
|
||||
routerLifetime = updateLifetime(routerLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updatePrefixValidLifetime(long lifetime) {
|
||||
prefixValidLifetime = updateLifetime(prefixValidLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updatePrefixPreferredLifetime(long lifetime) {
|
||||
prefixPreferredLifetime = updateLifetime(prefixPreferredLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updateRouteInfoLifetime(long lifetime) {
|
||||
routeInfoLifetime = updateLifetime(routeInfoLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updateRdnssLifetime(long lifetime) {
|
||||
rdnssLifetime = updateLifetime(rdnssLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder updateDnsslLifetime(long lifetime) {
|
||||
dnsslLifetime = updateLifetime(dnsslLifetime, lifetime);
|
||||
return this;
|
||||
}
|
||||
|
||||
private long updateLifetime(long currentLifetime, long newLifetime) {
|
||||
if (currentLifetime == RaEvent.NO_LIFETIME) {
|
||||
return newLifetime;
|
||||
}
|
||||
return Math.min(currentLifetime, newLifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class ApfFilter {
|
||||
private final ApfCapabilities mApfCapabilities;
|
||||
private final IpManager.Callback mIpManagerCallback;
|
||||
private final NetworkInterface mNetworkInterface;
|
||||
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
|
||||
private final IpConnectivityLog mMetricsLog;
|
||||
@VisibleForTesting
|
||||
byte[] mHardwareAddress;
|
||||
@VisibleForTesting
|
||||
@@ -228,11 +228,12 @@ public class ApfFilter {
|
||||
|
||||
@VisibleForTesting
|
||||
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
|
||||
IpManager.Callback ipManagerCallback, boolean multicastFilter) {
|
||||
IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) {
|
||||
mApfCapabilities = apfCapabilities;
|
||||
mIpManagerCallback = ipManagerCallback;
|
||||
mNetworkInterface = networkInterface;
|
||||
mMulticastFilter = multicastFilter;
|
||||
mMetricsLog = log;
|
||||
|
||||
maybeStartFilter();
|
||||
}
|
||||
@@ -371,6 +372,14 @@ public class ApfFilter {
|
||||
return i & 0xffffffffL;
|
||||
}
|
||||
|
||||
private long getUint16(ByteBuffer buffer, int position) {
|
||||
return uint16(buffer.getShort(position));
|
||||
}
|
||||
|
||||
private long getUint32(ByteBuffer buffer, int position) {
|
||||
return uint32(buffer.getInt(position));
|
||||
}
|
||||
|
||||
private void prefixOptionToString(StringBuffer sb, int offset) {
|
||||
String prefix = IPv6AddresstoString(offset + 16);
|
||||
int length = uint8(mPacket.get(offset + 2));
|
||||
@@ -417,7 +426,7 @@ public class ApfFilter {
|
||||
* @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
|
||||
* @param lifetimeLength length of the next lifetime data.
|
||||
* @return offset within packet of where the next binary range of data not including
|
||||
* a lifetime. This can be passed into the next invocation of this function
|
||||
* a lifetime. This can be passed into the next invocation of this function
|
||||
* via {@code lastNonLifetimeStart}.
|
||||
*/
|
||||
private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
|
||||
@@ -438,9 +447,9 @@ public class ApfFilter {
|
||||
// (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
|
||||
// specifications.
|
||||
Ra(byte[] packet, int length) {
|
||||
mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
|
||||
mPacket.clear();
|
||||
mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
|
||||
mLastSeen = curTime();
|
||||
RaEvent.Builder builder = new RaEvent.Builder();
|
||||
|
||||
// Ignore the checksum.
|
||||
int lastNonLifetimeStart = addNonLifetime(0,
|
||||
@@ -451,14 +460,7 @@ public class ApfFilter {
|
||||
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
|
||||
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;
|
||||
builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
|
||||
|
||||
// Ensures that the RA is not truncated.
|
||||
mPacket.position(ICMP6_RA_OPTION_OFFSET);
|
||||
@@ -466,39 +468,42 @@ public class ApfFilter {
|
||||
final int position = mPacket.position();
|
||||
final int optionType = uint8(mPacket.get(position));
|
||||
final int optionLength = uint8(mPacket.get(position + 1)) * 8;
|
||||
long lifetime;
|
||||
switch (optionType) {
|
||||
case ICMP6_PREFIX_OPTION_TYPE:
|
||||
// Parse valid lifetime
|
||||
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
|
||||
ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
|
||||
ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
|
||||
lifetime = getUint32(mPacket,
|
||||
position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
|
||||
builder.updatePrefixValidLifetime(lifetime);
|
||||
// Parse preferred lifetime
|
||||
lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
|
||||
ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
|
||||
ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
|
||||
lifetime = getUint32(mPacket,
|
||||
position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
|
||||
builder.updatePrefixPreferredLifetime(lifetime);
|
||||
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, and
|
||||
// are processed with the same specialized addNonLifetime4B:
|
||||
// are processed with the same specialized addNonLifetimeU32:
|
||||
case ICMP6_RDNSS_OPTION_TYPE:
|
||||
mRdnssOptionOffsets.add(position);
|
||||
lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
|
||||
rdnssLifetime =
|
||||
uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
|
||||
lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
|
||||
builder.updateRdnssLifetime(lifetime);
|
||||
break;
|
||||
case ICMP6_ROUTE_INFO_OPTION_TYPE:
|
||||
lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
|
||||
routeInfoLifetime =
|
||||
uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
|
||||
lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
|
||||
builder.updateRouteInfoLifetime(lifetime);
|
||||
break;
|
||||
case ICMP6_DNSSL_OPTION_TYPE:
|
||||
lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
|
||||
dnsslLifetime =
|
||||
uint32(mPacket.getInt(ICMP6_4_BYTE_LIFETIME_OFFSET + position));
|
||||
lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
|
||||
builder.updateDnsslLifetime(lifetime);
|
||||
break;
|
||||
default:
|
||||
// RFC4861 section 4.2 dictates we ignore unknown options for fowards
|
||||
@@ -514,9 +519,7 @@ public class ApfFilter {
|
||||
// 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));
|
||||
mMetricsLog.log(builder.build());
|
||||
}
|
||||
|
||||
// Ignoring lifetimes (which may change) does {@code packet} match this RA?
|
||||
@@ -1000,7 +1003,8 @@ public class ApfFilter {
|
||||
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
|
||||
return null;
|
||||
}
|
||||
return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, multicastFilter);
|
||||
return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
|
||||
multicastFilter, new IpConnectivityLog());
|
||||
}
|
||||
|
||||
public synchronized void shutdown() {
|
||||
|
||||
@@ -26,14 +26,23 @@ import android.net.apf.ApfGenerator;
|
||||
import android.net.apf.ApfGenerator.IllegalInstructionException;
|
||||
import android.net.apf.ApfGenerator.Register;
|
||||
import android.net.ip.IpManager;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.metrics.RaEvent;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Parcelable;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -43,6 +52,7 @@ import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.io.Streams;
|
||||
@@ -56,9 +66,12 @@ import libcore.io.Streams;
|
||||
public class ApfTest extends AndroidTestCase {
|
||||
private static final int TIMEOUT_MS = 500;
|
||||
|
||||
@Mock IpConnectivityLog mLog;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
// Load up native shared library containing APF interpreter exposed via JNI.
|
||||
System.loadLibrary("servicestestsjni");
|
||||
}
|
||||
@@ -70,6 +83,9 @@ public class ApfTest extends AndroidTestCase {
|
||||
// least the minimum packet size.
|
||||
private final static int MIN_PKT_SIZE = 15;
|
||||
|
||||
private final static boolean DROP_MULTICAST = true;
|
||||
private final static boolean ALLOW_MULTICAST = false;
|
||||
|
||||
private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
|
||||
assertEquals(expected, apfSimulate(program, packet, filterAge));
|
||||
}
|
||||
@@ -562,10 +578,10 @@ public class ApfTest extends AndroidTestCase {
|
||||
public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
|
||||
private FileDescriptor mWriteSocket;
|
||||
|
||||
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter) throws
|
||||
Exception {
|
||||
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
|
||||
IpConnectivityLog log) throws Exception {
|
||||
super(new ApfCapabilities(2, 1000, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
|
||||
ipManagerCallback, multicastFilter);
|
||||
ipManagerCallback, multicastFilter, log);
|
||||
}
|
||||
|
||||
// Pretend an RA packet has been received and show it to ApfFilter.
|
||||
@@ -667,7 +683,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterIPv4() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Verify empty packet of 100 zero bytes is passed
|
||||
@@ -699,7 +715,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterIPv6() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Verify empty IPv6 packet is passed
|
||||
@@ -726,7 +742,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterMulticast() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Construct IPv4 and IPv6 multicast packets.
|
||||
@@ -772,7 +788,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
// Verify it can be initialized to on
|
||||
ipManagerCallback.resetApfProgramWait();
|
||||
apfFilter.shutdown();
|
||||
apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
|
||||
apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
||||
program = ipManagerCallback.getApfProgram();
|
||||
assertDrop(program, bcastv4packet.array(), 0);
|
||||
assertDrop(program, mcastv4packet.array(), 0);
|
||||
@@ -804,7 +820,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterArp() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Verify initially ARP filter is off
|
||||
@@ -867,6 +883,35 @@ public class ApfTest extends AndroidTestCase {
|
||||
verifyRaLifetime(ipManagerCallback, packet, lifetime);
|
||||
}
|
||||
|
||||
private void verifyRaEvent(RaEvent expected) {
|
||||
ArgumentCaptor<Parcelable> captor = ArgumentCaptor.forClass(Parcelable.class);
|
||||
verify(mLog, atLeastOnce()).log(captor.capture());
|
||||
RaEvent got = lastRaEvent(captor.getAllValues());
|
||||
if (!raEventEquals(expected, got)) {
|
||||
assertEquals(expected, got); // fail for printing an assertion error message.
|
||||
}
|
||||
}
|
||||
|
||||
private RaEvent lastRaEvent(List<Parcelable> events) {
|
||||
RaEvent got = null;
|
||||
for (Parcelable ev : events) {
|
||||
if (ev instanceof RaEvent) {
|
||||
got = (RaEvent) ev;
|
||||
}
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
private boolean raEventEquals(RaEvent ev1, RaEvent ev2) {
|
||||
return (ev1 != null) && (ev2 != null)
|
||||
&& (ev1.routerLifetime == ev2.routerLifetime)
|
||||
&& (ev1.prefixValidLifetime == ev2.prefixValidLifetime)
|
||||
&& (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime)
|
||||
&& (ev1.routeInfoLifetime == ev2.routeInfoLifetime)
|
||||
&& (ev1.rdnssLifetime == ev2.rdnssLifetime)
|
||||
&& (ev1.dnsslLifetime == ev2.dnsslLifetime);
|
||||
}
|
||||
|
||||
private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
|
||||
ByteBuffer packet) throws IOException, ErrnoException {
|
||||
ipManagerCallback.resetApfProgramWait();
|
||||
@@ -877,7 +922,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterRa() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
|
||||
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Verify RA is passed the first time
|
||||
@@ -891,6 +936,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
assertPass(program, basePacket.array(), 0);
|
||||
|
||||
testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
|
||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));
|
||||
|
||||
// Ensure zero-length options cause the packet to be silently skipped.
|
||||
// Do this before we test other packets. http://b/29586253
|
||||
@@ -916,6 +962,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
prefixOptionPacket.putInt(
|
||||
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200);
|
||||
testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100);
|
||||
verifyRaEvent(new RaEvent(1000, 200, 100, -1, -1, -1));
|
||||
|
||||
ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
|
||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||
@@ -926,6 +973,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
rdnssOptionPacket.putInt(
|
||||
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300);
|
||||
testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300);
|
||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, 300, -1));
|
||||
|
||||
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
|
||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||
@@ -936,6 +984,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
routeInfoOptionPacket.putInt(
|
||||
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400);
|
||||
testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400);
|
||||
verifyRaEvent(new RaEvent(1000, -1, -1, 400, -1, -1));
|
||||
|
||||
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
|
||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||
@@ -948,6 +997,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
// Note that lifetime of 2000 will be ignored in favor of shorter
|
||||
// route lifetime of 1000.
|
||||
testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000);
|
||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, 2000));
|
||||
|
||||
// Verify that current program filters all five RAs:
|
||||
verifyRaLifetime(ipManagerCallback, basePacket, 1000);
|
||||
|
||||
Reference in New Issue
Block a user