/* * Copyright 2021 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. * * main.c - main function */ #define LOG_TAG "bpfhelper" #include "libclat/bpfhelper.h" #include #include #include "bpf/BpfMap.h" #include "libclat/TcUtils.h" #define DEVICEPREFIX "v4-" using android::base::unique_fd; using android::net::RAWIP; using android::net::getClatEgress4MapFd; using android::net::getClatIngress6MapFd; using android::net::getClatEgress4ProgFd; using android::net::getClatIngress6ProgFd; using android::net::tcQdiscAddDevClsact; using android::net::tcFilterAddDevEgressClatIpv4; using android::net::tcFilterAddDevIngressClatIpv6; using android::net::tcFilterDelDevEgressClatIpv4; using android::net::tcFilterDelDevIngressClatIpv6; using android::bpf::BpfMap; BpfMap mClatEgress4Map; BpfMap mClatIngress6Map; namespace android { namespace net { namespace clat { // TODO: have a clearMap function to remove all stubs while system server crash. // For long term, move bpf access into java and map initialization should live // ClatCoordinator constructor. int initMaps(void) { int rv = getClatEgress4MapFd(); if (rv < 0) { ALOGE("getClatEgress4MapFd() failure: %s", strerror(-rv)); return -rv; } mClatEgress4Map.reset(rv); rv = getClatIngress6MapFd(); if (rv < 0) { ALOGE("getClatIngress6MapFd() failure: %s", strerror(-rv)); mClatEgress4Map.reset(-1); return -rv; } mClatIngress6Map.reset(rv); return 0; } void maybeStartBpf(const ClatdTracker& tracker) { auto isEthernet = android::net::isEthernet(tracker.iface); if (!isEthernet.ok()) { ALOGE("isEthernet(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex, isEthernet.error().message().c_str()); return; } // This program will be attached to the v4-* interface which is a TUN and thus always rawip. int rv = getClatEgress4ProgFd(RAWIP); if (rv < 0) { ALOGE("getClatEgress4ProgFd(RAWIP) failure: %s", strerror(-rv)); return; } unique_fd txRawIpProgFd(rv); rv = getClatIngress6ProgFd(isEthernet.value()); if (rv < 0) { ALOGE("getClatIngress6ProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv)); return; } unique_fd rxProgFd(rv); ClatEgress4Key txKey = { .iif = tracker.v4ifIndex, .local4 = tracker.v4, }; ClatEgress4Value txValue = { .oif = tracker.ifIndex, .local6 = tracker.v6, .pfx96 = tracker.pfx96, .oifIsEthernet = isEthernet.value(), }; auto ret = mClatEgress4Map.writeValue(txKey, txValue, BPF_ANY); if (!ret.ok()) { ALOGE("mClatEgress4Map.writeValue failure: %s", strerror(ret.error().code())); return; } ClatIngress6Key rxKey = { .iif = tracker.ifIndex, .pfx96 = tracker.pfx96, .local6 = tracker.v6, }; ClatIngress6Value rxValue = { // TODO: move all the clat code to eBPF and remove the tun interface entirely. .oif = tracker.v4ifIndex, .local4 = tracker.v4, }; ret = mClatIngress6Map.writeValue(rxKey, rxValue, BPF_ANY); if (!ret.ok()) { ALOGE("mClatIngress6Map.writeValue failure: %s", strerror(ret.error().code())); ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); return; } // We do tc setup *after* populating the maps, so scanning through them // can always be used to tell us what needs cleanup. // Usually the clsact will be added in RouteController::addInterfaceToPhysicalNetwork. // But clat is started before the v4- interface is added to the network. The clat startup have // to add clsact of v4- tun interface first for adding bpf filter in maybeStartBpf. // TODO: move "qdisc add clsact" of v4- tun interface out from ClatdController. rv = tcQdiscAddDevClsact(tracker.v4ifIndex); if (rv) { ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface, strerror(-rv)); ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } rv = tcFilterAddDevEgressClatIpv4(tracker.v4ifIndex, txRawIpProgFd, RAWIP); if (rv) { ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex, tracker.v4iface, strerror(-rv)); // The v4- interface clsact is not deleted for unwinding error because once it is created // with interface addition, the lifetime is till interface deletion. Moreover, the clsact // has no clat filter now. It should not break anything. ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } rv = tcFilterAddDevIngressClatIpv6(tracker.ifIndex, rxProgFd, isEthernet.value()); if (rv) { ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex, tracker.iface, isEthernet.value(), strerror(-rv)); rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex); if (rv) { ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface, strerror(-rv)); } // The v4- interface clsact is not deleted. See the reason in the error unwinding code of // the egress filter attaching of v4- tun interface. ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } // success } void maybeStopBpf(const ClatdTracker& tracker) { int rv = tcFilterDelDevIngressClatIpv6(tracker.ifIndex); if (rv < 0) { ALOGE("tcFilterDelDevIngressClatIpv6(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface, strerror(-rv)); } rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex); if (rv < 0) { ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface, strerror(-rv)); } // We cleanup the maps last, so scanning through them can be used to // determine what still needs cleanup. ClatEgress4Key txKey = { .iif = tracker.v4ifIndex, .local4 = tracker.v4, }; auto ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); ClatIngress6Key rxKey = { .iif = tracker.ifIndex, .pfx96 = tracker.pfx96, .local6 = tracker.v6, }; ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); } } // namespace clat } // namespace net } // namespace android