diff options
author | Bruno Martins <bgcngm@gmail.com> | 2018-01-08 12:51:07 +0000 |
---|---|---|
committer | Davide Garberi <dade.garberi@gmail.com> | 2018-01-20 09:30:21 +0100 |
commit | 1c67f47d4a205e12650dcb6255c00bafc0046082 (patch) | |
tree | 72bb48ca2152cb2295b0f8de39497490d8616f6f /data-ipa-cfg-mgr/hal/src | |
parent | 6b0c862328f58e523e2a6efa667a2f4b3a609bf3 (diff) |
msm8996-common: Import IPACM back
* QC package: LA.UM.6.5.r1-05300-8x96.0
* Squashed with commits 08e2a5b9b, 055b183 and 8fcf57b
Change-Id: I2f7a792dc0155986e065d1bf79e1f08370c3d79c
Signed-off-by: Davide Garberi <dade.garberi@gmail.com>
Diffstat (limited to 'data-ipa-cfg-mgr/hal/src')
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/CtUpdateAmbassador.cpp | 123 | ||||
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/HAL.cpp | 612 | ||||
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/IpaEventRelay.cpp | 83 | ||||
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/LocalLogBuffer.cpp | 126 | ||||
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/OffloadStatistics.cpp | 57 | ||||
-rw-r--r-- | data-ipa-cfg-mgr/hal/src/PrefixParser.cpp | 391 |
6 files changed, 1392 insertions, 0 deletions
diff --git a/data-ipa-cfg-mgr/hal/src/CtUpdateAmbassador.cpp b/data-ipa-cfg-mgr/hal/src/CtUpdateAmbassador.cpp new file mode 100644 index 0000000..4843fe2 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/CtUpdateAmbassador.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DBG + #define DBG false +#endif /* DBG */ +#define LOG_TAG "IPAHALService/CtUpdateAmbassador" + +/* External Includes */ +#include <arpa/inet.h> +#include <cutils/log.h> + +/* HIDL Includes */ +#include <android/hardware/tetheroffload/control/1.0/ITetheringOffloadCallback.h> + +/* Internal Includes */ +#include "CtUpdateAmbassador.h" + +/* Namespace pollution avoidance */ +using ::android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback; +using ::android::hardware::tetheroffload::control::V1_0::NetworkProtocol; +using HALIpAddrPortPair = ::android::hardware::tetheroffload::control::V1_0::IPv4AddrPortPair; +using HALNatTimeoutUpdate = ::android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate; + +using IpaIpAddrPortPair = ::IOffloadManager::ConntrackTimeoutUpdater::IpAddrPortPair; +using IpaNatTimeoutUpdate = ::IOffloadManager::ConntrackTimeoutUpdater::NatTimeoutUpdate; +using IpaL4Protocol = ::IOffloadManager::ConntrackTimeoutUpdater::L4Protocol; + + +CtUpdateAmbassador::CtUpdateAmbassador( + const ::android::sp<ITetheringOffloadCallback>& cb) : mFramework(cb) { +} /* CtUpdateAmbassador */ + +void CtUpdateAmbassador::updateTimeout(IpaNatTimeoutUpdate in) { + if (DBG) { + ALOGD("updateTimeout(src={%#010X, %#04X}, dest={%#010X, %#04X}, Proto=%d)", + in.src.ipAddr, in.src.port, in.dst.ipAddr, in.dst.port, + in.proto); + } + HALNatTimeoutUpdate out; + if (!translate(in, out)) { + /* Cannot log the input outside of DBG flag because it contains sensitive + * information. This will lead to a two step debug if the information + * cannot be gleaned from IPACM logs. The other option is to improve this + * with the use of our local log. That would likely still be hard to + * instruct testers to collect logs, because, assuming timeout updates + * are numerous, it will overrun the ring quickly. Therefore, the tester + * would have to know the exact moment as issue occurred. Or we make the + * ring massive. This would lead to a significant memory overhead. + * Because of this overhead, we would likely not want to check in a change + * with it and once we provide a debug build for increasing buffer size, + * why not just define the DBG flag? + */ + ALOGE("Failed to translate timeout event :("); + } else { + mFramework->updateTimeout(out); + } +} /* updateTimeout */ + +bool CtUpdateAmbassador::translate(IpaNatTimeoutUpdate in, HALNatTimeoutUpdate &out) { + return translate(in.src, out.src) + && translate(in.dst, out.dst) + && L4ToNetwork(in.proto, out.proto); +} /* translate */ + +bool CtUpdateAmbassador::translate(IpaIpAddrPortPair in, HALIpAddrPortPair& out) { + char ipAddrStr[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, &(in.ipAddr), ipAddrStr, INET_ADDRSTRLEN) == nullptr) { + /* errno would be valid here with EAFNOSUPPORT or ENOSPC, neither should really + * be possible in our scenario though. + */ + return false; + } + + out.addr = ipAddrStr; + out.port = in.port; + + return true; +} /* translate */ + +bool CtUpdateAmbassador::L4ToNetwork(IpaL4Protocol in, NetworkProtocol &out) { + bool ret = false; + switch(in) { + case IpaL4Protocol::TCP: + ret = true; + out = NetworkProtocol::TCP; + break; + case IpaL4Protocol::UDP: + ret = true; + out = NetworkProtocol::UDP; + break; + default: + ret = false; + break; + } + return ret; +} /* L4ToNetwork */ diff --git a/data-ipa-cfg-mgr/hal/src/HAL.cpp b/data-ipa-cfg-mgr/hal/src/HAL.cpp new file mode 100644 index 0000000..002bbb4 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/HAL.cpp @@ -0,0 +1,612 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DBG + #define DBG true +#endif /* DBG */ +#define LOG_TAG "IPAHALService" + +/* HIDL Includes */ +#include <hwbinder/IPCThreadState.h> +#include <hwbinder/ProcessState.h> + +/* Kernel Includes */ +#include <linux/netfilter/nfnetlink_compat.h> + +/* External Includes */ +#include <cutils/log.h> +#include <cstring> +#include <sys/socket.h> +#include <sys/types.h> +#include <vector> + +/* Internal Includes */ +#include "HAL.h" +#include "LocalLogBuffer.h" +#include "PrefixParser.h" + +/* Namespace pollution avoidance */ +using ::android::hardware::Void; +using ::android::status_t; + +using RET = ::IOffloadManager::RET; +using Prefix = ::IOffloadManager::Prefix; + +using ::std::map; +using ::std::vector; + + +/* ------------------------------ PUBLIC ------------------------------------ */ +HAL* HAL::makeIPAHAL(int version, IOffloadManager* mgr) { + if (DBG) + ALOGI("makeIPAHAL(%d, %s)", version, + (mgr != nullptr) ? "provided" : "null"); + if (nullptr == mgr) return NULL; + else if (version != 1) return NULL; + HAL* ret = new HAL(mgr); + if (nullptr == ret) return NULL; + configureRpcThreadpool(1, false); + ret->registerAsSystemService("ipacm"); + return ret; +} /* makeIPAHAL */ + + +/* ------------------------------ PRIVATE ----------------------------------- */ +HAL::HAL(IOffloadManager* mgr) : mLogs("HAL Function Calls", 50) { + mIPA = mgr; + mCb.clear(); + mCbIpa = nullptr; + mCbCt = nullptr; +} /* HAL */ + +void HAL::registerAsSystemService(const char* name) { + status_t ret = 0; + + ret = IOffloadControl::registerAsService(); + if (ret != 0) ALOGE("Failed to register IOffloadControl (%d)", ret); + else if (DBG) { + ALOGI("Successfully registered IOffloadControl"); + } + + ret = IOffloadConfig::registerAsService(); + if (ret != 0) ALOGE("Failed to register IOffloadConfig (%d)", ret); + else if (DBG) { + ALOGI("Successfully registered IOffloadConfig"); + } +} /* registerAsSystemService */ + +void HAL::doLogcatDump() { + ALOGD("mHandles"); + ALOGD("========"); + /* @TODO This will segfault if they aren't initialized and I don't currently + * care to check for initialization in a function that isn't used anyways + * ALOGD("fd1->%d", mHandle1->data[0]); + * ALOGD("fd2->%d", mHandle2->data[0]); + */ + ALOGD("========"); +} /* doLogcatDump */ + +HAL::BoolResult HAL::makeInputCheckFailure(string customErr) { + BoolResult ret; + ret.success = false; + ret.errMsg = "Failed Input Checks: " + customErr; + return ret; +} /* makeInputCheckFailure */ + +HAL::BoolResult HAL::ipaResultToBoolResult(RET in) { + BoolResult ret; + ret.success = (in >= RET::SUCCESS); + switch (in) { + case RET::FAIL_TOO_MANY_PREFIXES: + ret.errMsg = "Too Many Prefixes Provided"; + break; + case RET::FAIL_UNSUPPORTED: + ret.errMsg = "Unsupported by Hardware"; + break; + case RET::FAIL_INPUT_CHECK: + ret.errMsg = "Failed Input Checks"; + break; + case RET::FAIL_HARDWARE: + ret.errMsg = "Hardware did not accept"; + break; + case RET::FAIL_TRY_AGAIN: + ret.errMsg = "Try Again"; + break; + case RET::SUCCESS: + ret.errMsg = "Successful"; + break; + case RET::SUCCESS_DUPLICATE_CONFIG: + ret.errMsg = "Successful: Was a duplicate configuration"; + break; + case RET::SUCCESS_NO_OP: + ret.errMsg = "Successful: No action needed"; + break; + case RET::SUCCESS_OPTIMIZED: + ret.errMsg = "Successful: Performed optimized version of action"; + break; + default: + ret.errMsg = "Unknown Error"; + break; + } + return ret; +} /* ipaResultToBoolResult */ + +/* This will likely always result in doubling the number of loops the execution + * goes through. Obviously that is suboptimal. But if we first translate + * away from all HIDL specific code, then we can avoid sprinkling HIDL + * dependencies everywhere. + */ +vector<string> HAL::convertHidlStrToStdStr(hidl_vec<hidl_string> in) { + vector<string> ret; + for (size_t i = 0; i < in.size(); i++) { + string add = in[i]; + ret.push_back(add); + } + return ret; +} /* convertHidlStrToStdStr */ + +void HAL::registerEventListeners() { + registerIpaCb(); + registerCtCb(); +} /* registerEventListeners */ + +void HAL::registerIpaCb() { + if (isInitialized() && mCbIpa == nullptr) { + LocalLogBuffer::FunctionLog fl("registerEventListener"); + mCbIpa = new IpaEventRelay(mCb); + mIPA->registerEventListener(mCbIpa); + mLogs.addLog(fl); + } else { + ALOGE("Failed to registerIpaCb (isInitialized()=%s, (mCbIpa == nullptr)=%s)", + isInitialized() ? "true" : "false", + (mCbIpa == nullptr) ? "true" : "false"); + } +} /* registerIpaCb */ + +void HAL::registerCtCb() { + if (isInitialized() && mCbCt == nullptr) { + LocalLogBuffer::FunctionLog fl("registerCtTimeoutUpdater"); + mCbCt = new CtUpdateAmbassador(mCb); + mIPA->registerCtTimeoutUpdater(mCbCt); + mLogs.addLog(fl); + } else { + ALOGE("Failed to registerCtCb (isInitialized()=%s, (mCbCt == nullptr)=%s)", + isInitialized() ? "true" : "false", + (mCbCt == nullptr) ? "true" : "false"); + } +} /* registerCtCb */ + +void HAL::unregisterEventListeners() { + unregisterIpaCb(); + unregisterCtCb(); +} /* unregisterEventListeners */ + +void HAL::unregisterIpaCb() { + if (mCbIpa != nullptr) { + LocalLogBuffer::FunctionLog fl("unregisterEventListener"); + mIPA->unregisterEventListener(mCbIpa); + mCbIpa = nullptr; + mLogs.addLog(fl); + } else { + ALOGE("Failed to unregisterIpaCb"); + } +} /* unregisterIpaCb */ + +void HAL::unregisterCtCb() { + if (mCbCt != nullptr) { + LocalLogBuffer::FunctionLog fl("unregisterCtTimeoutUpdater"); + mIPA->unregisterCtTimeoutUpdater(mCbCt); + mCbCt = nullptr; + mLogs.addLog(fl); + } else { + ALOGE("Failed to unregisterCtCb"); + } +} /* unregisterCtCb */ + +void HAL::clearHandles() { + ALOGI("clearHandles()"); + /* @TODO handle this more gracefully... also remove the log + * + * Things that would be nice, but I can't do: + * [1] Destroy the object (it's on the stack) + * [2] Call freeHandle (it's private) + * + * Things I can do but are hacks: + * [1] Look at code and notice that setTo immediately calls freeHandle + */ + mHandle1.setTo(nullptr, true); + mHandle2.setTo(nullptr, true); +} /* clearHandles */ + +bool HAL::isInitialized() { + return mCb.get() != nullptr; +} /* isInitialized */ + + +/* -------------------------- IOffloadConfig -------------------------------- */ +Return<void> HAL::setHandles( + const hidl_handle &fd1, + const hidl_handle &fd2, + setHandles_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + + if (fd1->numFds != 1) { + BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd1)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + + mLogs.addLog(fl); + return Void(); + } + + if (fd2->numFds != 1) { + BoolResult res = makeInputCheckFailure("Must provide exactly one FD per handle (fd2)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + + mLogs.addLog(fl); + return Void(); + } + + /* The = operator calls freeHandle internally. Therefore, if we were using + * these handles previously, they're now gone... forever. But hopefully the + * new ones kick in very quickly. + * + * After freeing anything previously held, it will dup the FD so we have our + * own copy. + */ + mHandle1 = fd1; + mHandle2 = fd2; + + /* Log the DUPed FD instead of the actual input FD so that we can lookup + * this value in ls -l /proc/<pid>/<fd> + */ + fl.addArg("fd1", mHandle1->data[0]); + fl.addArg("fd2", mHandle2->data[0]); + + /* Try to provide each handle to IPACM. Destroy our DUPed hidl_handles if + * IPACM does not like either input. This keeps us from leaking FDs or + * providing half solutions. + * + * @TODO unfortunately, this does not cover duplicate configs where IPACM + * thinks it is still holding on to a handle that we would have freed above. + * It also probably means that IPACM would not know about the first FD being + * freed if it rejects the second FD. + */ + RET ipaReturn = mIPA->provideFd(mHandle1->data[0], UDP_SUBSCRIPTIONS); + if (ipaReturn == RET::SUCCESS) { + ipaReturn = mIPA->provideFd(mHandle2->data[0], TCP_SUBSCRIPTIONS); + } + + if (ipaReturn != RET::SUCCESS) { + ALOGE("IPACM failed to accept the FDs (%d %d)", mHandle1->data[0], + mHandle2->data[0]); + clearHandles(); + } else { + /* @TODO remove logs after stabilization */ + ALOGI("IPACM was provided two FDs (%d, %d)", mHandle1->data[0], + mHandle2->data[0]); + } + + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + return Void(); +} /* setHandles */ + + +/* -------------------------- IOffloadControl ------------------------------- */ +Return<void> HAL::initOffload +( + const ::android::sp<ITetheringOffloadCallback>& cb, + initOffload_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + + if (isInitialized()) { + BoolResult res = makeInputCheckFailure("Already initialized"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + } else { + /* Should storing the CB be a function? */ + mCb = cb; + registerEventListeners(); + BoolResult res = ipaResultToBoolResult(RET::SUCCESS); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + } + + return Void(); +} /* initOffload */ + +Return<void> HAL::stopOffload +( + stopOffload_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Was never initialized"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + } else { + /* Should removing the CB be a function? */ + mCb.clear(); + unregisterEventListeners(); + + RET ipaReturn = mIPA->stopAllOffload(); + if (ipaReturn != RET::SUCCESS) { + /* Ignore IPAs return value here and provide why stopAllOffload + * failed. However, if IPA failed to clearAllFds, then we can't + * clear our map because they may still be in use. + */ + RET ret = mIPA->clearAllFds(); + if (ret == RET::SUCCESS) { + clearHandles(); + } + } else { + ipaReturn = mIPA->clearAllFds(); + /* If IPA fails, they may still be using these for some reason. */ + if (ipaReturn == RET::SUCCESS) { + clearHandles(); + } else { + ALOGE("IPACM failed to return success for clearAllFds so they will not be released..."); + } + } + + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + } + + return Void(); +} /* stopOffload */ + +Return<void> HAL::setLocalPrefixes +( + const hidl_vec<hidl_string>& prefixes, + setLocalPrefixes_cb hidl_cb +) { + BoolResult res; + PrefixParser parser; + vector<string> prefixesStr = convertHidlStrToStdStr(prefixes); + + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("prefixes", prefixesStr); + + memset(&res,0,sizeof(BoolResult)); + + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Not initialized"); + } else if(prefixesStr.size() < 1) { + res = ipaResultToBoolResult(RET::FAIL_INPUT_CHECK); + } else if (!parser.add(prefixesStr)) { + res = makeInputCheckFailure(parser.getLastErrAsStr()); + } else { + res = ipaResultToBoolResult(RET::SUCCESS); + } + + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + mLogs.addLog(fl); + return Void(); +} /* setLocalPrefixes */ + +Return<void> HAL::getForwardedStats +( + const hidl_string& upstream, + getForwardedStats_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("upstream", upstream); + + OffloadStatistics ret; + RET ipaReturn = mIPA->getStats(upstream.c_str(), true, ret); + if (ipaReturn == RET::SUCCESS) { + hidl_cb(ret.getTotalRxBytes(), ret.getTotalTxBytes()); + fl.setResult(ret.getTotalRxBytes(), ret.getTotalTxBytes()); + } else { + /* @TODO Ensure the output is zeroed, but this is probably not enough to + * tell Framework that an error has occurred. If, for example, they had + * not yet polled for statistics previously, they may incorrectly assume + * that simply no statistics have transpired on hardware path. + * + * Maybe ITetheringOffloadCallback:onEvent(OFFLOAD_STOPPED_ERROR) is + * enough to handle this case, time will tell. + */ + hidl_cb(0, 0); + fl.setResult(0, 0); + } + + mLogs.addLog(fl); + return Void(); +} /* getForwardedStats */ + +Return<void> HAL::setDataLimit +( + const hidl_string& upstream, + uint64_t limit, + setDataLimit_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("upstream", upstream); + fl.addArg("limit", limit); + + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Not initialized (setDataLimit)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else { + RET ipaReturn = mIPA->setQuota(upstream.c_str(), limit); + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + + mLogs.addLog(fl); + return Void(); +} /* setDataLimit */ + +Return<void> HAL::setUpstreamParameters +( + const hidl_string& iface, + const hidl_string& v4Addr, + const hidl_string& v4Gw, + const hidl_vec<hidl_string>& v6Gws, + setUpstreamParameters_cb hidl_cb +) { + vector<string> v6GwStrs = convertHidlStrToStdStr(v6Gws); + + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("iface", iface); + fl.addArg("v4Addr", v4Addr); + fl.addArg("v4Gw", v4Gw); + fl.addArg("v6Gws", v6GwStrs); + + PrefixParser v4AddrParser; + PrefixParser v4GwParser; + PrefixParser v6GwParser; + + /* @TODO maybe we should enforce that these addresses and gateways are fully + * qualified here. But then, how do we allow them to be empty/null as well + * while still preserving a sane API on PrefixParser? + */ + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else if (!v4AddrParser.addV4(v4Addr) && !v4Addr.empty()) { + BoolResult res = makeInputCheckFailure(v4AddrParser.getLastErrAsStr()); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else if (!v4GwParser.addV4(v4Gw) && !v4Gw.empty()) { + BoolResult res = makeInputCheckFailure(v4GwParser.getLastErrAsStr()); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else if (v6GwStrs.size() >= 1 && !v6GwParser.addV6(v6GwStrs)) { + BoolResult res = makeInputCheckFailure(v6GwParser.getLastErrAsStr()); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else if (iface.size()>= 1) { + RET ipaReturn = mIPA->setUpstream( + iface.c_str(), + v4GwParser.getFirstPrefix(), + v6GwParser.getFirstPrefix()); + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else { + /* send NULL iface string when upstream down */ + RET ipaReturn = mIPA->setUpstream( + NULL, + v4GwParser.getFirstPrefix(IP_FAM::V4), + v6GwParser.getFirstPrefix(IP_FAM::V6)); + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + + mLogs.addLog(fl); + return Void(); +} /* setUpstreamParameters */ + +Return<void> HAL::addDownstream +( + const hidl_string& iface, + const hidl_string& prefix, + addDownstream_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("iface", iface); + fl.addArg("prefix", prefix); + + PrefixParser prefixParser; + + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + else if (!prefixParser.add(prefix)) { + BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr()); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else { + RET ipaReturn = mIPA->addDownstream( + iface.c_str(), + prefixParser.getFirstPrefix()); + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + + mLogs.addLog(fl); + return Void(); +} /* addDownstream */ + +Return<void> HAL::removeDownstream +( + const hidl_string& iface, + const hidl_string& prefix, + removeDownstream_cb hidl_cb +) { + LocalLogBuffer::FunctionLog fl(__func__); + fl.addArg("iface", iface); + fl.addArg("prefix", prefix); + + PrefixParser prefixParser; + + if (!isInitialized()) { + BoolResult res = makeInputCheckFailure("Not initialized (setUpstreamParameters)"); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + else if (!prefixParser.add(prefix)) { + BoolResult res = makeInputCheckFailure(prefixParser.getLastErrAsStr()); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } else { + RET ipaReturn = mIPA->removeDownstream( + iface.c_str(), + prefixParser.getFirstPrefix()); + BoolResult res = ipaResultToBoolResult(ipaReturn); + hidl_cb(res.success, res.errMsg); + fl.setResult(res.success, res.errMsg); + } + + mLogs.addLog(fl); + return Void(); +} /* removeDownstream */ diff --git a/data-ipa-cfg-mgr/hal/src/IpaEventRelay.cpp b/data-ipa-cfg-mgr/hal/src/IpaEventRelay.cpp new file mode 100644 index 0000000..788b152 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/IpaEventRelay.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define LOG_TAG "IPAHALService/IpaEventRelay" +/* External Includes */ +#include <cutils/log.h> + +/* HIDL Includes */ +#include <android/hardware/tetheroffload/control/1.0/ITetheringOffloadCallback.h> + +/* Internal Includes */ +#include "IpaEventRelay.h" + +/* Namespace pollution avoidance */ +using ::android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback; +using ::android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent; + + +IpaEventRelay::IpaEventRelay( + const ::android::sp<ITetheringOffloadCallback>& cb) : mFramework(cb) { +} /* IpaEventRelay */ + +void IpaEventRelay::onOffloadStarted() { + ALOGI("onOffloadStarted()"); + mFramework->onEvent(OffloadCallbackEvent::OFFLOAD_STARTED); +} /* onOffloadStarted */ + +void IpaEventRelay::onOffloadStopped(StoppedReason reason) { + ALOGI("onOffloadStopped(%d)", reason); + switch (reason) { + case REQUESTED: + /* + * No way to communicate this to Framework right now, they make an + * assumption that offload is stopped when they remove the + * configuration. + */ + break; + case ERROR: + mFramework->onEvent(OffloadCallbackEvent::OFFLOAD_STOPPED_ERROR); + break; + case UNSUPPORTED: + mFramework->onEvent(OffloadCallbackEvent::OFFLOAD_STOPPED_UNSUPPORTED); + break; + default: + ALOGE("Unknown stopped reason(%d)", reason); + break; + } +} /* onOffloadStopped */ + +void IpaEventRelay::onOffloadSupportAvailable() { + ALOGI("onOffloadSupportAvailable()"); + mFramework->onEvent(OffloadCallbackEvent::OFFLOAD_SUPPORT_AVAILABLE); +} /* onOffloadSupportAvailable */ + +void IpaEventRelay::onLimitReached() { + ALOGI("onLimitReached()"); + mFramework->onEvent(OffloadCallbackEvent::OFFLOAD_STOPPED_LIMIT_REACHED); +} /* onLimitReached */ diff --git a/data-ipa-cfg-mgr/hal/src/LocalLogBuffer.cpp b/data-ipa-cfg-mgr/hal/src/LocalLogBuffer.cpp new file mode 100644 index 0000000..f556e40 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/LocalLogBuffer.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define LOG_TAG "IPAHALService/dump" + +/* External Includes */ +#include <cutils/log.h> +#include <deque> +#include <string> +#include <sys/types.h> +#include <vector> + +/* Internal Includes */ +#include "LocalLogBuffer.h" + +/* Namespace pollution avoidance */ +using ::std::deque; +using ::std::string; +using ::std::vector; + + +LocalLogBuffer::FunctionLog::FunctionLog(string funcName) : mName(funcName) { + mArgsProvided = false; +} /* FunctionLog */ + +LocalLogBuffer::FunctionLog::FunctionLog(const FunctionLog& other) : + mName(other.mName) { + mArgsProvided = other.mArgsProvided; + /* Is this right? How do you copy stringstreams without wizardry? */ + mSSArgs.str(other.mSSArgs.str()); + mSSReturn.str(other.mSSReturn.str()); +} /* FunctionLog */ + +void LocalLogBuffer::FunctionLog::addArg(string kw, string arg) { + maybeAddArgsComma(); + mSSArgs << kw << "=" << arg; +} /* addArg */ + +void LocalLogBuffer::FunctionLog::addArg(string kw, vector<string> args) { + maybeAddArgsComma(); + mSSArgs << kw << "=["; + for (size_t i = 0; i < args.size(); i++) { + mSSArgs << args[i]; + if (i < (args.size() - 1)) + mSSArgs << ", "; + } + mSSArgs << "]"; +} /* addArg */ + +void LocalLogBuffer::FunctionLog::addArg(string kw, uint64_t arg) { + maybeAddArgsComma(); + mSSArgs << kw << "=" << arg; +} /* addArg */ + +void LocalLogBuffer::FunctionLog::maybeAddArgsComma() { + if (!mArgsProvided) { + mArgsProvided = true; + } else { + mSSArgs << ", "; + } +} /* maybeAddArgsComma */ + +void LocalLogBuffer::FunctionLog::setResult(bool success, string msg) { + mSSReturn << "[" << ((success) ? "success" : "failure") << ", " << msg + << "]"; +} /* setResult */ + +void LocalLogBuffer::FunctionLog::setResult(vector<unsigned int> ret) { + mSSReturn << "["; + for (size_t i = 0; i < ret.size(); i++) { + mSSReturn << ret[i]; + if (i < (ret.size() - 1)) + mSSReturn << ", "; + } + mSSReturn << "]"; +} /* setResult */ + +void LocalLogBuffer::FunctionLog::setResult(uint64_t rx, uint64_t tx) { + mSSReturn << "[rx=" << rx << ", tx=" << tx << "]"; +} /* setResult */ + +string LocalLogBuffer::FunctionLog::toString() { + stringstream ret; + ret << mName << "(" << mSSArgs.str() << ") returned " << mSSReturn.str(); + return ret.str(); +} /* toString */ + +LocalLogBuffer::LocalLogBuffer(string name, int maxLogs) : mName(name), + mMaxLogs(maxLogs) { +} /* LocalLogBuffer */ + +void LocalLogBuffer::addLog(FunctionLog log) { + while (mLogs.size() > mMaxLogs) + mLogs.pop_front(); + mLogs.push_back(log); +} /* addLog */ + +void LocalLogBuffer::toLogcat() { + for (size_t i = 0; i < mLogs.size(); i++) + ALOGD("%s: %s", mName.c_str(), mLogs[i].toString().c_str()); +} /* toLogcat */ diff --git a/data-ipa-cfg-mgr/hal/src/OffloadStatistics.cpp b/data-ipa-cfg-mgr/hal/src/OffloadStatistics.cpp new file mode 100644 index 0000000..8f8beb6 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/OffloadStatistics.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <string.h> +#include <sys/types.h> + +#include "OffloadStatistics.h" + + +/* ------------------------------ PUBLIC ------------------------------------ */ +OffloadStatistics::OffloadStatistics() { + this->upstream = "UNSET"; + this->rx = 0; + this->tx = 0; +} /* OffloadStatistics */ + +OffloadStatistics::OffloadStatistics +( + std::string upstream +) { + this->upstream = upstream; + this->rx = 0; + this->tx =0; +} /* OffloadStatistics */ + +uint64_t OffloadStatistics::getTotalRxBytes() { + return rx; +} /* getTotalRxBytes */ + +uint64_t OffloadStatistics::getTotalTxBytes() { + return tx; +} /* getTotalTxBytes */ diff --git a/data-ipa-cfg-mgr/hal/src/PrefixParser.cpp b/data-ipa-cfg-mgr/hal/src/PrefixParser.cpp new file mode 100644 index 0000000..ff55147 --- /dev/null +++ b/data-ipa-cfg-mgr/hal/src/PrefixParser.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* External Includes */ +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <vector> + +/* Internal Includes */ +#include "IOffloadManager.h" +#include "PrefixParser.h" + +/* Avoiding namespace pollution */ +using IP_FAM = ::IOffloadManager::IP_FAM; +using Prefix = ::IOffloadManager::Prefix; + +using ::std::string; +using ::std::vector; + + +/* ------------------------------ PUBLIC ------------------------------------ */ +PrefixParser::PrefixParser() { + mLastErr = "No Err"; +} /* PrefixParser */ + +bool PrefixParser::add(vector<string> in) { + return add(in, IP_FAM::INVALID); +} /* add */ + +bool PrefixParser::add(string in) { + return add(in, IP_FAM::INVALID); +} /* add */ + +bool PrefixParser::addV4(string in) { + return add(in, IP_FAM::V4); +} /* addV4 */ + +bool PrefixParser::addV4(vector<string> in) { + return add(in, IP_FAM::V4); +} /* addV4 */ + +bool PrefixParser::addV6(string in) { + return add(in, IP_FAM::V6); +} /* addV6 */ + +bool PrefixParser::addV6(vector<string> in) { + for (size_t i = 0; i < in.size(); i++) { + if (!addV6(in[i])) + return false; + } + return true; +} /* addV6 */ + +int PrefixParser::size() { + return mPrefixes.size(); +} /* size */ + +bool PrefixParser::allAreFullyQualified() { + for (size_t i = 0; i < mPrefixes.size(); i++) { + if (mPrefixes[i].fam == IP_FAM::V4) { + uint32_t masked = mPrefixes[i].v4Addr & mPrefixes[i].v4Mask; + if (masked != mPrefixes[i].v4Addr) + return false; + } else { + uint32_t masked[4]; + masked[0] = mPrefixes[i].v6Addr[0] & mPrefixes[i].v6Mask[0]; + masked[1] = mPrefixes[i].v6Addr[1] & mPrefixes[i].v6Mask[1]; + masked[2] = mPrefixes[i].v6Addr[2] & mPrefixes[i].v6Mask[2]; + masked[3] = mPrefixes[i].v6Addr[3] & mPrefixes[i].v6Mask[3]; + for (int j = 0; j < 4; j++) { + if (masked[j] != mPrefixes[i].v6Addr[j]) + return false; + } + } + } + return true; +} /* allAreFullyQualified */ + +Prefix PrefixParser::getFirstPrefix() { + if (size() >= 1) + return mPrefixes[0]; + return makeBlankPrefix(IP_FAM::INVALID); +} /* getFirstPrefix */ + +Prefix PrefixParser::getFirstPrefix(IP_FAM famHint) { + if (size() >= 1) + return mPrefixes[0]; + return makeBlankPrefix(famHint); +} /* getFirstPrefix */ + +string PrefixParser::getLastErrAsStr() { + return mLastErr; +} /* getLastErrAsStr */ + + +/* ------------------------------ PRIVATE ----------------------------------- */ +bool PrefixParser::add(vector<string> in, IP_FAM famHint) { + if (in.size() == 0) + return false; + + for (size_t i = 0; i < in.size(); i++) { + if (!add(in[i], famHint)) + return false; + } + return true; +} /* add */ + +bool PrefixParser::add(string in, IP_FAM famHint) { + if (in.length() == 0) { + mLastErr = "Failed to parse string, length = 0..."; + return false; + } + + if (famHint == IP_FAM::INVALID) + famHint = guessIPFamily(in); + + string subnet; + string addr; + + if (!splitIntoAddrAndMask(in, addr, subnet)) { + mLastErr = "Failed to split into Address and Mask(" + in + ")"; + return false; + } + + int mask = parseSubnetMask(subnet, famHint); + if (!isMaskValid(mask, famHint)) { + mLastErr = "Invalid mask"; + return false; + } + + Prefix pre = makeBlankPrefix(famHint); + + if (famHint == IP_FAM::V4) { + if (!parseV4Addr(addr, pre)) { + mLastErr = "Failed to parse V4 Address(" + addr + ")"; + return false; + } + } else if (!parseV6Addr(addr, pre)) { + mLastErr = "Failed to parse V6 Address(" + addr + ")"; + return false; + } + + if (famHint == IP_FAM::V4 && !populateV4Mask(mask, pre)) { + mLastErr = "Failed to populate IPv4 Mask(" + std::to_string(mask) + + ", " + addr + ")"; + return false; + } else if (!populateV6Mask(mask, pre)) { + mLastErr = "Failed to populate IPv6 Mask(" + std::to_string(mask) + + ", " + addr + ")"; + return false; + } + + mPrefixes.push_back(pre); + return true; +} /* add */ + +/* Assumption (based on man inet_pton) + * + * X represents a hex character + * d represents a base 10 digit + * / represents the start of the subnet mask + * (assume that it can be left off of all below combinations) + * + * IPv4 Addresses always look like the following: + * ddd.ddd.ddd.ddd/dd + * + * IPv6 Addresses can look a few different ways: + * x:x:x:x:x:x:x:x/ddd + * x::x/ddd + * x:x:x:x:x:x:d.d.d.d/ddd + * + * Therefore, if a presentation of an IP Address contains a colon, then it + * may not be a valid IPv6, but, it is definitely not valid IPv4. If a + * presentation of an IP Address does not contain a colon, then it may not be + * a valid IPv4, but, it is definitely not IPv6. + */ +IP_FAM PrefixParser::guessIPFamily(string in) { + size_t found = in.find(":"); + if (found != string::npos) + return IP_FAM::V6; + return IP_FAM::V4; +} /* guessIPFamily */ + +bool PrefixParser::splitIntoAddrAndMask(string in, string &addr, string &mask) { + size_t pos = in.find("/"); + + if (pos != string::npos && pos >= 1) { + /* addr is now everything up until the first / */ + addr = in.substr(0, pos); + } else if (pos == string::npos) { + /* There is no /, so the entire input is an address */ + addr = in; + } else { + /* There was nothing before the /, not recoverable */ + return false; + } + + if (pos != string::npos && pos < in.size()) { + /* There is a / and it is not the last character. Everything after / + * must be the subnet. + */ + mask = in.substr(pos + 1); + } else if (pos != string::npos && pos == in.size()) { + /* There is a /, but it is the last character. This is garbage, but, + * we may still be able to interpret the address so we will throw it + * out. + */ + mask = ""; + } else if (pos == string::npos) { + /* There is no /, therefore, there is no subnet */ + mask = ""; + } else { + /* This really shouldn't be possible because it would imply that find + * returned a position larger than the size of the input. Just + * preserving sanity that mask is always initialized. + */ + mask = ""; + } + + return true; +} /* splitIntoAddrAndMask */ + +int PrefixParser::parseSubnetMask(string in, IP_FAM famHint) { + if (in.empty()) + /* Treat no subnet mask as fully qualified */ + return (famHint == IP_FAM::V6) ? 128 : 32; + return atoi(in.c_str()); +} /* parseSubnetMask */ + +bool PrefixParser::parseV4Addr(string in, Prefix &out) { + struct sockaddr_in sa; + + int ret = inet_pton(AF_INET, in.c_str(), &(sa.sin_addr)); + + if (ret < 0) { + /* errno would be valid */ + return false; + } else if (ret == 0) { + /* input was not a valid IP address */ + return false; + } + + /* Address in network byte order */ + out.v4Addr = htonl(sa.sin_addr.s_addr); + return true; +} /* parseV4Addr */ + +bool PrefixParser::parseV6Addr(string in, Prefix &out) { + struct sockaddr_in6 sa; + + int ret = inet_pton(AF_INET6, in.c_str(), &(sa.sin6_addr)); + + if (ret < 0) { + /* errno would be valid */ + return false; + } else if (ret == 0) { + /* input was not a valid IP address */ + return false; + } + + /* Translate unsigned chars to unsigned ints to match IPA + * + * TODO there must be a better way to do this beyond bit fiddling + * Maybe a Union since we've already made the assumption that the data + * structures match? + */ + out.v6Addr[0] = (sa.sin6_addr.s6_addr[0] << 24) | + (sa.sin6_addr.s6_addr[1] << 16) | + (sa.sin6_addr.s6_addr[2] << 8) | + (sa.sin6_addr.s6_addr[3]); + out.v6Addr[1] = (sa.sin6_addr.s6_addr[4] << 24) | + (sa.sin6_addr.s6_addr[5] << 16) | + (sa.sin6_addr.s6_addr[6] << 8) | + (sa.sin6_addr.s6_addr[7]); + out.v6Addr[2] = (sa.sin6_addr.s6_addr[8] << 24) | + (sa.sin6_addr.s6_addr[9] << 16) | + (sa.sin6_addr.s6_addr[10] << 8) | + (sa.sin6_addr.s6_addr[11]); + out.v6Addr[3] = (sa.sin6_addr.s6_addr[12] << 24) | + (sa.sin6_addr.s6_addr[13] << 16) | + (sa.sin6_addr.s6_addr[14] << 8) | + (sa.sin6_addr.s6_addr[15]); + return true; +} /* parseV6Addr */ + +bool PrefixParser::populateV4Mask(int mask, Prefix &out) { + if (mask < 0 || mask > 32) + return false; + out.v4Mask = createMask(mask); + return true; +} /* populateV4Mask */ + +bool PrefixParser::populateV6Mask(int mask, Prefix &out) { + if (mask < 0 || mask > 128) + return false; + + for (int i = 0; i < 4; i++) { + out.v6Mask[i] = createMask(mask); + mask = (mask > 32) ? mask - 32 : 0; + } + + return true; +} /* populateV6Mask */ + +uint32_t PrefixParser::createMask(int mask) { + uint32_t ret = 0; + + if (mask >= 32) { + ret = ~ret; + return ret; + } + + for (int i = 0; i < 32; i++) { + if (i < mask) + ret = (ret << 1) | 1; + else + ret = (ret << 1); + } + + return ret; +} /* createMask */ + +Prefix PrefixParser::makeBlankPrefix(IP_FAM famHint) { + Prefix ret; + + ret.fam = famHint; + + ret.v4Addr = 0; + ret.v4Mask = 0; + + ret.v6Addr[0] = 0; + ret.v6Addr[1] = 0; + ret.v6Addr[2] = 0; + ret.v6Addr[3] = 0; + + ret.v6Mask[0] = 0; + ret.v6Mask[1] = 0; + ret.v6Mask[2] = 0; + ret.v6Mask[3] = 0; + + return ret; +} /* makeBlankPrefix */ + +bool PrefixParser::isMaskValid(int mask, IP_FAM fam) { + if (mask < 0) { + mLastErr = "Failed parse subnet mask(" + std::to_string(mask) + ")"; + return false; + } else if (mask == 0) { + mLastErr = "Subnet mask cannot be 0(" + std::to_string(mask) + ")"; + return false; + } else if (fam == IP_FAM::V4 && mask > 32) { + mLastErr = "Interpreted address as V4 but mask was too large(" + + std::to_string(mask) + ")"; + return false; + } else if (fam == IP_FAM::V6 && mask > 128) { + mLastErr = "Interpreted address as V6 but mask was too large(" + + std::to_string(mask) + ")"; + return false; + } + + return true; +} /* isMaskValid */ |