Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93885989 authored by Hungming Chen's avatar Hungming Chen
Browse files

Use device option to control BPF offload features

If BPF offload device config is not enabled:
- Does not add/remove offload forwarding rules through disabling IP
  neighbor monitor.
- Does not apply the RA MTU reduction.

Bug: 149997301
Test: atest IpServerTest
Change-Id: I2d6f80f0229f580c4b16243a064e889a6c37f77a
parent 8b42d7ce
Loading
Loading
Loading
Loading
+27 −7
Original line number Original line Diff line number Diff line
@@ -227,6 +227,7 @@ public class IpServer extends StateMachine {
    private final int mInterfaceType;
    private final int mInterfaceType;
    private final LinkProperties mLinkProperties;
    private final LinkProperties mLinkProperties;
    private final boolean mUsingLegacyDhcp;
    private final boolean mUsingLegacyDhcp;
    private final boolean mUsingBpfOffload;


    private final Dependencies mDeps;
    private final Dependencies mDeps;


@@ -304,7 +305,8 @@ public class IpServer extends StateMachine {


    public IpServer(
    public IpServer(
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
            INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
            Dependencies deps) {
        super(ifaceName, looper);
        super(ifaceName, looper);
        mLog = log.forSubComponent(ifaceName);
        mLog = log.forSubComponent(ifaceName);
        mNetd = netd;
        mNetd = netd;
@@ -314,6 +316,7 @@ public class IpServer extends StateMachine {
        mInterfaceType = interfaceType;
        mInterfaceType = interfaceType;
        mLinkProperties = new LinkProperties();
        mLinkProperties = new LinkProperties();
        mUsingLegacyDhcp = usingLegacyDhcp;
        mUsingLegacyDhcp = usingLegacyDhcp;
        mUsingBpfOffload = usingBpfOffload;
        mDeps = deps;
        mDeps = deps;
        resetLinkProperties();
        resetLinkProperties();
        mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
        mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -321,9 +324,16 @@ public class IpServer extends StateMachine {


        mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
        mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
                new MyNeighborEventConsumer());
                new MyNeighborEventConsumer());

        // IP neighbor monitor monitors the neighbor event for adding/removing offload
        // forwarding rules per client. If BPF offload is not supported, don't start listening
        // neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
        // removeIpv6ForwardingRule.
        if (mUsingBpfOffload) {
            if (!mIpNeighborMonitor.start()) {
            if (!mIpNeighborMonitor.start()) {
                mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
                mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
            }
            }
        }


        mInitialState = new InitialState();
        mInitialState = new InitialState();
        mLocalHotspotState = new LocalHotspotState();
        mLocalHotspotState = new LocalHotspotState();
@@ -715,12 +725,12 @@ public class IpServer extends StateMachine {
            final String upstreamIface = v6only.getInterfaceName();
            final String upstreamIface = v6only.getInterfaceName();


            params = new RaParams();
            params = new RaParams();
            // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14,
            // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
            // the ethernet header size.  This makes kernel ebpf tethering offload happy.
            // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
            // This hack should be reverted once we have the kernel fixed up.
            // offload happy. This hack should be reverted once we have the kernel fixed up.
            // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
            // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
            // see RouterAdvertisementDaemon.java putMtu()
            // see RouterAdvertisementDaemon.java putMtu()
            params.mtu = v6only.getMtu() - 16;
            params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
            params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
            params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();


            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
@@ -844,6 +854,11 @@ public class IpServer extends StateMachine {
    }
    }


    private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
    private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
        // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
        // offload is disabled. Add this check just in case.
        // TODO: Perhaps remove this protection check.
        if (!mUsingBpfOffload) return;

        try {
        try {
            mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
            mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
            mIpv6ForwardingRules.put(rule.address, rule);
            mIpv6ForwardingRules.put(rule.address, rule);
@@ -853,6 +868,11 @@ public class IpServer extends StateMachine {
    }
    }


    private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
    private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
        // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
        // offload is disabled. Add this check just in case.
        // TODO: Perhaps remove this protection check.
        if (!mUsingBpfOffload) return;

        try {
        try {
            mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
            mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
            if (removeFromMap) {
            if (removeFromMap) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -2289,7 +2289,7 @@ public class Tethering {
        final TetherState tetherState = new TetherState(
        final TetherState tetherState = new TetherState(
                new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                             makeControlCallback(), mConfig.enableLegacyDhcpServer,
                             makeControlCallback(), mConfig.enableLegacyDhcpServer,
                             mDeps.getIpServerDependencies()));
                             mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
        mTetherStates.put(iface, tetherState);
        mTetherStates.put(iface, tetherState);
        tetherState.ipServer.start();
        tetherState.ipServer.start();
    }
    }
+62 −9
Original line number Original line Diff line number Diff line
@@ -106,6 +106,7 @@ public class IpServerTest {
    private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
    private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
    private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
    private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
    private static final int DHCP_LEASE_TIME_SECS = 3600;
    private static final int DHCP_LEASE_TIME_SECS = 3600;
    private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;


    private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
    private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
            IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
            IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
@@ -130,10 +131,11 @@ public class IpServerTest {
    private NeighborEventConsumer mNeighborEventConsumer;
    private NeighborEventConsumer mNeighborEventConsumer;


    private void initStateMachine(int interfaceType) throws Exception {
    private void initStateMachine(int interfaceType) throws Exception {
        initStateMachine(interfaceType, false /* usingLegacyDhcp */);
        initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
    }
    }


    private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
    private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
            boolean usingBpfOffload) throws Exception {
        doAnswer(inv -> {
        doAnswer(inv -> {
            final IDhcpServerCallbacks cb = inv.getArgument(2);
            final IDhcpServerCallbacks cb = inv.getArgument(2);
            new Thread(() -> {
            new Thread(() -> {
@@ -165,7 +167,7 @@ public class IpServerTest {


        mIpServer = new IpServer(
        mIpServer = new IpServer(
                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
                mCallback, usingLegacyDhcp, mDependencies);
                mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
        mIpServer.start();
        mIpServer.start();
        mNeighborEventConsumer = neighborCaptor.getValue();
        mNeighborEventConsumer = neighborCaptor.getValue();


@@ -179,12 +181,13 @@ public class IpServerTest {


    private void initTetheredStateMachine(int interfaceType, String upstreamIface)
    private void initTetheredStateMachine(int interfaceType, String upstreamIface)
            throws Exception {
            throws Exception {
        initTetheredStateMachine(interfaceType, upstreamIface, false);
        initTetheredStateMachine(interfaceType, upstreamIface, false,
                DEFAULT_USING_BPF_OFFLOAD);
    }
    }


    private void initTetheredStateMachine(int interfaceType, String upstreamIface,
    private void initTetheredStateMachine(int interfaceType, String upstreamIface,
            boolean usingLegacyDhcp) throws Exception {
            boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception {
        initStateMachine(interfaceType, usingLegacyDhcp);
        initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
        if (upstreamIface != null) {
        if (upstreamIface != null) {
            LinkProperties lp = new LinkProperties();
            LinkProperties lp = new LinkProperties();
@@ -204,7 +207,8 @@ public class IpServerTest {
        when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
        when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
                .thenReturn(mIpNeighborMonitor);
                .thenReturn(mIpNeighborMonitor);
        mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
        mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
                mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
                mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
                mDependencies);
        mIpServer.start();
        mIpServer.start();
        mLooper.dispatchAll();
        mLooper.dispatchAll();
        verify(mCallback).updateInterfaceState(
        verify(mCallback).updateInterfaceState(
@@ -494,7 +498,8 @@ public class IpServerTest {


    @Test
    @Test
    public void doesNotStartDhcpServerIfDisabled() throws Exception {
    public void doesNotStartDhcpServerIfDisabled() throws Exception {
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
                DEFAULT_USING_BPF_OFFLOAD);
        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
        dispatchTetherConnectionChanged(UPSTREAM_IFACE);


        verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
        verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
@@ -577,7 +582,8 @@ public class IpServerTest {


    @Test
    @Test
    public void addRemoveipv6ForwardingRules() throws Exception {
    public void addRemoveipv6ForwardingRules() throws Exception {
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
                DEFAULT_USING_BPF_OFFLOAD);


        final int myIfindex = TEST_IFACE_PARAMS.index;
        final int myIfindex = TEST_IFACE_PARAMS.index;
        final int notMyIfindex = myIfindex - 1;
        final int notMyIfindex = myIfindex - 1;
@@ -678,6 +684,53 @@ public class IpServerTest {
        reset(mNetd);
        reset(mNetd);
    }
    }


    @Test
    public void enableDisableUsingBpfOffload() throws Exception {
        final int myIfindex = TEST_IFACE_PARAMS.index;
        final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
        final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
        final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");

        reset(mNetd);

        // Expect that rules can be only added/removed when the BPF offload config is enabled.
        // Note that the usingBpfOffload false case is not a realistic test case. Because IP
        // neighbor monitor doesn't start if BPF offload is disabled, there should have no
        // neighbor event listening. This is used for testing the protection check just in case.
        // TODO: Perhaps remove this test once we don't need this check anymore.
        for (boolean usingBpfOffload : new boolean[]{true, false}) {
            initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
                    usingBpfOffload);

            // A neighbor is added.
            recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
            if (usingBpfOffload) {
                verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
            } else {
                verify(mNetd, never()).tetherOffloadRuleAdd(any());
            }
            reset(mNetd);

            // A neighbor is deleted.
            recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
            if (usingBpfOffload) {
                verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
            } else {
                verify(mNetd, never()).tetherOffloadRuleRemove(any());
            }
            reset(mNetd);
        }
    }

    @Test
    public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
                false /* usingBpfOffload */);

        // IP neighbor monitor doesn't start if BPF offload is disabled.
        verify(mIpNeighborMonitor, never()).start();
    }

    private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
    private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
        verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
        verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(