Merge "ApfTest: fix flaky testApfFilterRa."
This commit is contained in:
@@ -286,7 +286,8 @@ public class ApfFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns seconds since device boot.
|
// Returns seconds since device boot.
|
||||||
private static long curTime() {
|
@VisibleForTesting
|
||||||
|
protected long currentTimeSeconds() {
|
||||||
return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
|
return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +451,7 @@ public class ApfFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
|
mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
|
||||||
mLastSeen = curTime();
|
mLastSeen = currentTimeSeconds();
|
||||||
|
|
||||||
// Sanity check packet in case a packet arrives before we attach RA filter
|
// Sanity check packet in case a packet arrives before we attach RA filter
|
||||||
// to our packet socket. b/29586253
|
// to our packet socket. b/29586253
|
||||||
@@ -580,7 +581,7 @@ public class ApfFilter {
|
|||||||
// How many seconds does this RA's have to live, taking into account the fact
|
// How many seconds does this RA's have to live, taking into account the fact
|
||||||
// that we might have seen it a while ago.
|
// that we might have seen it a while ago.
|
||||||
long currentLifetime() {
|
long currentLifetime() {
|
||||||
return mMinLifetime - (curTime() - mLastSeen);
|
return mMinLifetime - (currentTimeSeconds() - mLastSeen);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isExpired() {
|
boolean isExpired() {
|
||||||
@@ -946,7 +947,7 @@ public class ApfFilter {
|
|||||||
Log.e(TAG, "Failed to generate APF program.", e);
|
Log.e(TAG, "Failed to generate APF program.", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mLastTimeInstalledProgram = curTime();
|
mLastTimeInstalledProgram = currentTimeSeconds();
|
||||||
mLastInstalledProgramMinLifetime = programMinLifetime;
|
mLastInstalledProgramMinLifetime = programMinLifetime;
|
||||||
mLastInstalledProgram = program;
|
mLastInstalledProgram = program;
|
||||||
mNumProgramUpdates++;
|
mNumProgramUpdates++;
|
||||||
@@ -965,7 +966,7 @@ public class ApfFilter {
|
|||||||
*/
|
*/
|
||||||
private boolean shouldInstallnewProgram() {
|
private boolean shouldInstallnewProgram() {
|
||||||
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
|
long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
|
||||||
return expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
|
return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hexDump(String msg, byte[] packet, int length) {
|
private void hexDump(String msg, byte[] packet, int length) {
|
||||||
@@ -999,7 +1000,7 @@ public class ApfFilter {
|
|||||||
if (ra.matches(packet, length)) {
|
if (ra.matches(packet, length)) {
|
||||||
if (VDBG) log("matched RA " + ra);
|
if (VDBG) log("matched RA " + ra);
|
||||||
// Update lifetimes.
|
// Update lifetimes.
|
||||||
ra.mLastSeen = curTime();
|
ra.mLastSeen = currentTimeSeconds();
|
||||||
ra.mMinLifetime = ra.minLifetime(packet, length);
|
ra.mMinLifetime = ra.minLifetime(packet, length);
|
||||||
ra.seenCount++;
|
ra.seenCount++;
|
||||||
|
|
||||||
@@ -1128,7 +1129,7 @@ public class ApfFilter {
|
|||||||
pw.println("Program updates: " + mNumProgramUpdates);
|
pw.println("Program updates: " + mNumProgramUpdates);
|
||||||
pw.println(String.format(
|
pw.println(String.format(
|
||||||
"Last program length %d, installed %ds ago, lifetime %ds",
|
"Last program length %d, installed %ds ago, lifetime %ds",
|
||||||
mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram,
|
mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram,
|
||||||
mLastInstalledProgramMinLifetime));
|
mLastInstalledProgramMinLifetime));
|
||||||
|
|
||||||
pw.println("RA filters:");
|
pw.println("RA filters:");
|
||||||
@@ -1137,7 +1138,7 @@ public class ApfFilter {
|
|||||||
pw.println(ra);
|
pw.println(ra);
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
pw.println(String.format(
|
pw.println(String.format(
|
||||||
"Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen));
|
"Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen));
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
pw.println("Last match:");
|
pw.println("Last match:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
|
|||||||
@@ -29,9 +29,11 @@ import android.net.metrics.IpConnectivityLog;
|
|||||||
import android.net.metrics.RaEvent;
|
import android.net.metrics.RaEvent;
|
||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
import static android.system.OsConstants.*;
|
import static android.system.OsConstants.*;
|
||||||
|
|
||||||
@@ -604,6 +606,8 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
|
public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
|
||||||
private FileDescriptor mWriteSocket;
|
private FileDescriptor mWriteSocket;
|
||||||
|
|
||||||
|
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
|
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
|
||||||
IpConnectivityLog log) throws Exception {
|
IpConnectivityLog log) throws Exception {
|
||||||
super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
|
super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
|
||||||
@@ -616,6 +620,11 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
Os.write(mWriteSocket, packet, 0, packet.length);
|
Os.write(mWriteSocket, packet, 0, packet.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long currentTimeSeconds() {
|
||||||
|
return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void maybeStartFilter() {
|
void maybeStartFilter() {
|
||||||
mHardwareAddress = MOCK_MAC_ADDR;
|
mHardwareAddress = MOCK_MAC_ADDR;
|
||||||
@@ -969,27 +978,30 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
|
|
||||||
// Verify that the last program pushed to the IpManager.Callback properly filters the
|
// Verify that the last program pushed to the IpManager.Callback properly filters the
|
||||||
// given packet for the given lifetime.
|
// given packet for the given lifetime.
|
||||||
private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet,
|
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
|
||||||
int lifetime) {
|
final int FRACTION_OF_LIFETIME = 6;
|
||||||
byte[] program = ipManagerCallback.getApfProgram();
|
final int ageLimit = lifetime / FRACTION_OF_LIFETIME;
|
||||||
|
|
||||||
// Verify new program should drop RA for 1/6th its lifetime
|
// Verify new program should drop RA for 1/6th its lifetime and pass afterwards.
|
||||||
assertDrop(program, packet.array());
|
assertDrop(program, packet.array());
|
||||||
assertDrop(program, packet.array(), lifetime/6);
|
assertDrop(program, packet.array(), ageLimit);
|
||||||
assertPass(program, packet.array(), lifetime/6 + 1);
|
assertPass(program, packet.array(), ageLimit + 1);
|
||||||
assertPass(program, packet.array(), lifetime);
|
assertPass(program, packet.array(), lifetime);
|
||||||
|
|
||||||
// Verify RA checksum is ignored
|
// Verify RA checksum is ignored
|
||||||
|
final short originalChecksum = packet.getShort(ICMP6_RA_CHECKSUM_OFFSET);
|
||||||
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
|
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
|
||||||
assertDrop(program, packet.array());
|
assertDrop(program, packet.array());
|
||||||
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
|
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
|
||||||
assertDrop(program, packet.array());
|
assertDrop(program, packet.array());
|
||||||
|
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, originalChecksum);
|
||||||
|
|
||||||
// Verify other changes to RA make it not match filter
|
// Verify other changes to RA make it not match filter
|
||||||
|
final byte originalFirstByte = packet.get(0);
|
||||||
packet.put(0, (byte)-1);
|
packet.put(0, (byte)-1);
|
||||||
assertPass(program, packet.array());
|
assertPass(program, packet.array());
|
||||||
packet.put(0, (byte)0);
|
packet.put(0, (byte)0);
|
||||||
assertDrop(program, packet.array());
|
assertDrop(program, packet.array());
|
||||||
|
packet.put(0, originalFirstByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that when ApfFilter is shown the given packet, it generates a program to filter it
|
// Test that when ApfFilter is shown the given packet, it generates a program to filter it
|
||||||
@@ -999,9 +1011,8 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
// Verify new program generated if ApfFilter witnesses RA
|
// Verify new program generated if ApfFilter witnesses RA
|
||||||
ipManagerCallback.resetApfProgramWait();
|
ipManagerCallback.resetApfProgramWait();
|
||||||
apfFilter.pretendPacketReceived(packet.array());
|
apfFilter.pretendPacketReceived(packet.array());
|
||||||
ipManagerCallback.getApfProgram();
|
byte[] program = ipManagerCallback.getApfProgram();
|
||||||
|
verifyRaLifetime(program, packet, lifetime);
|
||||||
verifyRaLifetime(ipManagerCallback, packet, lifetime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyRaEvent(RaEvent expected) {
|
private void verifyRaEvent(RaEvent expected) {
|
||||||
@@ -1046,18 +1057,26 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
||||||
byte[] program = ipManagerCallback.getApfProgram();
|
byte[] program = ipManagerCallback.getApfProgram();
|
||||||
|
|
||||||
|
final int ROUTER_LIFETIME = 1000;
|
||||||
|
final int PREFIX_VALID_LIFETIME = 200;
|
||||||
|
final int PREFIX_PREFERRED_LIFETIME = 100;
|
||||||
|
final int RDNSS_LIFETIME = 300;
|
||||||
|
final int ROUTE_LIFETIME = 400;
|
||||||
|
// Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000.
|
||||||
|
final int DNSSL_LIFETIME = 2000;
|
||||||
|
|
||||||
// Verify RA is passed the first time
|
// Verify RA is passed the first time
|
||||||
ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
|
ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
|
||||||
basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
|
basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
|
||||||
basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
|
basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
|
||||||
basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
|
basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
|
||||||
basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
|
basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME);
|
||||||
basePacket.position(IPV6_DEST_ADDR_OFFSET);
|
basePacket.position(IPV6_DEST_ADDR_OFFSET);
|
||||||
basePacket.put(IPV6_ALL_NODES_ADDRESS);
|
basePacket.put(IPV6_ALL_NODES_ADDRESS);
|
||||||
assertPass(program, basePacket.array());
|
assertPass(program, basePacket.array());
|
||||||
|
|
||||||
testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
|
testRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
|
||||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));
|
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
|
||||||
|
|
||||||
// Ensure zero-length options cause the packet to be silently skipped.
|
// Ensure zero-length options cause the packet to be silently skipped.
|
||||||
// Do this before we test other packets. http://b/29586253
|
// Do this before we test other packets. http://b/29586253
|
||||||
@@ -1079,11 +1098,14 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
|
prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
|
||||||
prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8));
|
prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8));
|
||||||
prefixOptionPacket.putInt(
|
prefixOptionPacket.putInt(
|
||||||
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, 100);
|
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
|
||||||
|
PREFIX_PREFERRED_LIFETIME);
|
||||||
prefixOptionPacket.putInt(
|
prefixOptionPacket.putInt(
|
||||||
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200);
|
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
|
||||||
testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100);
|
PREFIX_VALID_LIFETIME);
|
||||||
verifyRaEvent(new RaEvent(1000, 200, 100, -1, -1, -1));
|
testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
|
||||||
|
verifyRaEvent(new RaEvent(
|
||||||
|
ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
|
||||||
|
|
||||||
ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
|
ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
|
||||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||||
@@ -1092,9 +1114,9 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE);
|
rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE);
|
||||||
rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
||||||
rdnssOptionPacket.putInt(
|
rdnssOptionPacket.putInt(
|
||||||
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300);
|
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
|
||||||
testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300);
|
testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
|
||||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, 300, -1));
|
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
|
||||||
|
|
||||||
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
|
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
|
||||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||||
@@ -1103,9 +1125,9 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE);
|
routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE);
|
||||||
routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
||||||
routeInfoOptionPacket.putInt(
|
routeInfoOptionPacket.putInt(
|
||||||
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400);
|
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
|
||||||
testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400);
|
testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
|
||||||
verifyRaEvent(new RaEvent(1000, -1, -1, 400, -1, -1));
|
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
|
||||||
|
|
||||||
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
|
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
|
||||||
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
|
||||||
@@ -1114,18 +1136,17 @@ public class ApfTest extends AndroidTestCase {
|
|||||||
dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE);
|
dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE);
|
||||||
dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
|
||||||
dnsslOptionPacket.putInt(
|
dnsslOptionPacket.putInt(
|
||||||
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 2000);
|
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
|
||||||
// Note that lifetime of 2000 will be ignored in favor of shorter
|
testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
|
||||||
// route lifetime of 1000.
|
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
|
||||||
testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000);
|
|
||||||
verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, 2000));
|
|
||||||
|
|
||||||
// Verify that current program filters all five RAs:
|
// Verify that current program filters all five RAs:
|
||||||
verifyRaLifetime(ipManagerCallback, basePacket, 1000);
|
program = ipManagerCallback.getApfProgram();
|
||||||
verifyRaLifetime(ipManagerCallback, prefixOptionPacket, 100);
|
verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
|
||||||
verifyRaLifetime(ipManagerCallback, rdnssOptionPacket, 300);
|
verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
|
||||||
verifyRaLifetime(ipManagerCallback, routeInfoOptionPacket, 400);
|
verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME);
|
||||||
verifyRaLifetime(ipManagerCallback, dnsslOptionPacket, 1000);
|
verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME);
|
||||||
|
verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME);
|
||||||
|
|
||||||
apfFilter.shutdown();
|
apfFilter.shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user