Refactor the fwmark stuff to use an explicit union of bit-fields.

This is a pure refactor. There's no effect on any functionality.

Change-Id: I99d1f3fb76781cf84f67c2dff44eaf3a2cf50a9f
diff --git a/Android.mk b/Android.mk
index 66903d7..89392ff 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,7 +8,6 @@
                   CommandListener.cpp                  \
                   DnsProxyListener.cpp                 \
                   FirewallController.cpp               \
-                  Fwmark.cpp                           \
                   IdletimerController.cpp              \
                   InterfaceController.cpp              \
                   MDnsSdListener.cpp                   \
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index 7379788..3a8e475 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -53,10 +53,13 @@
 }
 
 uint32_t DnsProxyListener::calcMark(SocketClient *c, unsigned netId) const {
+    Fwmark fwmark;
+    fwmark.netId = netId;
     // If netd's UID is forced into a VPN that isn't the intended network,
     // use VPN protect bit to force it into the desired network.
-    bool vpnProtect = mNetCtrl->getNetwork(getuid(), netId, true) != netId;
-    return getFwmark(netId, false, vpnProtect, mPermCtrl->getPermissionForUser(c->getUid()));
+    fwmark.protectedFromVpn = mNetCtrl->getNetwork(getuid(), netId, true) != netId;
+    fwmark.permission = mPermCtrl->getPermissionForUser(c->getUid());
+    return fwmark.intValue;
 }
 
 DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient *c,
diff --git a/Fwmark.cpp b/Fwmark.cpp
deleted file mode 100644
index 52c8606..0000000
--- a/Fwmark.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Fwmark.h"
-
-namespace {
-
-const uint32_t FWMARK_MASK_NET_ID = 0xffff;
-const uint32_t FWMARK_MASK_EXPLICIT = 0x10000;
-const uint32_t FWMARK_MASK_PROTECT = 0x20000;
-const uint32_t FWMARK_MASK_CHANGE_NETWORK_STATE = 0x40000;
-const uint32_t FWMARK_MASK_CONNECTIVITY_INTERNAL = 0x80000;
-
-}  // namespace
-
-uint32_t getFwmark(unsigned netId, bool exp, bool protect, Permission permission) {
-    uint32_t fwmark = netId & FWMARK_MASK_NET_ID;
-    if (exp) {
-        fwmark |= FWMARK_MASK_EXPLICIT;
-    }
-    if (protect) {
-        fwmark |= FWMARK_MASK_PROTECT;
-    }
-    if (permission & PERMISSION_CHANGE_NETWORK_STATE) {
-        fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE;
-    }
-    if (permission & PERMISSION_CONNECTIVITY_INTERNAL) {
-        fwmark |= FWMARK_MASK_CONNECTIVITY_INTERNAL;
-    }
-    return fwmark;
-}
-
-uint32_t getFwmarkMask(bool netId, bool exp, bool protect, Permission permission) {
-    return getFwmark(netId ? FWMARK_MASK_NET_ID : 0, exp, protect, permission);
-}
diff --git a/Fwmark.h b/Fwmark.h
index ee3bed0..d358f81 100644
--- a/Fwmark.h
+++ b/Fwmark.h
@@ -19,15 +19,27 @@
 
 #include "Permission.h"
 
+#include <utils/Debug.h>
 #include <stdint.h>
 
-// Composes a fwmark comprising of |netId|, along with bits representing:
-//     |exp|: true if the |netId| is being explicitly requested
-//     |protect|: true if VPNs should be bypassed
-//     |permission|: != PERMISSION_NONE to assert that |permission| is held
-uint32_t getFwmark(unsigned netId, bool exp, bool protect, Permission permission);
+union Fwmark {
+    Fwmark() : intValue(0) {}
+    uint32_t intValue;
+    struct {
+        unsigned netId          : 16;
+        bool explicitlySelected :  1;
+        bool protectedFromVpn   :  1;
+        Permission permission   :  2;
+    };
+};
 
