hal: 1.0: Initial commit

Create a shared library (liboffloadhal) that links against
android.hardware.tetheroffload.config@1.0.so and
android.hardware.tetheroffload.control@1.0.so in order to
provide an abstraction for IPACM that translates input/output
to/from the controlling client and IPACM.  Also, provide headers
to IPACM for defining abstract classes (aka interfaces).

Change-Id: Ie1f8478c4ac918a33af38a303b5e3bd74226562c
CRs-Fixed: 1112672
diff --git a/hal/Android.mk b/hal/Android.mk
new file mode 100644
index 0000000..b342a69
--- /dev/null
+++ b/hal/Android.mk
@@ -0,0 +1,29 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_ARM_MODE := arm
+LOCAL_SRC_FILES := src/CtUpdateAmbassador.cpp \
+                src/HAL.cpp \
+                src/IpaEventRelay.cpp \
+                src/LocalLogBuffer.cpp \
+                src/OffloadStatistics.cpp \
+                src/PrefixParser.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/inc
+LOCAL_MODULE := liboffloadhal
+LOCAL_CPP_FLAGS := -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libhwbinder \
+                        libhidlbase \
+                        libhidltransport \
+                        liblog \
+                        libcutils \
+                        libdl \
+                        libbase \
+                        libutils \
+                        libhardware_legacy \
+                        libhardware \
+                        android.hardware.tetheroffload.config@1.0 \
+                        android.hardware.tetheroffload.control@1.0
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_PATH_32 := $(TARGET_OUT_VENDOR)/lib
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64
+include $(BUILD_SHARED_LIBRARY)
diff --git a/hal/inc/CtUpdateAmbassador.h b/hal/inc/CtUpdateAmbassador.h
new file mode 100644
index 0000000..d4890f3
--- /dev/null
+++ b/hal/inc/CtUpdateAmbassador.h
@@ -0,0 +1,62 @@
+/*
+ * 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 _CT_UPDATE_AMBASSADOR_H_
+#define _CT_UPDATE_AMBASSADOR_H_
+/* External Includes */
+#include <hidl/HidlTransportSupport.h>
+
+/* HIDL Includes */
+#include <android/hardware/tetheroffload/control/1.0/ITetheringOffloadCallback.h>
+
+/* Internal Includes */
+#include "IOffloadManager.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;
+
+
+class CtUpdateAmbassador : public IOffloadManager::ConntrackTimeoutUpdater {
+public:
+    CtUpdateAmbassador(const ::android::sp<ITetheringOffloadCallback>& /* cb */);
+    /* ------------------- CONNTRACK TIMEOUT UPDATER ------------------------ */
+    void updateTimeout(IpaNatTimeoutUpdate /* update */);
+private:
+    static bool translate(IpaNatTimeoutUpdate /* in */, HALNatTimeoutUpdate& /* out */);
+    static bool translate(IpaIpAddrPortPair /* in */, HALIpAddrPortPair& /* out */);
+    static bool L4ToNetwork(IpaL4Protocol /* in */, NetworkProtocol& /* out */);
+    const ::android::sp<ITetheringOffloadCallback>& mFramework;
+}; /* CtUpdateAmbassador */
+#endif /* _CT_UPDATE_AMBASSADOR_H_ */
\ No newline at end of file
diff --git a/hal/inc/HAL.h b/hal/inc/HAL.h
new file mode 100644
index 0000000..622a67e
--- /dev/null
+++ b/hal/inc/HAL.h
@@ -0,0 +1,199 @@
+/*
+ * 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 _HAL_H_
+#define _HAL_H_
+
+/* HIDL Includes */
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
+#include <hidl/HidlTransportSupport.h>
+
+/* External Includes */
+#include <string>
+#include <vector>
+
+/* Internal Includes */
+#include "CtUpdateAmbassador.h"
+#include "IOffloadManager.h"
+#include "IpaEventRelay.h"
+#include "LocalLogBuffer.h"
+
+/* Avoid the namespace litering everywhere */
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+using RET = ::IOffloadManager::RET;
+using Prefix = ::IOffloadManager::Prefix;
+
+using ::std::map;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
+using ::android::hardware::tetheroffload::control::V1_0::IOffloadControl;
+
+using ::android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
+
+
+class HAL : public IOffloadControl, IOffloadConfig {
+public:
+    /* Static Const Definitions */
+    static const uint32_t UDP_SUBSCRIPTIONS =
+            NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY;
+    static const uint32_t TCP_SUBSCRIPTIONS =
+            NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY;
+
+    /* Interface to IPACM */
+    /**
+     * @TODO This will likely need to be extended into a proper FactoryPattern
+     * when version bumps are needed.
+     *
+     * This makeIPAHAL function would move to a HALFactory class.  Each HAL could
+     * then be versioned (class HAL_V1, class HAL_V2, etc) and inherit from a base class HAL.
+     * Then the version number in this function could be used to decide which one to return
+     * (if any).
+     *
+     * IPACM does not need to talk directly back to the returned HAL class.  The other methods that
+     * IPACM needs to call are covered by registering the event listeners.  If IPACM did need to
+     * talk directly back to the HAL object, without HAL registering a callback, these methods would
+     * need to be defined in the HAL base class.
+     *
+     * This would slightly break backwards compatibility so it should be discouraged; however, the
+     * base class could define a sane default implementation and not require that the child class
+     * implement this new method.  This "sane default implementation" might only be possible in the
+     * case of listening to async events; if IPACM needs to query something, then this would not
+     * be backwards compatible and should be done via registering a callback so that IPACM knows
+     * this version of HAL supports that functionality.
+     *
+     * The above statements assume that only one version of the HAL will be instantiated at a time.
+     * Yet, it seems possible that a HAL_V1 and HAL_V2 service could both be registered, extending
+     * support to both old and new client implementations.  It would be difficult to multiplex
+     * information from both versions.  Additionally, IPACM would be responsible for instantiating
+     * two HALs (makeIPAHAL(1, ...); makeIPAHAL(2, ...)) which makes signaling between HAL versions
+     * (see next paragraph) slightly more difficult but not impossible.
+     *
+     * If concurrent versions of HAL are required, there will likely need to only be one master.
+     * Whichever version of HAL receives a client first may be allowed to take over control while
+     * other versions would be required to return failures (ETRYAGAIN: another version in use) until
+     * that version of the client relinquishes control.  This should work seemlessly because we
+     * currently have an assumption that only one client will be present in system image.
+     * Logically, that client will have only a single version (or if it supports multiple, it will
+     * always attempt the newest version of HAL before falling back) and therefore no version
+     * collisions could possibly occur.
+     *
+     * Dislaimer:
+     * ==========
+     * Supporting multiple versions of an interface, in the same code base, at runtime, comes with a
+     * significant carrying cost and overhead in the form of developer headaches.  This should not
+     * be done lightly and should be extensively scoped before committing to the effort.
+     *
+     * Perhaps the notion of minor version could be introduced to bridge the gaps created above.
+     * For example, 1.x and 1.y could be ran concurrently and supported from the same IPACM code.
+     * Yet, a major version update, would not be backwards compatible.  This means that a 2.x HAL
+     * could not linked into the same IPACM code base as a 1.x HAL.
+     */
+    static HAL* makeIPAHAL(int /* version */, IOffloadManager* /* mgr */);
+
+    /* IOffloadConfig */
+    Return<void> setHandles(
+            const hidl_handle& /* fd1 */,
+            const hidl_handle& /* fd2 */,
+            setHandles_cb /* hidl_cb */);
+
+    /* IOffloadControl */
+    Return<void> initOffload(
+            const ::android::sp<ITetheringOffloadCallback>& /* cb */,
+            initOffload_cb /* hidl_cb */);
+    Return<void> stopOffload(
+            stopOffload_cb /* hidl_cb */);
+    Return<void> setLocalPrefixes(
+            const hidl_vec<hidl_string>& /* prefixes */,
+            setLocalPrefixes_cb /* hidl_cb */);
+    Return<void> getForwardedStats(
+            const hidl_string& /* upstream */,
+            getForwardedStats_cb /* hidl_cb */);
+    Return<void> setDataLimit(
+            const hidl_string& /* upstream */,
+            uint64_t /* limit */,
+            setDataLimit_cb /* hidl_cb */);
+    Return<void> setUpstreamParameters(
+            const hidl_string& /* iface */,
+            const hidl_string& /* v4Addr */,
+            const hidl_string& /* v4Gw */,
+            const hidl_vec<hidl_string>& /* v6Gws */,
+            setUpstreamParameters_cb /* hidl_cb */);
+    Return<void> addDownstream(
+            const hidl_string& /* iface */,
+            const hidl_string& /* prefix */,
+            addDownstream_cb /* hidl_cb */);
+    Return<void> removeDownstream(
+            const hidl_string& /* iface */,
+            const hidl_string& /* prefix */,
+            removeDownstream_cb /* hidl_cb */);
+
+private:
+    typedef struct BoolResult {
+        bool success;
+        string errMsg;
+    } boolResult_t;
+
+    HAL(IOffloadManager* /* mgr */);
+    void registerAsSystemService(const char* /* name */);
+
+    void doLogcatDump();
+
+    static BoolResult makeInputCheckFailure(string /* customErr */);
+    static BoolResult ipaResultToBoolResult(RET /* in */);
+
+    static vector<string> convertHidlStrToStdStr(hidl_vec<hidl_string> /* in */);
+
+    void registerEventListeners();
+    void registerIpaCb();
+    void registerCtCb();
+    void unregisterEventListeners();
+    void unregisterIpaCb();
+    void unregisterCtCb();
+
+    void clearHandles();
+
+    bool isInitialized();
+
+    IOffloadManager* mIPA;
+    hidl_handle mHandle1;
+    hidl_handle mHandle2;
+    LocalLogBuffer mLogs;
+    ::android::sp<ITetheringOffloadCallback> mCb;
+    IpaEventRelay *mCbIpa;
+    CtUpdateAmbassador *mCbCt;
+}; /* HAL */
+#endif /* _HAL_H_ */
diff --git a/hal/inc/IOffloadManager.h b/hal/inc/IOffloadManager.h
new file mode 100644
index 0000000..6a357b3
--- /dev/null
+++ b/hal/inc/IOffloadManager.h
@@ -0,0 +1,350 @@
+/*
+ * 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 _I_OFFLOAD_MANAGER_H_
+#define _I_OFFLOAD_MANAGER_H_
+
+/* External Includes */
+#include <sys/types.h>
+
+/* Internal Includes */
+#include "OffloadStatistics.h"
+
+
+class IOffloadManager {
+public:
+    enum RET {
+        FAIL_TOO_MANY_PREFIXES = -6,
+        FAIL_UNSUPPORTED = -5,
+        FAIL_INPUT_CHECK = -4,
+        FAIL_HARDWARE = -3,
+        FAIL_UNNEEDED = -2,
+        FAIL_TRY_AGAIN = -1,
+        SUCCESS = 0,
+        SUCCESS_DUPLICATE_CONFIG = 1,
+        SUCCESS_NO_OP = 2,
+        SUCCESS_OPTIMIZED = 3
+    }; /* RET */
+
+    enum IP_FAM {
+        V4 = 0,
+        V6 = 1,
+        INVALID = 2
+    }; /* IP_FAM */
+
+    /* Overloading to use for addresses as well */
+    typedef struct Prefix {
+        IP_FAM fam;
+        uint32_t v4Addr;
+        uint32_t v4Mask;
+        uint32_t v6Addr[4];
+        uint32_t v6Mask[4];
+    } prefix_t;
+
+    /* ---------------------------- LIFECYCLE ------------------------------- */
+    virtual ~IOffloadManager(){}
+
+    /* ---------------------- ASYNC EVENT CALLBACKS ------------------------- */
+    class IpaEventListener {
+    public:
+        enum StoppedReason {
+            /**
+             * Offload was stopped due to the configuration being removed via
+             * setUpstreamParameters/removeDownstream.
+             */
+            REQUESTED,
+            /**
+             * Offload was stopped due to an internal (to IPA or modem) error.
+             *
+             * Statistics may be temporarily unavailable.
+             */
+            ERROR,
+            /**
+             * Offload was stopped because the upstream connection has
+             * migrated to unsupported radio access technology.
+             *
+             * Statistics will still be available.
+             */
+            UNSUPPORTED
+        }; /* StoppedReason */
+        virtual ~IpaEventListener(){}
+        /**
+         * Called when Offload first begins to occur on any upstream and
+         * tether interface pair.  It should be paired with an onOffloadStopped
+         * call.
+         */
+        virtual void onOffloadStarted(){}
+        /**
+         * Called when Offload stops occurring on all upstream and tether
+         * interface pairs.  It comes after a call to onOffloadStarted.
+         *
+         * @param reason Reason that Offload was stopped
+         */
+        virtual void onOffloadStopped(StoppedReason /* reason */){}
+        /**
+         * Called when the hardware can support Offload again.
+         *
+         * Any statistics that were previously unavailable, may be queried
+         * again at this time.
+         */
+        virtual void onOffloadSupportAvailable(){}
+        /**
+         * Called when the limit set via setQuota has expired.
+         *
+         * It is implied that Offload has been stopped on all upstream and
+         * tether interface pairs when this callback is called.
+         */
+        virtual void onLimitReached(){}
+    }; /* IpaEventListener */
+
+    /**
+     * Request notifications about asynchronous events that occur in hardware.
+     *
+     * The calling client must be able to handle the callback on a separate
+     * thread (i.e. their implementation of IpaEventListener must be thread
+     * safe).
+     *
+     * @return SUCCESS iff callback successfully registered
+     *
+     * Remarks: This can't really be allowed to fail.
+     */
+    virtual RET registerEventListener(IpaEventListener* /* listener */) = 0;
+    /**
+     * Unregister a previously registered listener.
+     *
+     * @return SUCCESS iff callback successfully unregistered
+     *         FAIL_INPUT_CHECK if callback was never registered
+     */
+    virtual RET unregisterEventListener(IpaEventListener* /* listener */) = 0;
+
+    class ConntrackTimeoutUpdater {
+    public:
+        enum L4Protocol {
+            TCP = 0,
+            UDP = 1
+        }; /* L4Protocol */
+        typedef struct IpAddrPortPair {
+            uint32_t ipAddr;
+            uint16_t port;
+        } ipAddrPortPair_t;
+        typedef struct NatTimeoutUpdate {
+            IpAddrPortPair src;
+            IpAddrPortPair dst;
+            L4Protocol proto;
+        } natTimeoutUpdate_t;
+        virtual ~ConntrackTimeoutUpdater(){}
+        virtual void updateTimeout(NatTimeoutUpdate /* update */) {}
+    }; /* ConntrackTimeoutUpdater */
+
+    /**
+     * Register a callback that may be called if the OffloadManager wants to
+     * update the timeout value in conntrack of kernel.
+     *
+     * The calling client must be able to handle the callback on a separate
+     * thread (i.e. their implementation of ConntrackTimeoutUpdater must be
+     * thread safe)
+     *
+     * @return SUCCESS iff callback successfully registered
+     *
+     * Remarks: This can't really be allowed to fail
+     */
+    virtual RET registerCtTimeoutUpdater(ConntrackTimeoutUpdater* /* cb */) = 0;
+    /**
+     * Unregister a previously registered callback.
+     *
+     * @return SUCCESS iff callback successfully unregistered
+     *         FAIL_INPUT_CHECK if callback was never registered
+     */
+    virtual RET unregisterCtTimeoutUpdater(ConntrackTimeoutUpdater* /* cb */) = 0;
+
+    /* ----------------------------- CONFIG --------------------------------- */
+    /**
+     * Provide a file descriptor for use with conntrack library
+     *
+     * @param fd File Descriptor that has been opened and bound to groups
+     * @param groups Groups (bit mask) that fd has been bound to
+     *
+     * @return SUCCESS iff IOffloadManager needed this file descriptor and
+     *                 it was properly bound.
+     *         FAIL_INPUT_CHECK if IOffloadManager needed this file descriptor
+     *                          but it was found to not be properly bound
+     *         FAIL_UNNEEDED if IOffloadManager determined that it does not need
+     *                       a file descriptor bound to these groups.
+     */
+    virtual RET provideFd(int /* fd */, unsigned int /* group */) = 0;
+    /**
+     * Indicate that IOffloadManager <b>must</b> cease using all file
+     * descriptors passed via provideFd API.
+     *
+     * After this call returns, the file descriptors will likely be closed by
+     * the calling client.
+     *
+     * @return SUCCESS iff IOffloadManager has stopped using all file
+     *                 descriptors
+     *         FAIL_TRY_AGAIN if IOffloadManager needs more time with these
+     *                        file descriptors before it can release them
+     *
+     * Remarks: Currently, it would be very difficult to handle a FAIL_TRY_AGAIN
+     *          because HAL serivce does not own a thread outside of RPC
+     *          Threadpool to reschedule this call.
+     */
+    virtual RET clearAllFds() = 0;
+    /**
+     * Query whether STA+AP offload is supported on this device.
+     *
+     * @return true if supported, false otherwise
+     */
+    virtual bool isStaApSupported() = 0;
+
+    /* ------------------------------ ROUTE --------------------------------- */
+    /**
+     * Add a downstream prefix that <i>may</i> be forwarded.
+     *
+     * The Prefix may be an IPv4 or IPv6 address to signify which family can be
+     * offloaded from the specified tether interface.  If the given IP family,
+     * as determined by the Prefix, has a corresponding upstream configured,
+     * then traffic should be forwarded between the two interfaces.
+     *
+     * Only traffic that has a downstream address within the specified Prefix
+     * can be forwarded.  Traffic from the same downstream interface that falls
+     * outside of the Prefix will be unaffected and can be forwarded iff it was
+     * previously configured via a separate addDownstream call.
+     *
+     * If no upstream has been configured, then this information must be cached
+     * so that offload may begin once an upstream is configured.
+     *
+     * This API does <b>not</b> replace any previously configured downstreams
+     * and must be explicitly removed by calling removeDownstream or by clearing
+     * the entire configuration by calling stopAllOffload.
+     *
+     * @return SUCCESS The new information was accepted
+     *         FAIL_TOO_MANY_PREFIXES The hardware has already accepted the max
+     *                                number of Prefixes that can be supported.
+     *                                If offload is desired on this Prefix then
+     *                                another must be removed first.
+     *         FAIL_UNSUPPORTED The hardware cannot forward traffic from this
+     *                          downstream interface and will never be able to.
+     */
+    virtual RET addDownstream(const char* /* downstream */,
+            const Prefix& /* prefix */) = 0;
+    /**
+     * Remove a downstream Prefix that forwarding was previously requested for.
+     *
+     * The Prefix may be an IPv4 or IPv6 address.  Traffic outside of this
+     * Prefix is not affected.
+     *
+     * @return SUCCESS iff forwarding was previously occurring and has been
+     *                 stopped
+     *         SUCCESS_NO_OP iff forwarding was not previously occurring and
+     *                       therefore no action needed to be taken
+     */
+    virtual RET removeDownstream(const char* /* downstream */,
+            const Prefix& /* prefix */) = 0;
+    /**
+     * Indicate that hardware should forward traffic from any configured
+     * downstreams to the specified upstream.
+     *
+     * When iface is non-null and non-empty and v4Gw is valid, then any
+     * currently configured or future configured IPv4 downstreams should be
+     * forwarded to this upstream interface.
+     *
+     * When iface is non-null and non-empty and v6Gw is valid, then any
+     * currently configured or future configured IPv6 downstreams should be
+     * forwarded to this upstream interface.
+     *
+     * @param iface Upstream interface name.  Only one is needed because IPv4
+     *              and IPv6 interface names are required to match.
+     * @param v4Gw The address of the IPv4 Gateway on the iface
+     * @param v6Gw The address of one of the IPv6 Gateways on the iface
+     *
+     * @return SUCCESS iff the specified configuration was applied
+     *         SUCCESS_DUPLICATE_CONFIG if this configuration <i>exactly</i>
+     *                                  matches a previously provided
+     *                                  configuration.  This means that no
+     *                                  action has to be taken, but, the
+     *                                  configuration was previously accepted
+     *                                  and applied.
+     *         FAIL_UNSUPPORTED if hardware cannot support forwarding to this
+     *                          upstream interface
+     *
+     * Remarks: This overrides any previously configured parameters
+     */
+    virtual RET setUpstream(const char* /* iface */, const Prefix& /* v4Gw */,
+            const Prefix& /* v6Gw */) = 0;
+    /**
+     * All traffic must be returned to the software path and all configuration
+     * (including provided file descriptors) must be forgotten.
+     *
+     * @return SUCCESS If all offload was successfully stopped and provided
+     *                 file descriptors were released.
+     *
+     * Remarks: This can't really fail?
+     */
+    virtual RET stopAllOffload() = 0;
+
+    /* --------------------------- STATS/POLICY ----------------------------- */
+    /**
+     * Instruct hardware to stop forwarding traffic and send a callback after
+     * limit bytes have been transferred in either direction on this upstream
+     * interface.
+     *
+     * @param upstream Upstream interface name that the limit should apply to
+     * @param limit Bytes limit that can occur before action should be taken
+     *
+     * @return SUCCESS If the limit was successfully applied
+     *         SUCCESS_OPTIMIZED If the limit was sufficiently high to be
+     *                           interpreted as "no quota".
+     *         FAIL_HARDWARE If the limit was rejected by the hardware
+     *         FAIL_UNSUPPORTED If metering is not supported on this interface
+     *         FAIL_TRY_AGAIN If this upstream has not been previously
+     *                        configured to allow offload
+     *                        (via setUpstreamParameters)
+     */
+    virtual RET setQuota(const char* /* upstream */, uint64_t /* limit */) = 0;
+    /**
+     * Query for statistics counters in hardware.
+     *
+     * This returns an aggregate of all hardware accelerated traffic which
+     * has occurred on this upstream interface.
+     *
+     * @param upstream Interface on which traffic entered/exited
+     * @param reset Whether hardware counters should reset after returning
+     *              current statistics
+     * @param ret Output variable where statistics are returned
+     *
+     * @return SUCCESS If the statistics were successfully populated in ret and
+     *                 were successfully reset if requested.
+     *         FAIL_TRY_AGAIN If the statistics are not currently available but
+     *                        may be available later.  This may occur during
+     *                        a subsystem restart.
+     *         FAIL_UNSUPPORTED If statistics are not supported on this upstream
+     */
+    virtual RET getStats(const char* /* upstream */, bool /* reset */,
+            OffloadStatistics& /* ret */) = 0;
+}; /* IOffloadManager */
+#endif /* _I_OFFLOAD_MANAGER_H_ */
diff --git a/hal/inc/IpaEventRelay.h b/hal/inc/IpaEventRelay.h
new file mode 100644
index 0000000..4541510
--- /dev/null
+++ b/hal/inc/IpaEventRelay.h
@@ -0,0 +1,55 @@
+/*
+ * 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 _IPA_EVENT_RELAY_H_
+#define _IPA_EVENT_RELAY_H_
+/* External Includes */
+#include <hidl/HidlTransportSupport.h>
+
+/* HIDL Includes */
+#include <android/hardware/tetheroffload/control/1.0/ITetheringOffloadCallback.h>
+
+/* Internal Includes */
+#include "IOffloadManager.h"
+
+/* Namespace pollution avoidance */
+using ::android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
+
+
+class IpaEventRelay : public IOffloadManager::IpaEventListener {
+public:
+    IpaEventRelay(const ::android::sp<ITetheringOffloadCallback>& /* cb */);
+    /* ----------------------- IPA EVENT LISTENER --------------------------- */
+    void onOffloadStarted();
+    void onOffloadStopped(StoppedReason /* reason */);
+    void onOffloadSupportAvailable();
+    void onLimitReached();
+private:
+    const ::android::sp<ITetheringOffloadCallback>& mFramework;
+}; /* IpaEventRelay */
+#endif /* _IPA_EVENT_RELAY_H_ */
\ No newline at end of file
diff --git a/hal/inc/LocalLogBuffer.h b/hal/inc/LocalLogBuffer.h
new file mode 100644
index 0000000..c23ef7d
--- /dev/null
+++ b/hal/inc/LocalLogBuffer.h
@@ -0,0 +1,73 @@
+/*
+ * 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 _LOCAL_LOG_BUFFER_H_
+#define _LOCAL_LOG_BUFFER_H_
+/* External Includes */
+#include <deque>
+#include <sstream>
+#include <string>
+#include <sys/types.h>
+#include <vector>
+
+/* Namespace pollution avoidance */
+using ::std::deque;
+using ::std::string;
+using ::std::stringstream;
+using ::std::vector;
+
+
+class LocalLogBuffer {
+public:
+    class FunctionLog {
+    public:
+        FunctionLog(string /* funcName */);
+        FunctionLog(const FunctionLog& /* other */);
+        void addArg(string /* kw */, string /* arg */);
+        void addArg(string /* kw */, vector<string> /* args */);
+        void addArg(string /* kw */, uint64_t /* arg */);
+        void setResult(bool /* success */, string /* msg */);
+        void setResult(vector<unsigned int> /* ret */);
+        void setResult(uint64_t /* rx */, uint64_t /* tx */);
+        string toString();
+    private:
+        void maybeAddArgsComma();
+        const string mName;
+        bool mArgsProvided;
+        stringstream mSSArgs;
+        stringstream mSSReturn;
+    }; /* FunctionLog */
+    LocalLogBuffer(string /* name */, int /* maxLogs */);
+    void addLog(FunctionLog /* log */);
+    void toLogcat();
+private:
+    deque<FunctionLog> mLogs;
+    const string mName;
+    const size_t mMaxLogs;
+}; /* LocalLogBuffer */
+#endif /* _LOCAL_LOG_BUFFER_H_ */
\ No newline at end of file
diff --git a/hal/inc/OffloadStatistics.h b/hal/inc/OffloadStatistics.h
new file mode 100644
index 0000000..226ea72
--- /dev/null
+++ b/hal/inc/OffloadStatistics.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _OFFLOAD_STATISTICS_H_
+#define _OFFLOAD_STATISTICS_H_
+
+#include <string>
+#include <sys/types.h>
+
+
+class OffloadStatistics {
+public:
+    uint64_t rx;
+    uint64_t tx;
+
+    OffloadStatistics();
+    OffloadStatistics(std::string /* upstream */);
+
+    uint64_t getTotalRxBytes();
+    uint64_t getTotalTxBytes();
+private:
+    std::string upstream;
+}; /* OffloadStatistics */
+#endif /* _OFFLOAD_STATISTICS_H_ */
diff --git a/hal/inc/PrefixParser.h b/hal/inc/PrefixParser.h
new file mode 100644
index 0000000..4682aa5
--- /dev/null
+++ b/hal/inc/PrefixParser.h
@@ -0,0 +1,79 @@
+/*
+ * 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 _PREFIX_PARSER_H_
+#define _PREFIX_PARSER_H_
+
+/* External Includes */
+#include <string.h>
+#include <sys/types.h>
+#include <vector>
+
+/* Internal Includes */
+#include "IOffloadManager.h"
+
+/* Avoiding namespace pollution */
+using IP_FAM = ::IOffloadManager::IP_FAM;
+using Prefix = ::IOffloadManager::Prefix;
+
+using ::std::string;
+using ::std::vector;
+
+
+class PrefixParser {
+public:
+    PrefixParser();
+    bool add(vector<string> /* in */);
+    bool add(string /* in */);
+    bool addV4(vector<string> /* in */);
+    bool addV4(string /* in */);
+    bool addV6(vector<string> /* in */);
+    bool addV6(string /* in */);
+    int size();
+    bool allAreFullyQualified();
+    Prefix getFirstPrefix();
+    string getLastErrAsStr();
+private:
+    bool add(string /* in */, IP_FAM /* famHint */);
+    bool add(vector<string> /* in */, IP_FAM /* famHint */);
+    static IP_FAM guessIPFamily(string /* in */);
+    static bool splitIntoAddrAndMask(string /* in */, string& /* addr */,
+            string& /* mask */);
+    static int parseSubnetMask(string /* in */, IP_FAM /* famHint */);
+    static bool parseV4Addr(string /* in */, Prefix& /* out */);
+    static bool parseV6Addr(string /* in */, Prefix& /* out */);
+    static bool populateV4Mask(int /* mask */, Prefix& /* out */);
+    static bool populateV6Mask(int /* mask */, Prefix& /* out */);
+    static uint32_t createMask(int /* mask */);
+    static Prefix makeBlankPrefix(IP_FAM /* famHint */);
+    bool isMaskValid(int /* mask */, IP_FAM /* fam */);
+    static const uint32_t FULLY_QUALIFIED_MASK = ~0;
+    vector<Prefix> mPrefixes;
+    string mLastErr;
+}; /* PrefixParser */
+#endif /* _PREFIX_PARSER_H_ */
\ No newline at end of file
diff --git a/hal/src/CtUpdateAmbassador.cpp b/hal/src/CtUpdateAmbassador.cpp
new file mode 100644
index 0000000..4843fe2
--- /dev/null
+++ b/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/hal/src/HAL.cpp b/hal/src/HAL.cpp
new file mode 100644
index 0000000..0d25aa0
--- /dev/null
+++ b/hal/src/HAL.cpp
@@ -0,0 +1,582 @@
+/*
+ * 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 <string>
+#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(name);
+    if (ret != 0) ALOGE("Failed to register IOffloadControl (%d)", ret);
+    else if (DBG) {
+        ALOGI("Successfully registered IOffloadControl (%s)", name);
+    }
+
+    IOffloadConfig::registerAsService(name);
+    if (ret != 0) ALOGE("Failed to register IOffloadConfig (%d)", ret);
+    else if (DBG) {
+        ALOGI("Successfully registered IOffloadConfig (%s)", name);
+    }
+} /* 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
+) {
+    LocalLogBuffer::FunctionLog fl(__func__);
+    fl.addArg("prefixes", "unused");
+    #pragma unused(prefixes)
+
+    /* Fake Success */
+    BoolResult res = ipaResultToBoolResult(RET::SUCCESS);
+    hidl_cb(res.success, res.errMsg);
+
+    fl.setResult(res.success, res.errMsg);
+    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);
+
+    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 (!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 (v6GwParser.size() > 1) {
+        RET ipaReturn = mIPA->stopAllOffload();
+        if (ipaReturn != RET::SUCCESS) {
+            BoolResult res =
+                    makeInputCheckFailure("Cannot accept more than 1 IPv6 Gateway.  Offload still running and may result in data path errors");
+            hidl_cb(res.success, res.errMsg);
+            fl.setResult(res.success, res.errMsg);
+        } else {
+            BoolResult res =
+                    makeInputCheckFailure("Cannot accept more than 1 IPv6 Gateway.  In an effort to avoid any data path errors, offload has been stopped");
+            hidl_cb(res.success, res.errMsg);
+            fl.setResult(res.success, res.errMsg);
+        }
+    } else {
+        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);
+    }
+
+    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 (!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 (!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/hal/src/IpaEventRelay.cpp b/hal/src/IpaEventRelay.cpp
new file mode 100644
index 0000000..788b152
--- /dev/null
+++ b/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/hal/src/LocalLogBuffer.cpp b/hal/src/LocalLogBuffer.cpp
new file mode 100644
index 0000000..f556e40
--- /dev/null
+++ b/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/hal/src/OffloadStatistics.cpp b/hal/src/OffloadStatistics.cpp
new file mode 100644
index 0000000..8f8beb6
--- /dev/null
+++ b/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/hal/src/PrefixParser.cpp b/hal/src/PrefixParser.cpp
new file mode 100644
index 0000000..60aae08
--- /dev/null
+++ b/hal/src/PrefixParser.cpp
@@ -0,0 +1,375 @@
+/*
+ * 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 */
+
+string PrefixParser::getLastErrAsStr() {
+    return mLastErr;
+} /* getLastErrAsStr */
+
+
+/* ------------------------------ PRIVATE ----------------------------------- */
+bool PrefixParser::add(vector<string> in, IP_FAM famHint) {
+    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 (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))
+        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 */