[NFCT.TETHER.10] Add/delete IPv4 offload BPF rules to/from BPF map
Access the IPv4 downstream and upstream BPF map with the built rules.
Test: atest TetheringCoverageTests
Change-Id: I8cd6e49b377c72250988019eea57f93cccd78309
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index eafa3ea..90b9b3f 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -28,7 +28,11 @@
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
+import com.android.networkstack.tethering.TetherDownstream4Key;
+import com.android.networkstack.tethering.TetherDownstream4Value;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream4Key;
+import com.android.networkstack.tethering.TetherUpstream4Value;
/**
* Bpf coordinator class for API shims.
@@ -132,6 +136,32 @@
}
@Override
+ public boolean tetherOffloadRuleAdd(@NonNull TetherDownstream4Key key,
+ @NonNull TetherDownstream4Value value) {
+ /* no op */
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleRemove(@NonNull TetherDownstream4Key key) {
+ /* no op */
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleAdd(@NonNull TetherUpstream4Key key,
+ @NonNull TetherUpstream4Value value) {
+ /* no op */
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleRemove(@NonNull TetherUpstream4Key key) {
+ /* no op */
+ return true;
+ }
+
+ @Override
public String toString() {
return "Netd used";
}
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index c0d85ae..b9ce769 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -30,12 +30,16 @@
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.BpfMap;
+import com.android.networkstack.tethering.TetherDownstream4Key;
+import com.android.networkstack.tethering.TetherDownstream4Value;
import com.android.networkstack.tethering.TetherDownstream6Key;
import com.android.networkstack.tethering.TetherDownstream6Value;
import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherStatsKey;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream4Key;
+import com.android.networkstack.tethering.TetherUpstream4Value;
import java.io.FileDescriptor;
@@ -54,6 +58,16 @@
@NonNull
private final SharedLog mLog;
+ // BPF map of ingress queueing discipline which pre-processes the packets by the IPv4
+ // downstream rules.
+ @Nullable
+ private final BpfMap<TetherDownstream4Key, TetherDownstream4Value> mBpfDownstream4Map;
+
+ // BPF map of ingress queueing discipline which pre-processes the packets by the IPv4
+ // upstream rules.
+ @Nullable
+ private final BpfMap<TetherUpstream4Key, TetherUpstream4Value> mBpfUpstream4Map;
+
// BPF map of ingress queueing discipline which pre-processes the packets by the IPv6
// forwarding rules.
@Nullable
@@ -69,6 +83,8 @@
public BpfCoordinatorShimImpl(@NonNull final Dependencies deps) {
mLog = deps.getSharedLog().forSubComponent(TAG);
+ mBpfDownstream4Map = deps.getBpfDownstream4Map();
+ mBpfUpstream4Map = deps.getBpfUpstream4Map();
mBpfDownstream6Map = deps.getBpfDownstream6Map();
mBpfStatsMap = deps.getBpfStatsMap();
mBpfLimitMap = deps.getBpfLimitMap();
@@ -76,7 +92,8 @@
@Override
public boolean isInitialized() {
- return mBpfDownstream6Map != null && mBpfStatsMap != null && mBpfLimitMap != null;
+ return mBpfDownstream4Map != null && mBpfUpstream4Map != null && mBpfDownstream6Map != null
+ && mBpfStatsMap != null && mBpfLimitMap != null;
}
@Override
@@ -233,14 +250,85 @@
}
@Override
+ public boolean tetherOffloadRuleAdd(@NonNull TetherDownstream4Key key,
+ @NonNull TetherDownstream4Value value) {
+ if (!isInitialized()) return false;
+
+ try {
+ // The last used time field of the value is updated by the bpf program. Adding the same
+ // map pair twice causes the unexpected refresh. Must be fixed before starting the
+ // conntrack timeout extension implementation.
+ // TODO: consider using insertEntry.
+ mBpfDownstream4Map.updateEntry(key, value);
+ } catch (ErrnoException e) {
+ mLog.e("Could not update entry: ", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleRemove(@NonNull TetherDownstream4Key key) {
+ if (!isInitialized()) return false;
+
+ try {
+ mBpfDownstream4Map.deleteEntry(key);
+ } catch (ErrnoException e) {
+ // Silent if the rule did not exist.
+ if (e.errno != OsConstants.ENOENT) {
+ mLog.e("Could not delete entry: ", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleAdd(@NonNull TetherUpstream4Key key,
+ @NonNull TetherUpstream4Value value) {
+ if (!isInitialized()) return false;
+
+ try {
+ // The last used time field of the value is updated by the bpf program. Adding the same
+ // map pair twice causes the unexpected refresh. Must be fixed before starting the
+ // conntrack timeout extension implementation.
+ // TODO: consider using insertEntry.
+ mBpfUpstream4Map.updateEntry(key, value);
+ } catch (ErrnoException e) {
+ mLog.e("Could not update entry: ", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean tetherOffloadRuleRemove(@NonNull TetherUpstream4Key key) {
+ if (!isInitialized()) return false;
+
+ try {
+ mBpfUpstream4Map.deleteEntry(key);
+ } catch (ErrnoException e) {
+ // Silent if the rule did not exist.
+ if (e.errno != OsConstants.ENOENT) {
+ mLog.e("Could not delete entry: ", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public String toString() {
- return "mBpfDownstream6Map{"
+ return "mBpfDownstream4Map{"
+ + (mBpfDownstream4Map != null ? "initialized" : "not initialized") + "}, "
+ + "mBpfUpstream4Map{"
+ + (mBpfUpstream4Map != null ? "initialized" : "not initialized") + "}, "
+ + "mBpfDownstream6Map{"
+ (mBpfDownstream6Map != null ? "initialized" : "not initialized") + "}, "
+ "mBpfStatsMap{"
+ (mBpfStatsMap != null ? "initialized" : "not initialized") + "}, "
+ "mBpfLimitMap{"
- + (mBpfLimitMap != null ? "initialized" : "not initialized") + "} "
- + "}";
+ + (mBpfLimitMap != null ? "initialized" : "not initialized") + "} ";
}
/**
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 61abfa3..36d2de1 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -23,7 +23,11 @@
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
+import com.android.networkstack.tethering.TetherDownstream4Key;
+import com.android.networkstack.tethering.TetherDownstream4Value;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream4Key;
+import com.android.networkstack.tethering.TetherUpstream4Value;
/**
* Bpf coordinator class for API shims.
@@ -108,5 +112,27 @@
*/
@Nullable
public abstract TetherStatsValue tetherOffloadGetAndClearStats(int ifIndex);
+
+ /**
+ * Adds a tethering IPv4 downstream offload rule to BPF map.
+ */
+ public abstract boolean tetherOffloadRuleAdd(@NonNull TetherDownstream4Key key,
+ @NonNull TetherDownstream4Value value);
+
+ /**
+ * Deletes a tethering IPv4 downstream offload rule from the BPF map.
+ */
+ public abstract boolean tetherOffloadRuleRemove(@NonNull TetherDownstream4Key key);
+
+ /**
+ * Adds a tethering IPv4 upstream offload rule to BPF map.
+ */
+ public abstract boolean tetherOffloadRuleAdd(@NonNull TetherUpstream4Key key,
+ @NonNull TetherUpstream4Value value);
+
+ /**
+ * Deletes a tethering IPv4 upstream offload rule from the BPF map.
+ */
+ public abstract boolean tetherOffloadRuleRemove(@NonNull TetherUpstream4Key key);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 717bf61..e4216d8 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -87,6 +87,10 @@
private static final int DUMP_TIMEOUT_MS = 10_000;
private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString(
"00:00:00:00:00:00");
+ private static final String TETHER_DOWNSTREAM4_MAP_PATH =
+ "/sys/fs/bpf/tethering/map_offload_tether_downstream4_map";
+ private static final String TETHER_UPSTREAM4_MAP_PATH =
+ "/sys/fs/bpf/tethering/map_offload_tether_upstream4_map";
private static final String TETHER_DOWNSTREAM6_FS_PATH =
"/sys/fs/bpf/tethering/map_offload_tether_downstream6_map";
private static final String TETHER_STATS_MAP_PATH =
@@ -232,6 +236,30 @@
return SdkLevel.isAtLeastS();
}
+ /** Get downstream4 BPF map. */
+ @Nullable public BpfMap<TetherDownstream4Key, TetherDownstream4Value>
+ getBpfDownstream4Map() {
+ try {
+ return new BpfMap<>(TETHER_DOWNSTREAM4_MAP_PATH,
+ BpfMap.BPF_F_RDWR, TetherDownstream4Key.class, TetherDownstream4Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create downstream4 map: " + e);
+ return null;
+ }
+ }
+
+ /** Get upstream4 BPF map. */
+ @Nullable public BpfMap<TetherUpstream4Key, TetherUpstream4Value>
+ getBpfUpstream4Map() {
+ try {
+ return new BpfMap<>(TETHER_UPSTREAM4_MAP_PATH,
+ BpfMap.BPF_F_RDWR, TetherUpstream4Key.class, TetherUpstream4Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create upstream4 map: " + e);
+ return null;
+ }
+ }
+
/** Get downstream6 BPF map. */
@Nullable public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
getBpfDownstream6Map() {
@@ -896,14 +924,12 @@
@NonNull
private TetherUpstream4Value makeTetherUpstream4Value(@NonNull ConntrackEvent e,
int upstreamIndex) {
- // TODO: convert {src46, dst46} from ipv4 address (a.b.c.d) to ipv4-mapped address
- // (::ffff:a.b.d.d).
return new TetherUpstream4Value(upstreamIndex,
NULL_MAC_ADDRESS /* ethDstMac (rawip) */,
NULL_MAC_ADDRESS /* ethSrcMac (rawip) */, ETH_P_IP,
- NetworkStackConstants.ETHER_MTU, e.tupleReply.dstIp.getAddress(),
- e.tupleReply.srcIp.getAddress(), e.tupleReply.dstPort, e.tupleReply.srcPort,
- 0 /* lastUsed, filled by bpf prog only */);
+ NetworkStackConstants.ETHER_MTU, toIpv4MappedAddressBytes(e.tupleReply.dstIp),
+ toIpv4MappedAddressBytes(e.tupleReply.srcIp), e.tupleReply.dstPort,
+ e.tupleReply.srcPort, 0 /* lastUsed, filled by bpf prog only */);
}
@NonNull
@@ -916,6 +942,19 @@
0 /* lastUsed, filled by bpf prog only */);
}
+ @NonNull
+ private byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
+ final byte[] addr4 = ia4.getAddress();
+ final byte[] addr6 = new byte[16];
+ addr6[10] = (byte) 0xff;
+ addr6[11] = (byte) 0xff;
+ addr6[12] = addr4[0];
+ addr6[13] = addr4[1];
+ addr6[14] = addr4[2];
+ addr6[15] = addr4[3];
+ return addr6;
+ }
+
public void accept(ConntrackEvent e) {
final ClientInfo tetherClient = getClientInfo(e.tupleOrig.srcIp);
if (tetherClient == null) return;
@@ -929,7 +968,8 @@
if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
| NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
- // TODO: remove ingress and egress rules from BPF maps.
+ mBpfCoordinatorShim.tetherOffloadRuleRemove(upstream4Key);
+ mBpfCoordinatorShim.tetherOffloadRuleRemove(downstream4Key);
return;
}
@@ -938,7 +978,8 @@
final TetherDownstream4Value downstream4Value = makeTetherDownstream4Value(e,
tetherClient, upstreamIndex);
- // TODO: insert ingress and egress rules to BPF maps.
+ mBpfCoordinatorShim.tetherOffloadRuleAdd(upstream4Key, upstream4Value);
+ mBpfCoordinatorShim.tetherOffloadRuleAdd(downstream4Key, downstream4Value);
}
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index e20e011..9b42c73 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -104,12 +104,16 @@
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
+import com.android.networkstack.tethering.TetherDownstream4Key;
+import com.android.networkstack.tethering.TetherDownstream4Value;
import com.android.networkstack.tethering.TetherDownstream6Key;
import com.android.networkstack.tethering.TetherDownstream6Value;
import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherStatsKey;
import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.networkstack.tethering.TetherUpstream4Key;
+import com.android.networkstack.tethering.TetherUpstream4Value;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -173,6 +177,8 @@
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
+ @Mock private BpfMap<TetherDownstream4Key, TetherDownstream4Value> mBpfDownstream4Map;
+ @Mock private BpfMap<TetherUpstream4Key, TetherUpstream4Value> mBpfUpstream4Map;
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
@Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
@Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
@@ -303,6 +309,18 @@
}
@Nullable
+ public BpfMap<TetherDownstream4Key, TetherDownstream4Value>
+ getBpfDownstream4Map() {
+ return mBpfDownstream4Map;
+ }
+
+ @Nullable
+ public BpfMap<TetherUpstream4Key, TetherUpstream4Value>
+ getBpfUpstream4Map() {
+ return mBpfUpstream4Map;
+ }
+
+ @Nullable
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
getBpfDownstream6Map() {
return mBpfDownstream6Map;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 764e651..30b4bf4 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -158,6 +158,8 @@
@Mock private IpServer mIpServer2;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
+ @Mock private BpfMap<TetherDownstream4Key, TetherDownstream4Value> mBpfDownstream4Map;
+ @Mock private BpfMap<TetherUpstream4Key, TetherUpstream4Value> mBpfUpstream4Map;
@Mock private BpfMap<TetherDownstream6Key, TetherDownstream6Value> mBpfDownstream6Map;
// Late init since methods must be called by the thread that created this object.
@@ -203,6 +205,18 @@
}
@Nullable
+ public BpfMap<TetherDownstream4Key, TetherDownstream4Value>
+ getBpfDownstream4Map() {
+ return mBpfDownstream4Map;
+ }
+
+ @Nullable
+ public BpfMap<TetherUpstream4Key, TetherUpstream4Value>
+ getBpfUpstream4Map() {
+ return mBpfUpstream4Map;
+ }
+
+ @Nullable
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
getBpfDownstream6Map() {
return mBpfDownstream6Map;