Refresh conntrack entry timeout

Needed because the BPF maps offloads IPv4 traffic. The kernel can't
trace the offloaded traffic to keep the conntrack entry.

Bug: 190783768
Test: atest TetheringCoverageTests
Original-Change: https://android-review.googlesource.com/1566871
Merged-In: Idbcf686c9b2124b192944156ac5111be741744fb
Change-Id: Idbcf686c9b2124b192944156ac5111be741744fb
This commit is contained in:
Nucca Chen
2021-06-25 18:55:47 +00:00
parent 01785d7eff
commit fadd7b7a33
2 changed files with 302 additions and 10 deletions

View File

@@ -41,7 +41,9 @@ import android.net.TetherOffloadRuleParcel;
import android.net.ip.ConntrackMonitor; import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer; import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer; import android.net.ip.IpServer;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
import android.net.netstats.provider.NetworkStatsProvider; import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.InterfaceParams; import android.net.util.InterfaceParams;
import android.net.util.SharedLog; import android.net.util.SharedLog;
@@ -49,6 +51,7 @@ import android.net.util.TetheringUtils.ForwardedStats;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
@@ -120,6 +123,13 @@ public class BpfCoordinator {
return makeMapPath((downstream ? "downstream" : "upstream") + ipVersion); return makeMapPath((downstream ? "downstream" : "upstream") + ipVersion);
} }
@VisibleForTesting
static final int POLLING_CONNTRACK_TIMEOUT_MS = 60_000;
@VisibleForTesting
static final int NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED = 432000;
@VisibleForTesting
static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
@VisibleForTesting @VisibleForTesting
enum StatsType { enum StatsType {
STATS_PER_IFACE, STATS_PER_IFACE,
@@ -234,11 +244,17 @@ public class BpfCoordinator {
private int mLastIPv4UpstreamIfindex = 0; private int mLastIPv4UpstreamIfindex = 0;
// Runnable that used by scheduling next polling of stats. // Runnable that used by scheduling next polling of stats.
private final Runnable mScheduledPollingTask = () -> { private final Runnable mScheduledPollingStats = () -> {
updateForwardedStats(); updateForwardedStats();
maybeSchedulePollingStats(); maybeSchedulePollingStats();
}; };
// Runnable that used by scheduling next polling of conntrack timeout.
private final Runnable mScheduledPollingConntrackTimeout = () -> {
maybeRefreshConntrackTimeout();
maybeSchedulePollingConntrackTimeout();
};
// TODO: add BpfMap<TetherDownstream64Key, TetherDownstream64Value> retrieving function. // TODO: add BpfMap<TetherDownstream64Key, TetherDownstream64Value> retrieving function.
@VisibleForTesting @VisibleForTesting
public abstract static class Dependencies { public abstract static class Dependencies {
@@ -267,6 +283,13 @@ public class BpfCoordinator {
return InterfaceParams.getByName(ifName); return InterfaceParams.getByName(ifName);
} }
/**
* Represents an estimate of elapsed time since boot in nanoseconds.
*/
public long elapsedRealtimeNanos() {
return SystemClock.elapsedRealtimeNanos();
}
/** /**
* Check OS Build at least S. * Check OS Build at least S.
* *
@@ -274,7 +297,6 @@ public class BpfCoordinator {
* testing different code flows concurrently. * testing different code flows concurrently.
*/ */
public boolean isAtLeastS() { public boolean isAtLeastS() {
// TODO: consider using ShimUtils.isAtLeastS.
return SdkLevel.isAtLeastS(); return SdkLevel.isAtLeastS();
} }
@@ -412,6 +434,7 @@ public class BpfCoordinator {
mPollingStarted = true; mPollingStarted = true;
maybeSchedulePollingStats(); maybeSchedulePollingStats();
maybeSchedulePollingConntrackTimeout();
mLog.i("Polling started"); mLog.i("Polling started");
} }
@@ -427,9 +450,13 @@ public class BpfCoordinator {
public void stopPolling() { public void stopPolling() {
if (!mPollingStarted) return; if (!mPollingStarted) return;
// Stop scheduled polling tasks and poll the latest stats from BPF maps. // Stop scheduled polling conntrack timeout.
if (mHandler.hasCallbacks(mScheduledPollingTask)) { if (mHandler.hasCallbacks(mScheduledPollingConntrackTimeout)) {
mHandler.removeCallbacks(mScheduledPollingTask); mHandler.removeCallbacks(mScheduledPollingConntrackTimeout);
}
// Stop scheduled polling stats and poll the latest stats from BPF maps.
if (mHandler.hasCallbacks(mScheduledPollingStats)) {
mHandler.removeCallbacks(mScheduledPollingStats);
} }
updateForwardedStats(); updateForwardedStats();
mPollingStarted = false; mPollingStarted = false;
@@ -1412,12 +1439,86 @@ public class BpfCoordinator {
return addr6; return addr6;
} }
// Support raw ip only. @Nullable
// TODO: add ether ip support. private Inet4Address ipv4MappedAddressBytesToIpv4Address(final byte[] addr46) {
if (addr46.length != 16) return null;
if (addr46[0] != 0 || addr46[1] != 0 || addr46[2] != 0 || addr46[3] != 0
|| addr46[4] != 0 || addr46[5] != 0 || addr46[6] != 0 || addr46[7] != 0
|| addr46[8] != 0 && addr46[9] != 0 || (addr46[10] & 0xff) != 0xff
|| (addr46[11] & 0xff) != 0xff) {
return null;
}
final byte[] addr4 = new byte[4];
addr4[0] = addr46[12];
addr4[1] = addr46[13];
addr4[2] = addr46[14];
addr4[3] = addr46[15];
return parseIPv4Address(addr4);
}
// TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules // TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules
// while TCP status is established. // while TCP status is established.
@VisibleForTesting @VisibleForTesting
class BpfConntrackEventConsumer implements ConntrackEventConsumer { class BpfConntrackEventConsumer implements ConntrackEventConsumer {
// The upstream4 and downstream4 rules are built as the following tables. Only raw ip
// upstream interface is supported. Note that the field "lastUsed" is only updated by
// BPF program which records the last used time for a given rule.
// TODO: support ether ip upstream interface.
//
// NAT network topology:
//
// public network (rawip) private network
// | UE |
// +------------+ V +------------+------------+ V +------------+
// | Sever +---------+ Upstream | Downstream +---------+ Client |
// +------------+ +------------+------------+ +------------+
//
// upstream4 key and value:
//
// +------+------------------------------------------------+
// | | TetherUpstream4Key |
// +------+------+------+------+------+------+------+------+
// |field |iif |dstMac|l4prot|src4 |dst4 |srcPor|dstPor|
// | | | |o | | |t |t |
// +------+------+------+------+------+------+------+------+
// |value |downst|downst|tcp/ |client|server|client|server|
// | |ream |ream |udp | | | | |
// +------+------+------+------+------+------+------+------+
//
// +------+---------------------------------------------------------------------+
// | | TetherUpstream4Value |
// +------+------+------+------+------+------+------+------+------+------+------+
// |field |oif |ethDst|ethSrc|ethPro|pmtu |src46 |dst46 |srcPor|dstPor|lastUs|
// | | |mac |mac |to | | | |t |t |ed |
// +------+------+------+------+------+------+------+------+------+------+------+
// |value |upstre|-- |-- |ETH_P_|1500 |upstre|server|upstre|server|-- |
// | |am | | |IP | |am | |am | | |
// +------+------+------+------+------+------+------+------+------+------+------+
//
// downstream4 key and value:
//
// +------+------------------------------------------------+
// | | TetherDownstream4Key |
// +------+------+------+------+------+------+------+------+
// |field |iif |dstMac|l4prot|src4 |dst4 |srcPor|dstPor|
// | | | |o | | |t |t |
// +------+------+------+------+------+------+------+------+
// |value |upstre|-- |tcp/ |server|upstre|server|upstre|
// | |am | |udp | |am | |am |
// +------+------+------+------+------+------+------+------+
//
// +------+---------------------------------------------------------------------+
// | | TetherDownstream4Value |
// +------+------+------+------+------+------+------+------+------+------+------+
// |field |oif |ethDst|ethSrc|ethPro|pmtu |src46 |dst46 |srcPor|dstPor|lastUs|
// | | |mac |mac |to | | | |t |t |ed |
// +------+------+------+------+------+------+------+------+------+------+------+
// |value |downst|client|downst|ETH_P_|1500 |server|client|server|client|-- |
// | |ream | |ream |IP | | | | | | |
// +------+------+------+------+------+------+------+------+------+------+------+
//
@NonNull @NonNull
private Tether4Key makeTetherUpstream4Key( private Tether4Key makeTetherUpstream4Key(
@NonNull ConntrackEvent e, @NonNull ClientInfo c) { @NonNull ConntrackEvent e, @NonNull ClientInfo c) {
@@ -1751,14 +1852,89 @@ public class BpfCoordinator {
return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval); return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
} }
@Nullable
private Inet4Address parseIPv4Address(byte[] addrBytes) {
try {
final InetAddress ia = Inet4Address.getByAddress(addrBytes);
if (ia instanceof Inet4Address) return (Inet4Address) ia;
} catch (UnknownHostException | IllegalArgumentException e) {
mLog.e("Failed to parse IPv4 address: " + e);
}
return null;
}
// Update CTA_TUPLE_ORIG timeout for a given conntrack entry. Note that there will also be
// coming a conntrack event to notify updated timeout.
private void updateConntrackTimeout(byte proto, Inet4Address src4, short srcPort,
Inet4Address dst4, short dstPort) {
if (src4 == null || dst4 == null) return;
// TODO: consider acquiring the timeout setting from nf_conntrack_* variables.
// - proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
// - proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
// See kernel document nf_conntrack-sysctl.txt.
final int timeoutSec = (proto == OsConstants.IPPROTO_TCP)
? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
: NF_CONNTRACK_UDP_TIMEOUT_STREAM;
final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
proto, src4, (int) srcPort, dst4, (int) dstPort, timeoutSec);
try {
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
} catch (ErrnoException e) {
mLog.e("Error updating conntrack entry ("
+ "proto: " + proto + ", "
+ "src4: " + src4 + ", "
+ "srcPort: " + Short.toUnsignedInt(srcPort) + ", "
+ "dst4: " + dst4 + ", "
+ "dstPort: " + Short.toUnsignedInt(dstPort) + "), "
+ "msg: " + NetlinkConstants.hexify(msg) + ", "
+ "e: " + e);
}
}
private void maybeRefreshConntrackTimeout() {
final long now = mDeps.elapsedRealtimeNanos();
// Reverse the source and destination {address, port} from downstream value because
// #updateConntrackTimeout refresh the timeout of netlink attribute CTA_TUPLE_ORIG
// which is opposite direction for downstream map value.
mBpfCoordinatorShim.tetherOffloadRuleForEach(DOWNSTREAM, (k, v) -> {
if ((now - v.lastUsed) / 1_000_000 < POLLING_CONNTRACK_TIMEOUT_MS) {
updateConntrackTimeout((byte) k.l4proto,
ipv4MappedAddressBytesToIpv4Address(v.dst46), (short) v.dstPort,
ipv4MappedAddressBytesToIpv4Address(v.src46), (short) v.srcPort);
}
});
// TODO: Consider ignoring TCP traffic on upstream and monitor on downstream only
// because TCP is a bidirectional traffic. Probably don't need to extend timeout by
// both directions for TCP.
mBpfCoordinatorShim.tetherOffloadRuleForEach(UPSTREAM, (k, v) -> {
if ((now - v.lastUsed) / 1_000_000 < POLLING_CONNTRACK_TIMEOUT_MS) {
updateConntrackTimeout((byte) k.l4proto, parseIPv4Address(k.src4),
(short) k.srcPort, parseIPv4Address(k.dst4), (short) k.dstPort);
}
});
}
private void maybeSchedulePollingStats() { private void maybeSchedulePollingStats() {
if (!mPollingStarted) return; if (!mPollingStarted) return;
if (mHandler.hasCallbacks(mScheduledPollingTask)) { if (mHandler.hasCallbacks(mScheduledPollingStats)) {
mHandler.removeCallbacks(mScheduledPollingTask); mHandler.removeCallbacks(mScheduledPollingStats);
} }
mHandler.postDelayed(mScheduledPollingTask, getPollingInterval()); mHandler.postDelayed(mScheduledPollingStats, getPollingInterval());
}
private void maybeSchedulePollingConntrackTimeout() {
if (!mPollingStarted) return;
if (mHandler.hasCallbacks(mScheduledPollingConntrackTimeout)) {
mHandler.removeCallbacks(mScheduledPollingConntrackTimeout);
}
mHandler.postDelayed(mScheduledPollingConntrackTimeout, POLLING_CONNTRACK_TIMEOUT_MS);
} }
// Return forwarding rule map. This is used for testing only. // Return forwarding rule map. This is used for testing only.

View File

@@ -36,9 +36,13 @@ import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.NETLINK_NETFILTER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker; import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED;
import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_UDP_TIMEOUT_STREAM;
import static com.android.networkstack.tethering.BpfCoordinator.POLLING_CONNTRACK_TIMEOUT_MS;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType; import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
@@ -78,7 +82,9 @@ import android.net.TetherStatsParcel;
import android.net.ip.ConntrackMonitor; import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer; import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer; import android.net.ip.IpServer;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
import android.net.util.InterfaceParams; import android.net.util.InterfaceParams;
import android.net.util.SharedLog; import android.net.util.SharedLog;
import android.os.Build; import android.os.Build;
@@ -221,6 +227,7 @@ public class BpfCoordinatorTest {
// it has to access the non-static function of BPF coordinator. // it has to access the non-static function of BPF coordinator.
private BpfConntrackEventConsumer mConsumer; private BpfConntrackEventConsumer mConsumer;
private long mElapsedRealtimeNanos = 0;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor = private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class); ArgumentCaptor.forClass(ArrayList.class);
private final TestLooper mTestLooper = new TestLooper(); private final TestLooper mTestLooper = new TestLooper();
@@ -260,6 +267,10 @@ public class BpfCoordinatorTest {
return mConntrackMonitor; return mConntrackMonitor;
} }
public long elapsedRealtimeNanos() {
return mElapsedRealtimeNanos;
}
@Nullable @Nullable
public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() { public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() {
return mBpfDownstream4Map; return mBpfDownstream4Map;
@@ -1343,6 +1354,11 @@ public class BpfCoordinatorTest {
PRIVATE_ADDR_V4MAPPED_BYTES, REMOTE_PORT, PRIVATE_PORT, 0 /* lastUsed */); PRIVATE_ADDR_V4MAPPED_BYTES, REMOTE_PORT, PRIVATE_PORT, 0 /* lastUsed */);
} }
@NonNull
private Tether4Key makeDownstream4Key() {
return makeDownstream4Key(IPPROTO_TCP);
}
@NonNull @NonNull
private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) { private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) { if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
@@ -1504,4 +1520,104 @@ public class BpfCoordinatorTest {
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP)); mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP));
verify(mBpfDevMap, never()).updateEntry(any(), any()); verify(mBpfDevMap, never()).updateEntry(any(), any());
} }
private void setElapsedRealtimeNanos(long nanoSec) {
mElapsedRealtimeNanos = nanoSec;
}
private void checkRefreshConntrackTimeout(final TestBpfMap<Tether4Key, Tether4Value> bpfMap,
final Tether4Key tcpKey, final Tether4Value tcpValue, final Tether4Key udpKey,
final Tether4Value udpValue) throws Exception {
// Both system elapsed time since boot and the rule last used time are used to measure
// the rule expiration. In this test, all test rules are fixed the last used time to 0.
// Set the different testing elapsed time to make the rule to be valid or expired.
//
// Timeline:
// 0 60 (seconds)
// +---+---+---+---+--...--+---+---+---+---+---+- ..
// | POLLING_CONNTRACK_TIMEOUT_MS |
// +---+---+---+---+--...--+---+---+---+---+---+- ..
// |<- valid diff ->|
// |<- expired diff ->|
// ^ ^ ^
// last used time elapsed time (valid) elapsed time (expired)
final long validTime = (POLLING_CONNTRACK_TIMEOUT_MS - 1) * 1_000_000L;
final long expiredTime = (POLLING_CONNTRACK_TIMEOUT_MS + 1) * 1_000_000L;
// Static mocking for NetlinkSocket.
MockitoSession mockSession = ExtendedMockito.mockitoSession()
.mockStatic(NetlinkSocket.class)
.startMocking();
try {
final BpfCoordinator coordinator = makeBpfCoordinator();
coordinator.startPolling();
bpfMap.insertEntry(tcpKey, tcpValue);
bpfMap.insertEntry(udpKey, udpValue);
// [1] Don't refresh contrack timeout.
setElapsedRealtimeNanos(expiredTime);
mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
waitForIdle();
ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
// [2] Refresh contrack timeout.
setElapsedRealtimeNanos(validTime);
mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
waitForIdle();
final byte[] expectedNetlinkTcp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
IPPROTO_TCP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR,
(int) REMOTE_PORT, NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED);
final byte[] expectedNetlinkUdp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
IPPROTO_UDP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR,
(int) REMOTE_PORT, NF_CONNTRACK_UDP_TIMEOUT_STREAM);
ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage(
eq(NETLINK_NETFILTER), eq(expectedNetlinkTcp)));
ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage(
eq(NETLINK_NETFILTER), eq(expectedNetlinkUdp)));
ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
// [3] Don't refresh contrack timeout if polling stopped.
coordinator.stopPolling();
mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
waitForIdle();
ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
} finally {
mockSession.finishMocking();
}
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
public void testRefreshConntrackTimeout_Upstream4Map() throws Exception {
// TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object.
final TestBpfMap<Tether4Key, Tether4Value> bpfUpstream4Map =
new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
doReturn(bpfUpstream4Map).when(mDeps).getBpfUpstream4Map();
final Tether4Key tcpKey = makeUpstream4Key(IPPROTO_TCP);
final Tether4Key udpKey = makeUpstream4Key(IPPROTO_UDP);
final Tether4Value tcpValue = makeUpstream4Value();
final Tether4Value udpValue = makeUpstream4Value();
checkRefreshConntrackTimeout(bpfUpstream4Map, tcpKey, tcpValue, udpKey, udpValue);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
public void testRefreshConntrackTimeout_Downstream4Map() throws Exception {
// TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object.
final TestBpfMap<Tether4Key, Tether4Value> bpfDownstream4Map =
new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
doReturn(bpfDownstream4Map).when(mDeps).getBpfDownstream4Map();
final Tether4Key tcpKey = makeDownstream4Key(IPPROTO_TCP);
final Tether4Key udpKey = makeDownstream4Key(IPPROTO_UDP);
final Tether4Value tcpValue = makeDownstream4Value();
final Tether4Value udpValue = makeDownstream4Value();
checkRefreshConntrackTimeout(bpfDownstream4Map, tcpKey, tcpValue, udpKey, udpValue);
}
} }