android clat service

This software provides the nat 4->6 translation needed for the "clat" part of
the 464xlat standard.  It is needed for better IPv4 application support while
on an IPv6-only mobile network connection using 464xlat's nat64 (such as
T-Mobile's IPv6 trial).

A general diagram of how 464xlat works:
http://dan.drown.org/android/clat/Clat-Plat.png

Depends-on: I2392f8127dcd90d16b0f20ff31bcc5aa096db464
Change-Id: If2bc6916fc66fd4bca7cc241c83cfae839b82e15
Signed-off-by: Daniel Drown <dan-android@drown.org>
diff --git a/setroute.c b/setroute.c
new file mode 100644
index 0000000..3edfe39
--- /dev/null
+++ b/setroute.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * 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.
+ *
+ * setroute.c - network route configuration
+ */
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <netlink-types.h>
+
+#include "netlink_msg.h"
+#include "setroute.h"
+#include "logging.h"
+#include "getroute.h"
+
+/* function: if_route
+ * create/replace/delete a route
+ * ifname      - name of the outbound interface
+ * family      - AF_INET or AF_INET6
+ * destination - pointer to a struct in_addr or in6_addr for the destination network
+ * prefixlen   - bitlength of the network address (example: 24 for AF_INET's 255.255.255.0)
+ * gateway     - pointer to a struct in_addr or in6_addr for the gateway to use or NULL for an interface route
+ * metric      - route metric (lower is better)
+ * mtu         - route-specific mtu or 0 for the interface mtu
+ * change_type - ROUTE_DELETE, ROUTE_REPLACE, or ROUTE_CREATE
+ */
+int if_route(const char *ifname, int family, const void *destination, int prefixlen, const void *gateway, int metric, int mtu, int change_type) {
+  int retval;
+  struct nl_msg *msg = NULL;
+  struct rtmsg rt;
+  uint16_t type, flags = 0;
+  size_t addr_size;
+  uint32_t ifindex;
+
+  addr_size = inet_family_size(family);
+  if(addr_size == 0) {
+    retval = -EAFNOSUPPORT;
+    goto cleanup;
+  }
+
+  if (!(ifindex = if_nametoindex(ifname))) {
+    retval = -ENODEV;
+    goto cleanup;
+  }
+
+  memset(&rt, 0, sizeof(rt));
+  rt.rtm_family = family;
+  rt.rtm_table = RT_TABLE_MAIN;
+  rt.rtm_dst_len = prefixlen;
+  switch(change_type) {
+    case ROUTE_DELETE:
+      rt.rtm_scope = RT_SCOPE_NOWHERE;
+      type = RTM_DELROUTE;
+      break;
+
+    case ROUTE_REPLACE:
+      flags = NLM_F_REPLACE;
+    case ROUTE_CREATE:
+      type = RTM_NEWROUTE;
+      flags |= NLM_F_CREATE;
+      if(gateway == NULL) {
+        rt.rtm_scope = RT_SCOPE_LINK;
+      } else {
+        rt.rtm_scope = RT_SCOPE_UNIVERSE;
+      }
+      rt.rtm_type = RTN_UNICAST;
+      //RTPROT_STATIC = from administrator's configuration
+      //RTPROT_BOOT = from an automatic process
+      rt.rtm_protocol = RTPROT_BOOT;
+      break;
+
+    default:
+      retval = -EINVAL;
+      goto cleanup;
+  }
+
+  flags |= NLM_F_REQUEST | NLM_F_ACK;
+
+  msg = nlmsg_alloc_rtmsg(type, flags, &rt);
+  if(!msg) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+
+  if(nla_put(msg, RTA_DST, addr_size, destination) < 0) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+  if(gateway != NULL)
+    if(nla_put(msg, RTA_GATEWAY, addr_size, gateway) < 0) {
+      retval = -ENOMEM;
+      goto cleanup;
+    }
+  if(nla_put(msg, RTA_OIF, 4, &ifindex) < 0) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+  if(nla_put(msg, RTA_PRIORITY, 4, &metric) < 0) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+  if(mtu > 0 && change_type != ROUTE_DELETE) {
+    // MTU is inside an RTA_METRICS nested message
+    struct nlattr *metrics = nla_nest_start(msg, RTA_METRICS);
+    if(metrics == NULL) {
+      retval = -ENOMEM;
+      goto cleanup;
+    }
+
+    if(nla_put(msg, RTAX_MTU, 4, &mtu) < 0) {
+      retval = -ENOMEM;
+      goto cleanup;
+    }
+
+    nla_nest_end(msg, metrics);
+  }
+
+  retval = netlink_sendrecv(msg);
+
+cleanup:
+  if(msg)
+    nlmsg_free(msg);
+
+  return retval;
+}
+
+/* function: set_default_ipv6_route
+ * copies the default route on an interface (turns an RA route into a static
+ * route), which is needed to keep the route when forwarding is turned on
+ * device - interface to be the default route
+ */
+void set_default_ipv6_route(const char *device) {
+  struct in6_addr default_6 = IN6ADDR_ANY_INIT;
+  struct default_route_data default_route;
+  int status;
+  void *gateway = NULL;
+
+  memset(&default_route, '\0', sizeof(default_route));
+  default_route.request_family = AF_INET6;
+  default_route.request_interface_id = if_nametoindex(device);
+  if(default_route.request_interface_id == 0) {
+    logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route failed: no interface %s found", device);
+    exit(1);
+  }
+
+  status = get_default_route(&default_route);
+  if(status < 0) {
+    logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: returned %d", status);
+    exit(1);
+  }
+
+  if(!default_route.reply_found_route) {
+    logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: no default route found for %s", device);
+    exit(1);
+  }
+
+  if(default_route.reply_has_gateway) {
+    gateway = &default_route.reply_gateway.ip6;
+  }
+
+  if((status = if_route(device, AF_INET6, &default_6, 0, gateway, 1, 0, ROUTE_REPLACE)) < 0) {
+    if(status == -EEXIST) {
+      logmsg(ANDROID_LOG_WARN,"set_default_ipv6_route/if_route failed due to the route already existing");
+    } else {
+      logmsg(ANDROID_LOG_FATAL,"set_default_ipv6_route/if_route failed: %s",strerror(-status));
+      exit(1);
+    }
+  }
+}