Merge "[CTT-6] Update TCP conntrack entry timeout while adding rules"
This commit is contained in:
@@ -371,6 +371,7 @@ public class BpfCoordinatorShimImpl
|
|||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// Silent if the rule already exists. Note that the errno EEXIST was rethrown as
|
// Silent if the rule already exists. Note that the errno EEXIST was rethrown as
|
||||||
// IllegalStateException. See BpfMap#insertEntry.
|
// IllegalStateException. See BpfMap#insertEntry.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,11 @@ public abstract class BpfCoordinatorShim {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a tethering IPv4 offload rule to appropriate BPF map.
|
* Adds a tethering IPv4 offload rule to appropriate BPF map.
|
||||||
|
*
|
||||||
|
* @param downstream true if downstream, false if upstream.
|
||||||
|
* @param key the key to add.
|
||||||
|
* @param value the value to add.
|
||||||
|
* @return true iff the map was modified, false if the key exists or there was an error.
|
||||||
*/
|
*/
|
||||||
public abstract boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
|
public abstract boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
|
||||||
@NonNull Tether4Value value);
|
@NonNull Tether4Value value);
|
||||||
|
|||||||
@@ -130,8 +130,14 @@ public class BpfCoordinator {
|
|||||||
static final int CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS = 60_000;
|
static final int CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS = 60_000;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int CONNTRACK_TIMEOUT_UPDATE_SLACK_MS = 20_000;
|
static final int CONNTRACK_TIMEOUT_UPDATE_SLACK_MS = 20_000;
|
||||||
|
|
||||||
|
// Default timeouts sync from /proc/sys/net/netfilter/nf_conntrack_*
|
||||||
|
// See also kernel document nf_conntrack-sysctl.txt.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED = 432_000;
|
static final int NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED = 432_000;
|
||||||
|
static final int NF_CONNTRACK_TCP_TIMEOUT_UNACKNOWLEDGED = 300;
|
||||||
|
// The default value is 120 for 5.10 and that thus the periodicity of the updates of 60s is
|
||||||
|
// low enough to support all ACK kernels.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
|
static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
|
||||||
|
|
||||||
@@ -1566,6 +1572,18 @@ public class BpfCoordinator {
|
|||||||
final Tether4Key downstream4Key = makeTetherDownstream4Key(e, tetherClient,
|
final Tether4Key downstream4Key = makeTetherDownstream4Key(e, tetherClient,
|
||||||
upstreamIndex);
|
upstreamIndex);
|
||||||
|
|
||||||
|
// Using the timeout to distinguish tcp state is not a decent way. Need to fix.
|
||||||
|
// The received IPCTNL_MSG_CT_NEW must pass ConntrackMonitor#isEstablishedNatSession
|
||||||
|
// which checks CTA_STATUS. It implies that this entry has at least reached tcp
|
||||||
|
// state "established". For safety, treat any timeout which is equal or larger than 300
|
||||||
|
// seconds (UNACKNOWLEDGED, ESTABLISHED, ..) to be "established".
|
||||||
|
// TODO: parse tcp state in conntrack monitor.
|
||||||
|
final boolean isTcpEstablished =
|
||||||
|
e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
||||||
|
| NetlinkConstants.IPCTNL_MSG_CT_NEW)
|
||||||
|
&& e.tupleOrig.protoNum == OsConstants.IPPROTO_TCP
|
||||||
|
&& (e.timeoutSec >= NF_CONNTRACK_TCP_TIMEOUT_UNACKNOWLEDGED);
|
||||||
|
|
||||||
if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
||||||
| NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
|
| NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
|
||||||
final boolean deletedUpstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
|
final boolean deletedUpstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
|
||||||
@@ -1592,11 +1610,41 @@ public class BpfCoordinator {
|
|||||||
final Tether4Value upstream4Value = makeTetherUpstream4Value(e, upstreamIndex);
|
final Tether4Value upstream4Value = makeTetherUpstream4Value(e, upstreamIndex);
|
||||||
final Tether4Value downstream4Value = makeTetherDownstream4Value(e, tetherClient,
|
final Tether4Value downstream4Value = makeTetherDownstream4Value(e, tetherClient,
|
||||||
upstreamIndex);
|
upstreamIndex);
|
||||||
|
|
||||||
maybeAddDevMap(upstreamIndex, tetherClient.downstreamIfindex);
|
maybeAddDevMap(upstreamIndex, tetherClient.downstreamIfindex);
|
||||||
maybeSetLimit(upstreamIndex);
|
maybeSetLimit(upstreamIndex);
|
||||||
mBpfCoordinatorShim.tetherOffloadRuleAdd(UPSTREAM, upstream4Key, upstream4Value);
|
|
||||||
mBpfCoordinatorShim.tetherOffloadRuleAdd(DOWNSTREAM, downstream4Key, downstream4Value);
|
final boolean upstreamAdded = mBpfCoordinatorShim.tetherOffloadRuleAdd(UPSTREAM,
|
||||||
|
upstream4Key, upstream4Value);
|
||||||
|
final boolean downstreamAdded = mBpfCoordinatorShim.tetherOffloadRuleAdd(DOWNSTREAM,
|
||||||
|
downstream4Key, downstream4Value);
|
||||||
|
|
||||||
|
if (upstreamAdded != downstreamAdded) {
|
||||||
|
mLog.e("The bidirectional rules should be added or not added concurrently ("
|
||||||
|
+ "upstream: " + upstreamAdded
|
||||||
|
+ ", downstream: " + downstreamAdded + "). "
|
||||||
|
+ "Remove the added rules.");
|
||||||
|
if (upstreamAdded) {
|
||||||
|
mBpfCoordinatorShim.tetherOffloadRuleRemove(UPSTREAM, upstream4Key);
|
||||||
|
}
|
||||||
|
if (downstreamAdded) {
|
||||||
|
mBpfCoordinatorShim.tetherOffloadRuleRemove(DOWNSTREAM, downstream4Key);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update TCP timeout iif it is first time we're adding the rules. Needed because a
|
||||||
|
// payload data packet may have gone through non-offload path, before we added offload
|
||||||
|
// rules, and that this may result in in-kernel conntrack state being in ESTABLISHED
|
||||||
|
// but pending ACK (ie. UNACKED) state. But the in-kernel conntrack might never see the
|
||||||
|
// ACK because we just added offload rules. As such after adding the rules we need to
|
||||||
|
// force the timeout back to the normal ESTABLISHED timeout of 5 days. Note that
|
||||||
|
// updating the timeout will trigger another netlink event with the updated timeout.
|
||||||
|
// TODO: Remove this once the tcp state is parsed.
|
||||||
|
if (isTcpEstablished && upstreamAdded && downstreamAdded) {
|
||||||
|
updateConntrackTimeout((byte) upstream4Key.l4proto,
|
||||||
|
parseIPv4Address(upstream4Key.src4), (short) upstream4Key.srcPort,
|
||||||
|
parseIPv4Address(upstream4Key.dst4), (short) upstream4Key.dstPort);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1879,6 +1927,7 @@ public class BpfCoordinator {
|
|||||||
// - proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
|
// - proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
|
||||||
// - proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
|
// - proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
|
||||||
// See kernel document nf_conntrack-sysctl.txt.
|
// See kernel document nf_conntrack-sysctl.txt.
|
||||||
|
// TODO: we should account for the fact that lastUsed is in the past and not exactly now.
|
||||||
final int timeoutSec = (proto == OsConstants.IPPROTO_TCP)
|
final int timeoutSec = (proto == OsConstants.IPPROTO_TCP)
|
||||||
? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
|
? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
|
||||||
: NF_CONNTRACK_UDP_TIMEOUT_STREAM;
|
: NF_CONNTRACK_UDP_TIMEOUT_STREAM;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ 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.CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS;
|
import static com.android.networkstack.tethering.BpfCoordinator.CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS;
|
||||||
import static com.android.networkstack.tethering.BpfCoordinator.CONNTRACK_TIMEOUT_UPDATE_SLACK_MS;
|
import static com.android.networkstack.tethering.BpfCoordinator.CONNTRACK_TIMEOUT_UPDATE_SLACK_MS;
|
||||||
|
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.NF_CONNTRACK_UDP_TIMEOUT_STREAM;
|
||||||
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;
|
||||||
@@ -1369,8 +1370,14 @@ public class BpfCoordinatorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final int status = (msgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK;
|
final int status = (msgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK;
|
||||||
final int timeoutSec = (msgType == IPCTNL_MSG_CT_NEW) ? 100 /* nonzero, new */
|
int timeoutSec;
|
||||||
: 0 /* unused, delete */;
|
if (msgType == IPCTNL_MSG_CT_NEW) {
|
||||||
|
timeoutSec = (proto == IPPROTO_TCP)
|
||||||
|
? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
|
||||||
|
: NF_CONNTRACK_UDP_TIMEOUT_STREAM;
|
||||||
|
} else {
|
||||||
|
timeoutSec = 0; /* unused, CT_DELETE */
|
||||||
|
}
|
||||||
return new ConntrackEvent(
|
return new ConntrackEvent(
|
||||||
(short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
|
(short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
|
||||||
new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR),
|
new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR),
|
||||||
|
|||||||
Reference in New Issue
Block a user