[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
This commit is contained in:
parent
347dd90620
commit
dd833dee31
6 changed files with 228 additions and 11 deletions
|
|
@ -28,7 +28,11 @@ import androidx.annotation.Nullable;
|
|||
|
||||
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.
|
||||
|
|
@ -131,6 +135,32 @@ public class BpfCoordinatorShimImpl
|
|||
}
|
||||
}
|
||||
|
||||
@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";
|
||||
|
|
|
|||
|
|
@ -30,12 +30,16 @@ import androidx.annotation.Nullable;
|
|||
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 @@ public class BpfCoordinatorShimImpl
|
|||
@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 class BpfCoordinatorShimImpl
|
|||
|
||||
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 @@ public class BpfCoordinatorShimImpl
|
|||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return mBpfDownstream6Map != null && mBpfStatsMap != null && mBpfLimitMap != null;
|
||||
return mBpfDownstream4Map != null && mBpfUpstream4Map != null && mBpfDownstream6Map != null
|
||||
&& mBpfStatsMap != null && mBpfLimitMap != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -232,15 +249,86 @@ public class BpfCoordinatorShimImpl
|
|||
return statsValue;
|
||||
}
|
||||
|
||||
@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") + "} ";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ import androidx.annotation.Nullable;
|
|||
|
||||
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 @@ public abstract class BpfCoordinatorShim {
|
|||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ public class BpfCoordinator {
|
|||
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 @@ public class BpfCoordinator {
|
|||
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 @@ public class BpfCoordinator {
|
|||
@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 @@ public class BpfCoordinator {
|
|||
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 @@ public class BpfCoordinator {
|
|||
|
||||
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 @@ public class BpfCoordinator {
|
|||
final TetherDownstream4Value downstream4Value = makeTetherDownstream4Value(e,
|
||||
tetherClient, upstreamIndex);
|
||||
|
||||
// TODO: insert ingress and egress rules to BPF maps.
|
||||
mBpfCoordinatorShim.tetherOffloadRuleAdd(upstream4Key, upstream4Value);
|
||||
mBpfCoordinatorShim.tetherOffloadRuleAdd(downstream4Key, downstream4Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,12 +104,16 @@ import com.android.networkstack.tethering.BpfCoordinator;
|
|||
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 @@ public class IpServerTest {
|
|||
@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;
|
||||
|
|
@ -302,6 +308,18 @@ public class IpServerTest {
|
|||
return mConntrackMonitor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream4Key, TetherDownstream4Value>
|
||||
getBpfDownstream4Map() {
|
||||
return mBpfDownstream4Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherUpstream4Key, TetherUpstream4Value>
|
||||
getBpfUpstream4Map() {
|
||||
return mBpfUpstream4Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||
getBpfDownstream6Map() {
|
||||
|
|
|
|||
|
|
@ -158,6 +158,8 @@ public class BpfCoordinatorTest {
|
|||
@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.
|
||||
|
|
@ -202,6 +204,18 @@ public class BpfCoordinatorTest {
|
|||
return mConntrackMonitor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream4Key, TetherDownstream4Value>
|
||||
getBpfDownstream4Map() {
|
||||
return mBpfDownstream4Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherUpstream4Key, TetherUpstream4Value>
|
||||
getBpfUpstream4Map() {
|
||||
return mBpfUpstream4Map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BpfMap<TetherDownstream6Key, TetherDownstream6Value>
|
||||
getBpfDownstream6Map() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue