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/getroute.c b/getroute.c
new file mode 100644
index 0000000..5f9475e
--- /dev/null
+++ b/getroute.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012 Daniel Drown
+ *
+ * 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.
+ *
+ * getroute.c - get an ip route
+ */
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <arpa/inet.h>
+
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include "getroute.h"
+#include "netlink_callbacks.h"
+#include "netlink_msg.h"
+
+/* function: get_default_route_cb
+ * finds the default route with the request family and out interface and saves the gateway
+ * msg  - netlink message
+ * data - (struct default_route_data) requested filters and response storage
+ */
+static int get_default_route_cb(struct nl_msg *msg, void *data) {
+  struct rtmsg *rt_p;
+  struct rtattr *rta_p;
+  int rta_len;
+  struct default_route_data *default_route = data;
+  union anyip *this_gateway = NULL;
+  ssize_t this_gateway_size;
+  int this_interface_id = -1;
+
+  if(default_route->reply_found_route) { // we already found our route
+    return NL_OK;
+  }
+
+  rt_p = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));
+  if(rt_p->rtm_dst_len != 0) { // not a default route
+    return NL_OK;
+  }
+  if((rt_p->rtm_family != default_route->request_family) || (rt_p->rtm_table != RT_TABLE_MAIN)) { // not a route we care about
+    return NL_OK;
+  }
+
+  rta_p = (struct rtattr *)RTM_RTA(rt_p);
+  rta_len = RTM_PAYLOAD(nlmsg_hdr(msg));
+  for(; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) {
+    switch(rta_p->rta_type) {
+      case RTA_GATEWAY:
+        this_gateway = RTA_DATA(rta_p);
+        this_gateway_size = RTA_PAYLOAD(rta_p);
+        break;
+      case RTA_OIF:
+        this_interface_id = *(int *)RTA_DATA(rta_p);
+        break;
+      default:
+        break;
+    }
+  }
+
+  if(this_interface_id == default_route->request_interface_id) {
+    default_route->reply_found_route = 1;
+    if(this_gateway != NULL) {
+      memcpy(&default_route->reply_gateway, this_gateway, this_gateway_size);
+      default_route->reply_has_gateway = 1;
+    } else {
+      default_route->reply_has_gateway = 0;
+    }
+  }
+  return NL_OK;
+}
+
+/* function: error_handler
+ * error callback for get_default_route
+ * nla  - where the message came from
+ * err  - netlink message
+ * arg  - (int *) storage for the error number
+ */
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
+  int *retval = arg;
+  if(err->error < 0) { // error_handler called even on no error (NLMSG_ERROR reply type used)
+    *retval = err->error;
+  }
+  return NL_OK;
+}
+
+/* function: get_default_route
+ * finds the first default route with the given family and interface, returns the gateway (if it exists) in the struct
+ * default_route - requested family and interface, and response storage
+ */
+int get_default_route(struct default_route_data *default_route) {
+  struct rtmsg msg;
+  struct nl_cb *callbacks = NULL;
+  struct nl_msg *nlmsg = NULL;
+  int retval = 0;
+
+  default_route->reply_has_gateway = 0;
+  default_route->reply_found_route = 0;
+
+  memset(&msg,'\0',sizeof(msg));
+  msg.rtm_family = default_route->request_family;
+  msg.rtm_table = RT_TABLE_MAIN;
+  msg.rtm_protocol = RTPROT_KERNEL;
+  msg.rtm_scope = RT_SCOPE_UNIVERSE;
+
+  callbacks = nl_cb_alloc(NL_CB_DEFAULT);
+  if(!callbacks) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+  // get_default_route_cb sets the response fields in default_route
+  nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, get_default_route_cb, default_route);
+  nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &retval);
+
+  nlmsg = nlmsg_alloc_rtmsg(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ROOT, &msg);
+  if(!nlmsg) {
+    retval = -ENOMEM;
+    goto cleanup;
+  }
+  send_netlink_msg(nlmsg, callbacks);
+
+cleanup:
+  if(callbacks)
+    nl_cb_put(callbacks);
+  if(nlmsg)
+    nlmsg_free(nlmsg);
+
+  return retval;
+}