-// Composes a mask to test parts of the fwmark (see getFwmark() for details).
-uint32_t getFwmarkMask(bool netId, bool exp, bool protect, Permission permission);
+static const unsigned FWMARK_NET_ID_MASK = 0xffff;
+
+namespace android {
+
+// Ensure that all the fwmark fields fit into 32 bits.
+COMPILE_TIME_ASSERT(sizeof(Fwmark) == sizeof(uint32_t));
+
+}  // namespace android
 
 #endif  // SYSTEM_NETD_FWMARK_H
diff --git a/RouteController.cpp b/RouteController.cpp
index 05170b8..a58f02d 100644
--- a/RouteController.cpp
+++ b/RouteController.cpp
@@ -32,10 +32,6 @@
 const uint32_t RULE_PRIORITY_MAIN                  = 20000;
 const uint32_t RULE_PRIORITY_UNREACHABLE           = 21000;
 
-const bool FWMARK_USE_NET_ID   = true;
-const bool FWMARK_USE_EXPLICIT = true;
-const bool FWMARK_USE_PROTECT  = true;
-
 uint32_t getRouteTableForInterface(const char* interface) {
     uint32_t index = if_nametoindex(interface);
     return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
@@ -127,27 +123,18 @@
 
     const char* action = add ? ADD : DEL;
 
-    // A rule to route traffic based on an explicitly chosen network.
-    //
-    // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
-    //
-    // We don't really need to check the permission bits of the fwmark here, as they would've been
-    // checked at the time the netId was set into the fwmark, but we do so to be consistent.
-    uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
-                                  permission);
-    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark, mask, NULL)) {
-        return false;
-    }
+    Fwmark fwmark;
+    fwmark.permission = permission;
+
+    Fwmark mask;
+    mask.permission = permission;
 
     // A rule to route traffic based on a chosen outgoing interface.
     //
     // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
     // knows the outgoing interface (typically for link-local communications).
-    fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark, mask,
-                          interface)) {
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue,
+                          mask.intValue, interface)) {
         return false;
     }
 
@@ -156,9 +143,23 @@
     // This is for sockets that have not explicitly requested a particular network, but have been
     // bound to one when they called connect(). This ensures that sockets connected on a particular
     // network stay on that network even if the default network changes.
-    fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark, mask, NULL)) {
+    fwmark.netId = netId;
+    mask.netId = FWMARK_NET_ID_MASK;
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue,
+                          mask.intValue, NULL)) {
+        return false;
+    }
+
+    // A rule to route traffic based on an explicitly chosen network.
+    //
+    // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
+    //
+    // We don't really need to check the permission bits of the fwmark here, as they would've been
+    // checked at the time the netId was set into the fwmark, but we do so to be consistent.
+    fwmark.explicitlySelected = true;
+    mask.explicitlySelected = true;
+    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue,
+                          mask.intValue, NULL)) {
         return false;
     }
 
@@ -188,11 +189,16 @@
         return false;
     }
 
-    uint32_t fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
-    uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
-                                  permission);
+    Fwmark fwmark;
+    fwmark.netId = 0;
+    fwmark.permission = permission;
 
-    return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark, mask, NULL);
+    Fwmark mask;
+    mask.netId = FWMARK_NET_ID_MASK;
+    mask.permission = permission;
+
+    return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue,
+                            mask.intValue, NULL);
 }
 
 bool modifyRoute(const char* interface, const char* destination, const char* nexthop,
@@ -238,10 +244,13 @@
     // default route, the rule we're adding will never be used for normal routing lookups. However,
     // the kernel may fall-through to it to find directly-connected routes when it validates that a
     // nexthop (in a route being added) is reachable.
-    uint32_t fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, PERMISSION_NONE);
-    uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
-                                  PERMISSION_NONE);
-    runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark, mask, NULL);
+    Fwmark fwmark;
+    fwmark.netId = 0;
+
+    Fwmark mask;
+    mask.netId = FWMARK_NET_ID_MASK;
+
+    runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL);
 
 // TODO: Uncomment once we are sure everything works.
 #if 0