[NFCT.TETHER.9] Build IPv4 offload BPF rules for raw ip
Build the upstream and upstream rules when the conntrack event is received. Test: atest TetheringCoverageTests Change-Id: Ibb52c7b75812bd586091d809e260bc9206c06262
This commit is contained in:
parent
8784a60eab
commit
347dd90620
5 changed files with 439 additions and 1 deletions
|
|
@ -23,7 +23,9 @@ import static android.net.NetworkStats.SET_DEFAULT;
|
|||
import static android.net.NetworkStats.TAG_NONE;
|
||||
import static android.net.NetworkStats.UID_ALL;
|
||||
import static android.net.NetworkStats.UID_TETHERING;
|
||||
import static android.net.ip.ConntrackMonitor.ConntrackEvent;
|
||||
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
|
||||
import static android.system.OsConstants.ETH_P_IP;
|
||||
import static android.system.OsConstants.ETH_P_IPV6;
|
||||
|
||||
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
|
||||
|
|
@ -38,6 +40,7 @@ import android.net.TetherOffloadRuleParcel;
|
|||
import android.net.ip.ConntrackMonitor;
|
||||
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
|
||||
import android.net.ip.IpServer;
|
||||
import android.net.netlink.NetlinkConstants;
|
||||
import android.net.netstats.provider.NetworkStatsProvider;
|
||||
import android.net.util.InterfaceParams;
|
||||
import android.net.util.SharedLog;
|
||||
|
|
@ -82,6 +85,8 @@ import java.util.Set;
|
|||
public class BpfCoordinator {
|
||||
private static final String TAG = BpfCoordinator.class.getSimpleName();
|
||||
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_DOWNSTREAM6_FS_PATH =
|
||||
"/sys/fs/bpf/tethering/map_offload_tether_downstream6_map";
|
||||
private static final String TETHER_STATS_MAP_PATH =
|
||||
|
|
@ -174,6 +179,9 @@ public class BpfCoordinator {
|
|||
// - Must only be modified by that IpServer.
|
||||
// - Is created when the IpServer adds its first client, and deleted when the IpServer deletes
|
||||
// its last client.
|
||||
// Note that relying on the client address for finding downstream is okay for now because the
|
||||
// client address is unique. See PrivateAddressCoordinator#requestDownstreamAddress.
|
||||
// TODO: Refactor if any possible that the client address is not unique.
|
||||
private final HashMap<IpServer, HashMap<Inet4Address, ClientInfo>>
|
||||
mTetherClients = new HashMap<>();
|
||||
|
||||
|
|
@ -854,8 +862,84 @@ public class BpfCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClientInfo getClientInfo(@NonNull Inet4Address clientAddress) {
|
||||
for (HashMap<Inet4Address, ClientInfo> clients : mTetherClients.values()) {
|
||||
for (ClientInfo client : clients.values()) {
|
||||
if (clientAddress.equals(client.clientAddress)) {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Support raw ip only.
|
||||
// TODO: add ether ip support.
|
||||
private class BpfConntrackEventConsumer implements ConntrackEventConsumer {
|
||||
public void accept(ConntrackMonitor.ConntrackEvent e) { /* TODO */ }
|
||||
@NonNull
|
||||
private TetherUpstream4Key makeTetherUpstream4Key(
|
||||
@NonNull ConntrackEvent e, @NonNull ClientInfo c) {
|
||||
return new TetherUpstream4Key(c.downstreamIfindex, c.downstreamMac,
|
||||
e.tupleOrig.protoNum, e.tupleOrig.srcIp.getAddress(),
|
||||
e.tupleOrig.dstIp.getAddress(), e.tupleOrig.srcPort, e.tupleOrig.dstPort);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TetherDownstream4Key makeTetherDownstream4Key(
|
||||
@NonNull ConntrackEvent e, @NonNull ClientInfo c, int upstreamIndex) {
|
||||
return new TetherDownstream4Key(upstreamIndex, NULL_MAC_ADDRESS /* dstMac (rawip) */,
|
||||
e.tupleReply.protoNum, e.tupleReply.srcIp.getAddress(),
|
||||
e.tupleReply.dstIp.getAddress(), e.tupleReply.srcPort, e.tupleReply.dstPort);
|
||||
}
|
||||
|
||||
@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 */);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TetherDownstream4Value makeTetherDownstream4Value(@NonNull ConntrackEvent e,
|
||||
@NonNull ClientInfo c, int upstreamIndex) {
|
||||
return new TetherDownstream4Value(c.downstreamIfindex,
|
||||
c.clientMac, c.downstreamMac, ETH_P_IP, NetworkStackConstants.ETHER_MTU,
|
||||
e.tupleOrig.dstIp.getAddress(), e.tupleOrig.srcIp.getAddress(),
|
||||
e.tupleOrig.dstPort, e.tupleOrig.srcPort,
|
||||
0 /* lastUsed, filled by bpf prog only */);
|
||||
}
|
||||
|
||||
public void accept(ConntrackEvent e) {
|
||||
final ClientInfo tetherClient = getClientInfo(e.tupleOrig.srcIp);
|
||||
if (tetherClient == null) return;
|
||||
|
||||
final Integer upstreamIndex = mIpv4UpstreamIndices.get(e.tupleReply.dstIp);
|
||||
if (upstreamIndex == null) return;
|
||||
|
||||
final TetherUpstream4Key upstream4Key = makeTetherUpstream4Key(e, tetherClient);
|
||||
final TetherDownstream4Key downstream4Key = makeTetherDownstream4Key(e,
|
||||
tetherClient, upstreamIndex);
|
||||
|
||||
if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
||||
| NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
|
||||
// TODO: remove ingress and egress rules from BPF maps.
|
||||
return;
|
||||
}
|
||||
|
||||
final TetherUpstream4Value upstream4Value = makeTetherUpstream4Value(e,
|
||||
upstreamIndex);
|
||||
final TetherDownstream4Value downstream4Value = makeTetherDownstream4Value(e,
|
||||
tetherClient, upstreamIndex);
|
||||
|
||||
// TODO: insert ingress and egress rules to BPF maps.
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBpfEnabled() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.networkstack.tethering;
|
||||
|
||||
import android.net.MacAddress;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
|
||||
/** The key of BpfMap which is used for IPv4 bpf offload. */
|
||||
public class TetherDownstream4Key extends Struct {
|
||||
@Field(order = 0, type = Type.U32)
|
||||
public final long iif;
|
||||
|
||||
@Field(order = 1, type = Type.EUI48)
|
||||
public final MacAddress dstMac;
|
||||
|
||||
@Field(order = 2, type = Type.U8, padding = 1)
|
||||
public final short l4proto;
|
||||
|
||||
@Field(order = 3, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] src4;
|
||||
|
||||
@Field(order = 4, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] dst4;
|
||||
|
||||
@Field(order = 5, type = Type.UBE16)
|
||||
public final int srcPort;
|
||||
|
||||
@Field(order = 6, type = Type.UBE16)
|
||||
public final int dstPort;
|
||||
|
||||
public TetherDownstream4Key(final long iif, final MacAddress dstMac, final short l4proto,
|
||||
final byte[] src4, final byte[] dst4, final int srcPort,
|
||||
final int dstPort) {
|
||||
Objects.requireNonNull(dstMac);
|
||||
|
||||
this.iif = iif;
|
||||
this.dstMac = dstMac;
|
||||
this.l4proto = l4proto;
|
||||
this.src4 = src4;
|
||||
this.dst4 = dst4;
|
||||
this.srcPort = srcPort;
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return String.format(
|
||||
"iif: %d, dstMac: %s, l4proto: %d, src4: %s, dst4: %s, "
|
||||
+ "srcPort: %d, dstPort: %d",
|
||||
iif, dstMac, l4proto,
|
||||
Inet4Address.getByAddress(src4), Inet4Address.getByAddress(dst4),
|
||||
Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort));
|
||||
} catch (UnknownHostException | IllegalArgumentException e) {
|
||||
return String.format("Invalid IP address", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.networkstack.tethering;
|
||||
|
||||
import android.net.MacAddress;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
|
||||
/** The value of BpfMap which is used for IPv4 bpf offload. */
|
||||
public class TetherDownstream4Value extends Struct {
|
||||
@Field(order = 0, type = Type.U32)
|
||||
public final long oif;
|
||||
|
||||
// The ethhdr struct which is defined in uapi/linux/if_ether.h
|
||||
@Field(order = 1, type = Type.EUI48)
|
||||
public final MacAddress ethDstMac;
|
||||
@Field(order = 2, type = Type.EUI48)
|
||||
public final MacAddress ethSrcMac;
|
||||
@Field(order = 3, type = Type.UBE16)
|
||||
public final int ethProto; // Packet type ID field.
|
||||
|
||||
@Field(order = 4, type = Type.U16)
|
||||
public final int pmtu;
|
||||
|
||||
@Field(order = 5, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] src4;
|
||||
|
||||
@Field(order = 6, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] dst4;
|
||||
|
||||
@Field(order = 7, type = Type.UBE16)
|
||||
public final int srcPort;
|
||||
|
||||
@Field(order = 8, type = Type.UBE16)
|
||||
public final int dstPort;
|
||||
|
||||
// TODO: consider using U64.
|
||||
@Field(order = 9, type = Type.U63)
|
||||
public final long lastUsed;
|
||||
|
||||
public TetherDownstream4Value(final long oif, @NonNull final MacAddress ethDstMac,
|
||||
@NonNull final MacAddress ethSrcMac, final int ethProto, final int pmtu,
|
||||
final byte[] src4, final byte[] dst4, final int srcPort,
|
||||
final int dstPort, final long lastUsed) {
|
||||
Objects.requireNonNull(ethDstMac);
|
||||
Objects.requireNonNull(ethSrcMac);
|
||||
|
||||
this.oif = oif;
|
||||
this.ethDstMac = ethDstMac;
|
||||
this.ethSrcMac = ethSrcMac;
|
||||
this.ethProto = ethProto;
|
||||
this.pmtu = pmtu;
|
||||
this.src4 = src4;
|
||||
this.dst4 = dst4;
|
||||
this.srcPort = srcPort;
|
||||
this.dstPort = dstPort;
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return String.format(
|
||||
"oif: %d, ethDstMac: %s, ethSrcMac: %s, ethProto: %d, pmtu: %d, "
|
||||
+ "src4: %s, dst4: %s, srcPort: %d, dstPort: %d, "
|
||||
+ "lastUsed: %d",
|
||||
oif, ethDstMac, ethSrcMac, ethProto, pmtu,
|
||||
InetAddress.getByAddress(src4), InetAddress.getByAddress(dst4),
|
||||
Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort),
|
||||
lastUsed);
|
||||
} catch (UnknownHostException | IllegalArgumentException e) {
|
||||
return String.format("Invalid IP address", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.networkstack.tethering;
|
||||
|
||||
import android.net.MacAddress;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
|
||||
/** The key of BpfMap which is used for IPv4 bpf offload. */
|
||||
public class TetherUpstream4Key extends Struct {
|
||||
@Field(order = 0, type = Type.U32)
|
||||
public final long iif;
|
||||
|
||||
@Field(order = 1, type = Type.EUI48)
|
||||
public final MacAddress dstMac;
|
||||
|
||||
@Field(order = 2, type = Type.U8, padding = 1)
|
||||
public final short l4proto;
|
||||
|
||||
@Field(order = 3, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] src4;
|
||||
|
||||
@Field(order = 4, type = Type.ByteArray, arraysize = 4)
|
||||
public final byte[] dst4;
|
||||
|
||||
@Field(order = 5, type = Type.UBE16)
|
||||
public final int srcPort;
|
||||
|
||||
@Field(order = 6, type = Type.UBE16)
|
||||
public final int dstPort;
|
||||
|
||||
public TetherUpstream4Key(final long iif, @NonNull final MacAddress dstMac, final short l4proto,
|
||||
final byte[] src4, final byte[] dst4, final int srcPort,
|
||||
final int dstPort) {
|
||||
Objects.requireNonNull(dstMac);
|
||||
|
||||
this.iif = iif;
|
||||
this.dstMac = dstMac;
|
||||
this.l4proto = l4proto;
|
||||
this.src4 = src4;
|
||||
this.dst4 = dst4;
|
||||
this.srcPort = srcPort;
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return String.format(
|
||||
"iif: %d, dstMac: %s, l4proto: %d, src4: %s, dst4: %s, "
|
||||
+ "srcPort: %d, dstPort: %d",
|
||||
iif, dstMac, l4proto,
|
||||
Inet4Address.getByAddress(src4), Inet4Address.getByAddress(dst4),
|
||||
Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort));
|
||||
} catch (UnknownHostException | IllegalArgumentException e) {
|
||||
return String.format("Invalid IP address", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.networkstack.tethering;
|
||||
|
||||
import android.net.MacAddress;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import com.android.net.module.util.Struct.Field;
|
||||
import com.android.net.module.util.Struct.Type;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
|
||||
/** The value of BpfMap which is used for IPv4 bpf offload. */
|
||||
public class TetherUpstream4Value extends Struct {
|
||||
@Field(order = 0, type = Type.U32)
|
||||
public final long oif;
|
||||
|
||||
// The ethhdr struct which is defined in uapi/linux/if_ether.h
|
||||
@Field(order = 1, type = Type.EUI48)
|
||||
public final MacAddress ethDstMac;
|
||||
@Field(order = 2, type = Type.EUI48)
|
||||
public final MacAddress ethSrcMac;
|
||||
@Field(order = 3, type = Type.UBE16)
|
||||
public final int ethProto; // Packet type ID field.
|
||||
|
||||
@Field(order = 4, type = Type.U16)
|
||||
public final int pmtu;
|
||||
|
||||
@Field(order = 5, type = Type.ByteArray, arraysize = 16)
|
||||
public final byte[] src46;
|
||||
|
||||
@Field(order = 6, type = Type.ByteArray, arraysize = 16)
|
||||
public final byte[] dst46;
|
||||
|
||||
@Field(order = 7, type = Type.UBE16)
|
||||
public final int srcPort;
|
||||
|
||||
@Field(order = 8, type = Type.UBE16)
|
||||
public final int dstPort;
|
||||
|
||||
// TODO: consider using U64.
|
||||
@Field(order = 9, type = Type.U63)
|
||||
public final long lastUsed;
|
||||
|
||||
public TetherUpstream4Value(final long oif, @NonNull final MacAddress ethDstMac,
|
||||
@NonNull final MacAddress ethSrcMac, final int ethProto, final int pmtu,
|
||||
final byte[] src46, final byte[] dst46, final int srcPort,
|
||||
final int dstPort, final long lastUsed) {
|
||||
Objects.requireNonNull(ethDstMac);
|
||||
Objects.requireNonNull(ethSrcMac);
|
||||
|
||||
this.oif = oif;
|
||||
this.ethDstMac = ethDstMac;
|
||||
this.ethSrcMac = ethSrcMac;
|
||||
this.ethProto = ethProto;
|
||||
this.pmtu = pmtu;
|
||||
this.src46 = src46;
|
||||
this.dst46 = dst46;
|
||||
this.srcPort = srcPort;
|
||||
this.dstPort = dstPort;
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return String.format(
|
||||
"oif: %d, ethDstMac: %s, ethSrcMac: %s, ethProto: %d, pmtu: %d, "
|
||||
+ "src46: %s, dst46: %s, srcPort: %d, dstPort: %d, "
|
||||
+ "lastUsed: %d",
|
||||
oif, ethDstMac, ethSrcMac, ethProto, pmtu,
|
||||
InetAddress.getByAddress(src46), InetAddress.getByAddress(dst46),
|
||||
Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort),
|
||||
lastUsed);
|
||||
} catch (UnknownHostException | IllegalArgumentException e) {
|
||||
return String.format("Invalid IP address", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue