aboutsummaryrefslogtreecommitdiff
path: root/data-ipa-cfg-mgr/hal/src
diff options
context:
space:
mode:
authorBruno Martins <bgcngm@gmail.com>2018-01-08 12:51:07 +0000
committerDavide Garberi <dade.garberi@gmail.com>2018-01-20 09:30:21 +0100
commit1c67f47d4a205e12650dcb6255c00bafc0046082 (patch)
tree72bb48ca2152cb2295b0f8de39497490d8616f6f /data-ipa-cfg-mgr/hal/src
parent6b0c862328f58e523e2a6efa667a2f4b3a609bf3 (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.cpp123
-rw-r--r--data-ipa-cfg-mgr/hal/src/HAL.cpp612
-rw-r--r--data-ipa-cfg-mgr/hal/src/IpaEventRelay.cpp83
-rw-r--r--data-ipa-cfg-mgr/hal/src/LocalLogBuffer.cpp126
-rw-r--r--data-ipa-cfg-mgr/hal/src/OffloadStatistics.cpp57
-rw-r--r--data-ipa-cfg-mgr/hal/src/PrefixParser.cpp391
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 */