Initialize clatd for tizen 68/214468/1 accepted/tizen/unified/20190924.084321 submit/tizen/20190924.071950
authorSeonah Moon <seonah1.moon@samsung.com>
Mon, 23 Sep 2019 06:42:29 +0000 (15:42 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Mon, 23 Sep 2019 07:18:40 +0000 (16:18 +0900)
Change-Id: I51fe4ce27daa75f9e011d3273acfdb12ffa94f6b

59 files changed:
Android.mk [deleted file]
CMakeLists.txt [new file with mode: 0644]
clatd.c [deleted file]
clatd.manifest [new file with mode: 0644]
clatd_microbenchmark.c [deleted file]
clatd_test.cpp
config.c [deleted file]
dns64.c [deleted file]
include/checksum.h [moved from checksum.h with 100% similarity]
include/clatd.h [new file with mode: 0644]
include/common.h [moved from clatd.h with 56% similarity]
include/config.h [moved from config.h with 95% similarity]
include/config_utils.h [new file with mode: 0644]
include/dbus.h [new file with mode: 0644]
include/debug.h [moved from debug.h with 100% similarity]
include/dns64.h [moved from dns64.h with 100% similarity]
include/dump.h [moved from dump.h with 63% similarity]
include/getaddr.h [moved from getaddr.h with 100% similarity]
include/icmp.h [moved from icmp.h with 100% similarity]
include/ifc.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/logging.h [moved from logging.h with 100% similarity]
include/mtu.h [moved from mtu.h with 100% similarity]
include/netlink_callbacks.h [moved from netlink_callbacks.h with 100% similarity]
include/netlink_msg.h [moved from netlink_msg.h with 86% similarity]
include/ring.h [moved from ring.h with 100% similarity]
include/setif.h [moved from setif.h with 84% similarity]
include/specific_dns.h [new file with mode: 0644]
include/translate.h [moved from translate.h with 88% similarity]
include/tun.h [moved from tun.h with 60% similarity]
introspection/control.xml [new file with mode: 0644]
logging.c [deleted file]
packaging/clat.spec [new file with mode: 0644]
packaging/com.samsung.clatd.conf [new file with mode: 0644]
packaging/com.samsung.clatd.service [new file with mode: 0644]
res/clatd.conf [moved from clatd.conf with 100% similarity]
src/checksum.c [moved from checksum.c with 57% similarity]
src/clatd.c [new file with mode: 0644]
src/clatd_microbenchmark.c [new file with mode: 0644]
src/config.c [new file with mode: 0644]
src/config_utils.c [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/dns64.c [new file with mode: 0644]
src/dump.c [moved from dump.c with 51% similarity]
src/getaddr.c [moved from getaddr.c with 76% similarity]
src/icmp.c [moved from icmp.c with 85% similarity]
src/ifc_utils.c [new file with mode: 0644]
src/ipv4.c [moved from ipv4.c with 73% similarity]
src/ipv6.c [moved from ipv6.c with 82% similarity]
src/load_file.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/mtu.c [moved from mtu.c with 87% similarity]
src/netlink_callbacks.c [moved from netlink_callbacks.c with 85% similarity]
src/netlink_msg.c [moved from netlink_msg.c with 86% similarity]
src/ring.c [moved from ring.c with 62% similarity]
src/setif.c [moved from setif.c with 57% similarity]
src/translate.c [moved from translate.c with 82% similarity]
src/tun.c [moved from tun.c with 54% similarity]
src_ext/specific_dns.c [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
deleted file mode 100644 (file)
index 5f2fe63..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c
-
-LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
-LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include
-LOCAL_STATIC_LIBRARIES := libnl
-LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils
-
-# The clat daemon.
-LOCAL_MODULE := clatd
-
-include $(BUILD_EXECUTABLE)
-
-
-# The configuration file.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := clatd.conf
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-
-include $(BUILD_PREBUILT)
-
-
-# Unit tests.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := clatd_test
-LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
-LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c
-LOCAL_MODULE_TAGS := eng tests
-LOCAL_SHARED_LIBRARIES := liblog
-
-include $(BUILD_NATIVE_TEST)
-
-# Microbenchmark.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := clatd_microbenchmark
-LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter
-LOCAL_SRC_FILES := clatd_microbenchmark.c checksum.c tun.c
-LOCAL_MODULE_TAGS := eng tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..519b63b
--- /dev/null
@@ -0,0 +1,86 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(clatd C)
+SET(PACKAGE ${PROJECT_NAME})
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(BINDIR "${PREFIX}/bin")
+SET(DATADIR "${PREFIX}/share")
+SET(LIBDIR "${PREFIX}/${LIB_PATH}")
+SET(INTROSPECTION "${CMAKE_SOURCE_DIR}/introspection")
+
+SET(SRCS
+       src/main.c
+       src/dbus.c
+       src/checksum.c
+       src/clatd.c
+       src/config.c
+       src/config_utils.c
+       src/dns64.c
+       src/dump.c
+       src/getaddr.c
+       src/icmp.c
+       src/ifc_utils.c
+       src/ipv4.c
+       src/ipv6.c
+       src/load_file.c
+       src/mtu.c
+       src/netlink_callbacks.c
+       src/netlink_msg.c
+       src/ring.c
+       src/setif.c
+       src/translate.c
+       src/tun.c
+       src_ext/specific_dns.c
+)
+
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+       SET(CMAKE_BUILD_TYPE "Release")
+ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
+
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(pkgs REQUIRED
+       dlog
+       gio-2.0
+       glib-2.0
+       gio-unix-2.0
+       libnl-2.0
+       libcares)
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -DTIZEN_EXT -fvisibility=hidden -fPIC")
+SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_C_FLAGS "-I${CMAKE_SOURCE_DIR} -I${CMAKE_SOURCE_DIR}/include ${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -D_GNU_SOURCE")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_C_FLAGS_RELEASE "-O2")
+
+FIND_PROGRAM(UNAME NAMES uname)
+EXEC_PROGRAM("${UNAME}" ARGS "-m"
+OUTPUT_VARIABLE "ARCH")
+IF("${ARCH}" STREQUAL "arm")
+       ADD_DEFINITIONS("-DTARGET_ARM")
+       MESSAGE("add -DTARGET_ARM")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPACKAGE=\"${PACKAGE}\"")
+ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"")
+
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+ADD_CUSTOM_COMMAND(
+       WORKING_DIRECTORY
+       OUTPUT ${CMAKE_BINARY_DIR}/generated-code.c
+       COMMAND gdbus-codegen --interface-prefix com.samsung.clatd.
+       --generate-c-code generated-code
+       --c-namespace Clatd
+       --c-generate-object-manager
+       --generate-docbook generated-docs
+       ${INTROSPECTION}/control.xml
+       COMMENT "Generating GDBus .c/.h")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS} ${CMAKE_SOURCE_DIR}/generated-code.c)
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} "-ldl")
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR})
diff --git a/clatd.c b/clatd.c
deleted file mode 100644 (file)
index faeb679..0000000
--- a/clatd.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * 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.
- *
- * clatd.c - tun interface setup and main event loop
- */
-#include <poll.h>
-#include <signal.h>
-#include <time.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <fcntl.h>
-
-#include <sys/capability.h>
-#include <sys/uio.h>
-#include <linux/filter.h>
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
-#include <linux/if_packet.h>
-#include <net/if.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "translate.h"
-#include "clatd.h"
-#include "config.h"
-#include "logging.h"
-#include "resolv_netid.h"
-#include "setif.h"
-#include "mtu.h"
-#include "getaddr.h"
-#include "dump.h"
-#include "tun.h"
-#include "ring.h"
-
-#define DEVICEPREFIX "v4-"
-
-/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */
-#define MTU_DELTA 28
-
-volatile sig_atomic_t running = 1;
-
-/* function: stop_loop
- * signal handler: stop the event loop
- */
-void stop_loop() {
-  running = 0;
-}
-
-/* function: configure_packet_socket
- * Binds the packet socket and attaches the receive filter to it.
- * sock - the socket to configure
- */
-int configure_packet_socket(int sock) {
-  struct sockaddr_ll sll = {
-    .sll_family   = AF_PACKET,
-    .sll_protocol = htons(ETH_P_IPV6),
-    .sll_ifindex  = if_nametoindex((char *) &Global_Clatd_Config.default_pdp_interface),
-    .sll_pkttype  = PACKET_OTHERHOST,  // The 464xlat IPv6 address is not assigned to the kernel.
-  };
-  if (bind(sock, (struct sockaddr *) &sll, sizeof(sll))) {
-    logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno));
-    return 0;
-  }
-
-  uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32;
-  struct sock_filter filter_code[] = {
-    // Load the first four bytes of the IPv6 destination address (starts 24 bytes in).
-    // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads
-    // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it
-    // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other
-    // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet).
-    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  24),
-    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[0]), 0, 7),
-    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  28),
-    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[1]), 0, 5),
-    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  32),
-    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[2]), 0, 3),
-    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  36),
-    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[3]), 0, 1),
-    BPF_STMT(BPF_RET | BPF_K,              PACKETLEN),
-    BPF_STMT(BPF_RET | BPF_K, 0)
-  };
-  struct sock_fprog filter = {
-    sizeof(filter_code) / sizeof(filter_code[0]),
-    filter_code
-  };
-
-  if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
-    logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno));
-    return 0;
-  }
-
-  return 1;
-}
-
-/* function: configure_tun_ip
- * configures the ipv4 and ipv6 addresses on the tunnel interface
- * tunnel - tun device data
- */
-void configure_tun_ip(const struct tun_data *tunnel) {
-  int status;
-
-  // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically,
-  // there is a race here - if another clatd calls config_select_ipv4_address after we do, but
-  // before we call add_address, it can end up having the same IP address as we do. But the time
-  // window in which this can happen is extremely small, and even if we end up with a duplicate
-  // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go
-  // down.
-  in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet,
-                                                   Global_Clatd_Config.ipv4_local_prefixlen);
-  if (localaddr == INADDR_NONE) {
-    logmsg(ANDROID_LOG_FATAL,"No free IPv4 address in %s/%d",
-           inet_ntoa(Global_Clatd_Config.ipv4_local_subnet),
-           Global_Clatd_Config.ipv4_local_prefixlen);
-    exit(1);
-  }
-  Global_Clatd_Config.ipv4_local_subnet.s_addr = localaddr;
-
-  // Configure the interface before bringing it up. As soon as we bring the interface up, the
-  // framework will be notified and will assume the interface's configuration has been finalized.
-  status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet,
-      32, &Global_Clatd_Config.ipv4_local_subnet);
-  if(status < 0) {
-    logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(4) failed: %s",strerror(-status));
-    exit(1);
-  }
-
-  char addrstr[INET_ADDRSTRLEN];
-  inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr));
-  logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4);
-
-  if((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) {
-    logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(4) failed: %s",strerror(-status));
-    exit(1);
-  }
-}
-
-/* function: drop_root
- * drops root privs but keeps the needed capability
- */
-void drop_root() {
-  gid_t groups[] = { AID_INET, AID_VPN };
-  if(setgroups(sizeof(groups)/sizeof(groups[0]), groups) < 0) {
-    logmsg(ANDROID_LOG_FATAL,"drop_root/setgroups failed: %s",strerror(errno));
-    exit(1);
-  }
-
-  prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
-
-  if(setgid(AID_CLAT) < 0) {
-    logmsg(ANDROID_LOG_FATAL,"drop_root/setgid failed: %s",strerror(errno));
-    exit(1);
-  }
-  if(setuid(AID_CLAT) < 0) {
-    logmsg(ANDROID_LOG_FATAL,"drop_root/setuid failed: %s",strerror(errno));
-    exit(1);
-  }
-
-  struct __user_cap_header_struct header;
-  struct __user_cap_data_struct cap;
-  memset(&header, 0, sizeof(header));
-  memset(&cap, 0, sizeof(cap));
-
-  header.version = _LINUX_CAPABILITY_VERSION;
-  header.pid = 0; // 0 = change myself
-  cap.effective = cap.permitted = (1 << CAP_NET_ADMIN);
-
-  if(capset(&header, &cap) < 0) {
-    logmsg(ANDROID_LOG_FATAL,"drop_root/capset failed: %s",strerror(errno));
-    exit(1);
-  }
-}
-
-/* function: open_sockets
- * opens a packet socket to receive IPv6 packets and a raw socket to send them
- * tunnel - tun device data
- * mark - the socket mark to use for the sending raw socket
- */
-void open_sockets(struct tun_data *tunnel, uint32_t mark) {
-  int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
-  if (rawsock < 0) {
-    logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  int off = 0;
-  if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) {
-    logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno));
-  }
-  if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
-    logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno));
-  }
-
-  tunnel->write_fd6 = rawsock;
-
-  tunnel->read_fd6 = ring_create(tunnel);
-  if (tunnel->read_fd6 < 0) {
-    exit(1);
-  }
-}
-
-/* function: update_clat_ipv6_address
- * picks the clat IPv6 address and configures packet translation to use it.
- * tunnel - tun device data
- * interface - uplink interface name
- * returns: 1 on success, 0 on failure
- */
-int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) {
-  union anyip *interface_ip;
-  char addrstr[INET6_ADDRSTRLEN];
-
-  // TODO: check that the prefix length is /64.
-  interface_ip = getinterface_ip(interface, AF_INET6);
-  if (!interface_ip) {
-    logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface);
-    return 0;
-  }
-
-  // If our prefix hasn't changed, do nothing. (If this is the first time we configure an IPv6
-  // address, Global_Clatd_Config.ipv6_local_subnet will be ::, which won't match our new prefix.)
-  if (ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) {
-    free(interface_ip);
-    return 1;
-  }
-
-  // Generate an interface ID.
-  config_generate_local_ipv6_subnet(&interface_ip->ip6);
-  inet_ntop(AF_INET6, &interface_ip->ip6, addrstr, sizeof(addrstr));
-
-  if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)) {
-    // Startup.
-    logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface);
-  } else {
-    // Prefix change.
-    char from_addr[INET6_ADDRSTRLEN];
-    inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, from_addr, sizeof(from_addr));
-    logmsg(ANDROID_LOG_INFO, "clat IPv6 address changed from %s to %s", from_addr, addrstr);
-    del_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
-  }
-
-  // Start translating packets to the new prefix.
-  Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6;
-  add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface);
-  free(interface_ip);
-
-  // Update our packet socket filter to reflect the new 464xlat IP address.
-  if (!configure_packet_socket(tunnel->read_fd6)) {
-      // Things aren't going to work. Bail out and hope we have better luck next time.
-      // We don't log an error here because configure_packet_socket has already done so.
-      exit(1);
-  }
-
-  return 1;
-}
-
-/* function: configure_interface
- * reads the configuration and applies it to the interface
- * uplink_interface - network interface to use to reach the ipv6 internet
- * plat_prefix      - PLAT prefix to use
- * tunnel           - tun device data
- * net_id           - NetID to use, NETID_UNSET indicates use of default network
- */
-void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel, unsigned net_id) {
-  int error;
-
-  if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) {
-    logmsg(ANDROID_LOG_FATAL,"read_config failed");
-    exit(1);
-  }
-
-  if(Global_Clatd_Config.mtu > MAXMTU) {
-    logmsg(ANDROID_LOG_WARN,"Max MTU is %d, requested %d", MAXMTU, Global_Clatd_Config.mtu);
-    Global_Clatd_Config.mtu = MAXMTU;
-  }
-  if(Global_Clatd_Config.mtu <= 0) {
-    Global_Clatd_Config.mtu = getifmtu(Global_Clatd_Config.default_pdp_interface);
-    logmsg(ANDROID_LOG_WARN,"ifmtu=%d",Global_Clatd_Config.mtu);
-  }
-  if(Global_Clatd_Config.mtu < 1280) {
-    logmsg(ANDROID_LOG_WARN,"mtu too small = %d", Global_Clatd_Config.mtu);
-    Global_Clatd_Config.mtu = 1280;
-  }
-
-  if(Global_Clatd_Config.ipv4mtu <= 0 ||
-     Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) {
-    Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA;
-    logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu);
-  }
-
-  error = tun_alloc(tunnel->device4, tunnel->fd4);
-  if(error < 0) {
-    logmsg(ANDROID_LOG_FATAL,"tun_alloc/4 failed: %s",strerror(errno));
-    exit(1);
-  }
-
-  error = set_nonblocking(tunnel->fd4);
-  if (error < 0) {
-    logmsg(ANDROID_LOG_FATAL, "set_nonblocking failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  configure_tun_ip(tunnel);
-}
-
-/* function: read_packet
- * reads a packet from the tunnel fd and translates it
- * read_fd  - file descriptor to read original packet from
- * write_fd - file descriptor to write translated packet to
- * to_ipv6  - whether the packet is to be translated to ipv6 or ipv4
- */
-void read_packet(int read_fd, int write_fd, int to_ipv6) {
-  ssize_t readlen;
-  uint8_t buf[PACKETLEN], *packet;
-
-  readlen = read(read_fd, buf, PACKETLEN);
-
-  if(readlen < 0) {
-    if (errno != EAGAIN) {
-      logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno));
-    }
-    return;
-  } else if(readlen == 0) {
-    logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed");
-    running = 0;
-    return;
-  }
-
-  struct tun_pi *tun_header = (struct tun_pi *) buf;
-  if (readlen < (ssize_t) sizeof(*tun_header)) {
-    logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen);
-    return;
-  }
-
-  uint16_t proto = ntohs(tun_header->proto);
-  if (proto != ETH_P_IP) {
-    logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto);
-    return;
-  }
-
-  if(tun_header->flags != 0) {
-    logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags);
-  }
-
-  packet = (uint8_t *) (tun_header + 1);
-  readlen -= sizeof(*tun_header);
-  translate_packet(write_fd, to_ipv6, packet, readlen);
-}
-
-/* function: event_loop
- * reads packets from the tun network interface and passes them down the stack
- * tunnel - tun device data
- */
-void event_loop(struct tun_data *tunnel) {
-  time_t last_interface_poll;
-  struct pollfd wait_fd[] = {
-    { tunnel->read_fd6, POLLIN, 0 },
-    { tunnel->fd4, POLLIN, 0 },
-  };
-
-  // start the poll timer
-  last_interface_poll = time(NULL);
-
-  while(running) {
-    if(poll(wait_fd, 2, NO_TRAFFIC_INTERFACE_POLL_FREQUENCY*1000) == -1) {
-      if(errno != EINTR) {
-        logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno));
-      }
-    } else {
-      // Call read_packet if the socket has data to be read, but also if an
-      // error is waiting. If we don't call read() after getting POLLERR, a
-      // subsequent poll() will return immediately with POLLERR again,
-      // causing this code to spin in a loop. Calling read() will clear the
-      // socket error flag instead.
-      if (wait_fd[0].revents) {
-        ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */);
-      }
-      if (wait_fd[1].revents) {
-        read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */);
-      }
-    }
-
-    time_t now = time(NULL);
-    if(last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) {
-      update_clat_ipv6_address(tunnel, Global_Clatd_Config.default_pdp_interface);
-      last_interface_poll = now;
-    }
-  }
-}
-
-/* function: print_help
- * in case the user is running this on the command line
- */
-void print_help() {
-  printf("android-clat arguments:\n");
-  printf("-i [uplink interface]\n");
-  printf("-p [plat prefix]\n");
-  printf("-n [NetId]\n");
-  printf("-m [socket mark]\n");
-}
-
-/* function: parse_unsigned
- * parses a string as a decimal/hex/octal unsigned integer
- * str - the string to parse
- * out - the unsigned integer to write to, gets clobbered on failure
- */
-int parse_unsigned(const char *str, unsigned *out) {
-    char *end_ptr;
-    *out = strtoul(str, &end_ptr, 0);
-    return *str && !*end_ptr;
-}
-
-/* function: main
- * allocate and setup the tun device, then run the event loop
- */
-int main(int argc, char **argv) {
-  struct tun_data tunnel;
-  int opt;
-  char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL;
-  unsigned net_id = NETID_UNSET;
-  uint32_t mark = MARK_UNSET;
-  unsigned len;
-
-  while((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) {
-    switch(opt) {
-      case 'i':
-        uplink_interface = optarg;
-        break;
-      case 'p':
-        plat_prefix = optarg;
-        break;
-      case 'n':
-        net_id_str = optarg;
-        break;
-      case 'm':
-        mark_str = optarg;
-        break;
-      case 'h':
-        print_help();
-        exit(0);
-      default:
-        logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char) optopt);
-        exit(1);
-    }
-  }
-
-  if(uplink_interface == NULL) {
-    logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
-    exit(1);
-  }
-
-  if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) {
-    logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str);
-    exit(1);
-  }
-
-  if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) {
-    logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str);
-    exit(1);
-  }
-
-  len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface);
-  if (len >= sizeof(tunnel.device4)) {
-    logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4);
-    exit(1);
-  }
-
-  logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s",
-         CLATD_VERSION, uplink_interface,
-         net_id_str ? net_id_str : "(none)",
-         mark_str ? mark_str : "(none)");
-
-  // open our raw sockets before dropping privs
-  open_sockets(&tunnel, mark);
-
-  // run under a regular user
-  drop_root();
-
-  // we can create tun devices as non-root because we're in the VPN group.
-  tunnel.fd4 = tun_open();
-  if(tunnel.fd4 < 0) {
-    logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  // When run from netd, the environment variable ANDROID_DNS_MODE is set to
-  // "local", but that only works for the netd process itself.
-  unsetenv("ANDROID_DNS_MODE");
-
-  configure_interface(uplink_interface, plat_prefix, &tunnel, net_id);
-
-  update_clat_ipv6_address(&tunnel, uplink_interface);
-
-  // Loop until someone sends us a signal or brings down the tun interface.
-  if(signal(SIGTERM, stop_loop) == SIG_ERR) {
-    logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  event_loop(&tunnel);
-
-  logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface);
-  del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
-
-  return 0;
-}
diff --git a/clatd.manifest b/clatd.manifest
new file mode 100644 (file)
index 0000000..97e8c31
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c
deleted file mode 100644 (file)
index fed3100..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 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.
- *
- * clatd_microbenchmark.c - micro-benchmark for clatd tun send path
- *
- * Run with:
- *
- * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark
- * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark
- *
- */
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/udp.h>
-#include <arpa/inet.h>
-#include <linux/if.h>
-#include <linux/if_tun.h>
-
-#include "checksum.h"
-#include "tun.h"
-
-#define DEVICENAME "clat4"
-
-#define PORT 51339
-#define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr))
-#define NUMPACKETS 1000000
-#define SEC_TO_NANOSEC (1000 * 1000 * 1000)
-
-void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) {
-    sin->sin_family = AF_INET;
-    sin->sin_port = 0;
-    sin->sin_addr.s_addr = inet_addr(addr);
-}
-
-void die(const char *str) {
-    perror(str);
-    exit(1);
-}
-
-int setup_tun() {
-    int fd = tun_open();
-    if (fd == -1) die("tun_open");
-
-    char dev[IFNAMSIZ] = DEVICENAME;
-    int ret = tun_alloc(dev, fd);
-    if (ret == -1) die("tun_alloc");
-    struct ifreq ifr = {
-        .ifr_name = DEVICENAME,
-    };
-
-    int s = socket(AF_INET, SOCK_DGRAM, 0);
-    init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "192.0.0.4");
-    if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR");
-    init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "255.255.255.248");
-    if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK");
-    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS");
-    ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
-    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS");
-    return fd;
-}
-
-int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) {
-    struct tun_pi tun = { 0, htons(ETH_P_IP) };
-    struct udphdr udp = {
-        .source = htons(1234),
-        .dest = htons(PORT),
-        .len = htons(len + sizeof(udp)),
-        .check = 0,
-    };
-    struct iphdr ip = {
-        .version = 4,
-        .ihl = 5,
-        .tot_len = htons(len + sizeof(ip) + sizeof(udp)),
-        .frag_off = htons(IP_DF),
-        .ttl = 55,
-        .protocol = IPPROTO_UDP,
-        .saddr = htonl(0xc0000006),  // 192.0.0.6
-        .daddr = htonl(0xc0000004),  // 192.0.0.4
-    };
-    clat_packet out = {
-        { &tun, sizeof(tun) },  // tun header
-        { &ip, sizeof(ip) },    // IP header
-        { NULL, 0 },            // Fragment header
-        { &udp, sizeof(udp) },  // Transport header
-        { NULL, 0 },            // ICMP error IP header
-        { NULL, 0 },            // ICMP error fragment header
-        { NULL, 0 },            // ICMP error transport header
-        { payload, len },       // Payload
-    };
-
-    ip.check = ip_checksum(&ip, sizeof(ip));
-
-    uint32_t sum;
-    sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len));
-    sum = ip_checksum_add(sum, &udp, sizeof(udp));
-    sum += payload_checksum;
-    udp.check = ip_checksum_finish(sum);
-
-    return send_tun(fd, out, sizeof(out) / sizeof(out[0]));
-}
-
-double timedelta(const struct timespec tv1, const struct timespec tv2) {
-    struct timespec end = tv2;
-    if (end.tv_nsec < tv1.tv_nsec) {
-        end.tv_sec -= 1;
-        end.tv_nsec += SEC_TO_NANOSEC;
-    }
-    double seconds = (end.tv_sec - tv1.tv_sec);
-    seconds += (((double) (end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC);
-    return seconds;
-}
-
-void benchmark(const char *name, int fd, int s, int num, int do_read,
-               uint8_t payload[], int len, uint32_t payload_sum) {
-    int i;
-    char buf[4096];
-    struct timespec tv1, tv2;
-    int write_err = 0, read_err = 0;
-    clock_gettime(CLOCK_MONOTONIC, &tv1);
-    for (i = 0; i < num; i++) {
-        if (send_packet(fd, payload, len, payload_sum) == -1) write_err++;
-        if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) {
-            read_err++;
-            if (errno == ETIMEDOUT) {
-                printf("Timed out after %d packets!\n", i);
-                break;
-            }
-        }
-    }
-    clock_gettime(CLOCK_MONOTONIC, &tv2);
-    double seconds = timedelta(tv1, tv2);
-    int pps = (int) (i / seconds);
-    double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds);
-    printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps);
-    printf("read err %d (%.2f%%), write err %d (%.2f%%)\n",
-           read_err, (float) read_err / i * 100,
-           write_err, (float) write_err / i * 100);
-}
-
-int open_socket() {
-    int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
-
-    int on = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR");
-
-    struct timeval tv = { 1, 0 };
-    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO");
-
-    struct sockaddr_in addr = {
-        .sin_family = AF_INET,
-        .sin_port = ntohs(PORT),
-        .sin_addr = { INADDR_ANY }
-    };
-    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) die ("bind");
-
-   return sock;
-}
-
-int main() {
-    int fd = setup_tun();
-    int sock = open_socket();
-
-    int i;
-    uint8_t payload[PAYLOADSIZE];
-    for (i = 0; i < (int) sizeof(payload); i++) {
-        payload[i] = (uint8_t) i;
-    }
-    uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload));
-
-    // Check things are working.
-    char buf[4096];
-    if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet");
-    if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom");
-
-    benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum);
-    close(fd);
-
-    fd = setup_tun();
-    set_nonblocking(fd);
-    benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum);
-    close(fd);
-
-    fd = setup_tun();
-    set_nonblocking(fd);
-    benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum);
-    close(fd);
-
-    return 0;
-}
index 7e218f0..5ed2246 100644 (file)
 
 #include <iostream>
 
-#include <stdio.h>
 #include <arpa/inet.h>
 #include <netinet/in6.h>
+#include <stdio.h>
 #include <sys/uio.h>
 
 #include <gtest/gtest.h>
 
+//#include "netutils/ifc.h"
+#include "tun_interface.h"
+
 extern "C" {
+#include "clatd.h"
+#include "config.h"
+#include "getaddr.h"
 #include "checksum.h"
 #include "translate.h"
-#include "config.h"
-#include "clatd.h"
+#include "tun.h"
 }
 
 // For convenience.
 #define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0])
 
+using android::net::TunInterface;
+
 // Default translation parameters.
-static const char kIPv4LocalAddr[] = "192.0.0.4";
-static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464";
+static const char kIPv4LocalAddr[]  = "192.0.0.4";
+static const char kIPv6LocalAddr[]  = "2001:db8:0:b11::464";
 static const char kIPv6PlatSubnet[] = "64:ff9b::";
 
+// clang-format off
 // Test packet portions. Defined as macros because it's easy to concatenate them to make packets.
 #define IPV4_HEADER(p, c1, c2) \
     0x45, 0x00,    0,   41,  /* Version=4, IHL=5, ToS=0x80, len=41 */     \
@@ -164,6 +172,7 @@ static const uint8_t kReassembledIPv4[] = {
     0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
     0x01
 };
+// clang-format on
 
 // Expected checksums.
 static const uint32_t kUdpPartialChecksum     = 0xd5c8;
@@ -185,9 +194,9 @@ int is_ipv6_fragment(struct ip6_hdr *ip6, size_t len) {
   if (ip6->ip6_nxt != IPPROTO_FRAGMENT) {
     return 0;
   }
-  struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1);
+  struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1);
   return len >= sizeof(*ip6) + sizeof(*frag) &&
-          (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG));
+         (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG));
 }
 
 int ipv4_fragment_offset(struct iphdr *ip) {
@@ -200,50 +209,50 @@ int ipv6_fragment_offset(struct ip6_frag *frag) {
 
 void check_packet(const uint8_t *packet, size_t len, const char *msg) {
   void *payload;
-  size_t payload_length = 0;
+  size_t payload_length    = 0;
   uint32_t pseudo_checksum = 0;
-  uint8_t protocol = 0;
-  int version = ip_version(packet);
+  uint8_t protocol         = 0;
+  int version              = ip_version(packet);
   switch (version) {
     case 4: {
-      struct iphdr *ip = (struct iphdr *) packet;
+      struct iphdr *ip = (struct iphdr *)packet;
       ASSERT_GE(len, sizeof(*ip)) << msg << ": IPv4 packet shorter than IPv4 header\n";
       EXPECT_EQ(5, ip->ihl) << msg << ": Unsupported IP header length\n";
       EXPECT_EQ(len, ntohs(ip->tot_len)) << msg << ": Incorrect IPv4 length\n";
       EXPECT_EQ(0, ip_checksum(ip, sizeof(*ip))) << msg << ": Incorrect IP checksum\n";
       protocol = ip->protocol;
-      payload = ip + 1;
+      payload  = ip + 1;
       if (!is_ipv4_fragment(ip)) {
-        payload_length = len - sizeof(*ip);
+        payload_length  = len - sizeof(*ip);
         pseudo_checksum = ipv4_pseudo_header_checksum(ip, payload_length);
       }
       ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMP)
-          << msg << ": Unsupported IPv4 protocol " << protocol << "\n";
+        << msg << ": Unsupported IPv4 protocol " << protocol << "\n";
       break;
     }
     case 6: {
-      struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
+      struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
       ASSERT_GE(len, sizeof(*ip6)) << msg << ": IPv6 packet shorter than IPv6 header\n";
       EXPECT_EQ(len - sizeof(*ip6), htons(ip6->ip6_plen)) << msg << ": Incorrect IPv6 length\n";
 
       if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
-        struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1);
+        struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1);
         ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag))
-            << msg << ": IPv6 fragment: short fragment header\n";
+          << msg << ": IPv6 fragment: short fragment header\n";
         protocol = frag->ip6f_nxt;
-        payload = frag + 1;
+        payload  = frag + 1;
         // Even though the packet has a Fragment header, it might not be a fragment.
         if (!is_ipv6_fragment(ip6, len)) {
           payload_length = len - sizeof(*ip6) - sizeof(*frag);
         }
       } else {
         // Since there are no extension headers except Fragment, this must be the payload.
-        protocol = ip6->ip6_nxt;
-        payload = ip6 + 1;
+        protocol       = ip6->ip6_nxt;
+        payload        = ip6 + 1;
         payload_length = len - sizeof(*ip6);
       }
       ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6)
-          << msg << ": Unsupported IPv6 next header " << protocol;
+        << msg << ": Unsupported IPv6 next header " << protocol;
       if (payload_length) {
         pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol);
       }
@@ -257,7 +266,7 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) {
   // If we understand the payload, verify the checksum.
   if (payload_length) {
     uint16_t checksum;
-    switch(protocol) {
+    switch (protocol) {
       case IPPROTO_UDP:
       case IPPROTO_TCP:
       case IPPROTO_ICMPV6:
@@ -274,7 +283,7 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) {
   }
 
   if (protocol == IPPROTO_UDP) {
-    struct udphdr *udp = (struct udphdr *) payload;
+    struct udphdr *udp = (struct udphdr *)payload;
     EXPECT_NE(0, udp->check) << msg << ": UDP checksum 0 should be 0xffff";
     // If this is not a fragment, check the UDP length field.
     if (payload_length) {
@@ -285,15 +294,15 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) {
 
 void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets,
                        uint8_t *reassembled, size_t *reassembled_len, const char *msg) {
-  struct iphdr *ip = NULL;
-  struct ip6_hdr *ip6 = NULL;
-  size_t  total_length, pos = 0;
+  struct iphdr *ip    = nullptr;
+  struct ip6_hdr *ip6 = nullptr;
+  size_t total_length, pos = 0;
   uint8_t protocol = 0;
-  uint8_t version = ip_version(fragments[0]);
+  uint8_t version  = ip_version(fragments[0]);
 
   for (int i = 0; i < numpackets; i++) {
     const uint8_t *packet = fragments[i];
-    int len = lengths[i];
+    int len               = lengths[i];
     int headersize, payload_offset;
 
     ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n";
@@ -301,32 +310,32 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu
 
     switch (version) {
       case 4: {
-        struct iphdr *ip_orig = (struct iphdr *) packet;
-        headersize = sizeof(*ip_orig);
+        struct iphdr *ip_orig = (struct iphdr *)packet;
+        headersize            = sizeof(*ip_orig);
         ASSERT_TRUE(is_ipv4_fragment(ip_orig))
-            << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n";
-        ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip): 0))
-            << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n";
+          << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n";
+        ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip) : 0))
+          << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n";
 
-        headersize = sizeof(*ip_orig);
+        headersize     = sizeof(*ip_orig);
         payload_offset = headersize;
         if (pos == 0) {
-          ip = (struct iphdr *) reassembled;
+          ip = (struct iphdr *)reassembled;
         }
         break;
       }
       case 6: {
-        struct ip6_hdr *ip6_orig = (struct ip6_hdr *) packet;
-        struct ip6_frag *frag = (struct ip6_frag *) (ip6_orig + 1);
+        struct ip6_hdr *ip6_orig = (struct ip6_hdr *)packet;
+        struct ip6_frag *frag    = (struct ip6_frag *)(ip6_orig + 1);
         ASSERT_TRUE(is_ipv6_fragment(ip6_orig, len))
-            << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n";
-        ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6): 0))
-            << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n";
+          << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n";
+        ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6) : 0))
+          << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n";
 
-        headersize = sizeof(*ip6_orig);
+        headersize     = sizeof(*ip6_orig);
         payload_offset = sizeof(*ip6_orig) + sizeof(*frag);
         if (pos == 0) {
-          ip6 = (struct ip6_hdr *) reassembled;
+          ip6      = (struct ip6_hdr *)reassembled;
           protocol = frag->ip6f_nxt;
         }
         break;
@@ -337,7 +346,7 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu
 
     // If this is the first fragment, copy the header.
     if (pos == 0) {
-      ASSERT_LT(headersize, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n";
+      ASSERT_LT(headersize, (int)*reassembled_len) << msg << ": Reassembly buffer too small\n";
       memcpy(reassembled, packet, headersize);
       total_length = headersize;
       pos += headersize;
@@ -351,21 +360,20 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu
     pos += payload_length;
   }
 
-
   // Fix up the reassembled headers to reflect fragmentation and length (and IPv4 checksum).
   ASSERT_EQ(total_length, pos) << msg << ": Reassembled packet length incorrect\n";
   if (ip) {
     ip->frag_off &= ~htons(IP_MF);
     ip->tot_len = htons(total_length);
-    ip->check = 0;
-    ip->check = ip_checksum(ip, sizeof(*ip));
+    ip->check   = 0;
+    ip->check   = ip_checksum(ip, sizeof(*ip));
     ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n";
   }
   if (ip6) {
-    ip6->ip6_nxt = protocol;
+    ip6->ip6_nxt  = protocol;
     ip6->ip6_plen = htons(total_length - sizeof(*ip6));
     ASSERT_FALSE(is_ipv6_fragment(ip6, ip6->ip6_plen))
-        << msg << ": reassembled IPv6 packet is a fragment!\n";
+      << msg << ": reassembled IPv6 packet is a fragment!\n";
   }
 
   *reassembled_len = total_length;
@@ -379,12 +387,12 @@ void check_data_matches(const void *expected, const void *actual, size_t len, co
     unsigned pos = 0;
     for (unsigned i = 0; i < len; i++) {
       if (i % 20 == 0) {
-        sprintf(expected_hexdump + pos, "\n   ");
-        sprintf(actual_hexdump + pos, "\n   ");
+        snprintf(expected_hexdump + pos, hexdump_len - pos, "\n   ");
+        snprintf(actual_hexdump + pos, hexdump_len - pos, "\n   ");
         pos += 4;
       }
-      sprintf(expected_hexdump + pos, " %02x", ((uint8_t *) expected)[i]);
-      sprintf(actual_hexdump + pos, " %02x", ((uint8_t *) actual)[i]);
+      snprintf(expected_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)expected)[i]);
+      snprintf(actual_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)actual)[i]);
       pos += 3;
     }
     FAIL() << msg << ": Data doesn't match"
@@ -393,27 +401,27 @@ void check_data_matches(const void *expected, const void *actual, size_t len, co
   }
 }
 
-void fix_udp_checksum(uint8_tpacket) {
+void fix_udp_checksum(uint8_t *packet) {
   uint32_t pseudo_checksum;
   uint8_t version = ip_version(packet);
   struct udphdr *udp;
   switch (version) {
     case 4: {
-      struct iphdr *ip = (struct iphdr *) packet;
-      udp = (struct udphdr *) (ip + 1);
-      pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len));
+      struct iphdr *ip = (struct iphdr *)packet;
+      udp              = (struct udphdr *)(ip + 1);
+      pseudo_checksum  = ipv4_pseudo_header_checksum(ip, ntohs(udp->len));
       break;
     }
     case 6: {
-      struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
-      udp = (struct udphdr *) (ip6 + 1);
-      pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP);
+      struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
+      udp                 = (struct udphdr *)(ip6 + 1);
+      pseudo_checksum     = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP);
       break;
     }
     default:
       FAIL() << "unsupported IP version" << version << "\n";
       return;
-    }
+  }
 
   udp->check = 0;
   udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len)));
@@ -422,9 +430,7 @@ void fix_udp_checksum(uint8_t* packet) {
 // Testing stub for send_rawv6. The real version uses sendmsg() with a
 // destination IPv6 address, and attempting to call that on our test socketpair
 // fd results in EINVAL.
-extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) {
-    writev(fd, out, iov_len);
-}
+extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { writev(fd, out, iov_len); }
 
 void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen,
                          const char *msg) {
@@ -443,13 +449,13 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *
   switch (version) {
     case 4:
       expected_proto = htons(ETH_P_IPV6);
-      read_fd = fds[1];
-      write_fd = fds[0];
+      read_fd        = fds[1];
+      write_fd       = fds[0];
       break;
     case 6:
       expected_proto = htons(ETH_P_IP);
-      read_fd = fds[0];
-      write_fd = fds[1];
+      read_fd        = fds[0];
+      write_fd       = fds[1];
       break;
     default:
       FAIL() << msg << ": Unsupported IP version " << version << "\n";
@@ -464,11 +470,12 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *
     struct tun_pi new_tun_header;
     struct iovec iov[] = {
       { &new_tun_header, sizeof(new_tun_header) },
-      { out, *outlen }
+      { out, *outlen },
     };
+
     int len = readv(read_fd, iov, 2);
-    if (len > (int) sizeof(new_tun_header)) {
-      ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n";
+    if (len > (int)sizeof(new_tun_header)) {
+      ASSERT_LT((size_t)len, *outlen) << msg << ": Translated packet buffer too small\n";
       EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n";
       *outlen = len - sizeof(new_tun_header);
       check_packet(out, *outlen, msg);
@@ -483,8 +490,8 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *
   }
 }
 
-void check_translated_packet(const uint8_t *original, size_t original_len,
-                             const uint8_t *expected, size_t expected_len, const char *msg) {
+void check_translated_packet(const uint8_t *original, size_t original_len, const uint8_t *expected,
+                             size_t expected_len, const char *msg) {
   uint8_t translated[MAXMTU];
   size_t translated_len = sizeof(translated);
   do_translate_packet(original, original_len, translated, &translated_len, msg);
@@ -499,8 +506,8 @@ void check_fragment_translation(const uint8_t *original[], const size_t original
     // Check that each of the fragments translates as expected.
     char frag_msg[512];
     snprintf(frag_msg, sizeof(frag_msg), "%s: fragment #%d", msg, i + 1);
-    check_translated_packet(original[i], original_lengths[i],
-                            expected[i], expected_lengths[i], frag_msg);
+    check_translated_packet(original[i], original_lengths[i], expected[i], expected_lengths[i],
+                            frag_msg);
   }
 
   // Sanity check that reassembling the original and translated fragments produces valid packets.
@@ -524,17 +531,17 @@ int get_transport_checksum(const uint8_t *packet) {
   int version = ip_version(packet);
   switch (version) {
     case 4:
-      ip = (struct iphdr *) packet;
+      ip = (struct iphdr *)packet;
       if (is_ipv4_fragment(ip)) {
-          return -1;
+        return -1;
       }
       protocol = ip->protocol;
-      payload = ip + 1;
+      payload  = ip + 1;
       break;
     case 6:
-      ip6 = (struct ip6_hdr *) packet;
+      ip6      = (struct ip6_hdr *)packet;
       protocol = ip6->ip6_nxt;
-      payload = ip6 + 1;
+      payload  = ip6 + 1;
       break;
     default:
       return -1;
@@ -542,10 +549,10 @@ int get_transport_checksum(const uint8_t *packet) {
 
   switch (protocol) {
     case IPPROTO_UDP:
-      return ((struct udphdr *) payload)->check;
+      return ((struct udphdr *)payload)->check;
 
     case IPPROTO_TCP:
-      return ((struct tcphdr *) payload)->check;
+      return ((struct tcphdr *)payload)->check;
 
     case IPPROTO_FRAGMENT:
     default:
@@ -553,19 +560,45 @@ int get_transport_checksum(const uint8_t *packet) {
   }
 }
 
+static tun_data makeTunData() {
+  // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk.
+  return {
+    .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW),
+    .read_fd6  = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)),
+    .fd4       = socket(AF_UNIX, SOCK_DGRAM, 0),
+  };
+}
+
+void freeTunData(tun_data *tunnel) {
+  close(tunnel->write_fd6);
+  close(tunnel->read_fd6);
+  close(tunnel->fd4);
+}
+
 struct clat_config Global_Clatd_Config;
 
 class ClatdTest : public ::testing::Test {
  protected:
+  static TunInterface sTun;
+
   virtual void SetUp() {
     inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet);
     inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet);
-    inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet);
-    Global_Clatd_Config.ipv6_host_id = in6addr_any;
+    memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr));
+    Global_Clatd_Config.ipv6_host_id    = in6addr_any;
     Global_Clatd_Config.use_dynamic_iid = 1;
+    Global_Clatd_Config.default_pdp_interface = const_cast<char *>(sTun.name().c_str());
   }
+
+  // Static because setting up the tun interface takes about 40ms.
+  static void SetUpTestCase() { ASSERT_EQ(0, sTun.init()); }
+
+  // Closing the socket removes the interface and IP addresses.
+  static void TearDownTestCase() { sTun.destroy(); }
 };
 
+TunInterface ClatdTest::sTun;
+
 void expect_ipv6_addr_equal(struct in6_addr *expected, struct in6_addr *actual) {
   if (!IN6_ARE_ADDR_EQUAL(expected, actual)) {
     char expected_str[INET6_ADDRSTRLEN], actual_str[INET6_ADDRSTRLEN];
@@ -597,7 +630,7 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) {
 int count_onebits(const void *data, size_t size) {
   int onebits = 0;
   for (size_t pos = 0; pos < size; pos++) {
-    uint8_t *byte = ((uint8_t*) data) + pos;
+    uint8_t *byte = ((uint8_t *)data) + pos;
     for (int shift = 0; shift < 8; shift++) {
       onebits += (*byte >> shift) & 1;
     }
@@ -611,7 +644,7 @@ TEST_F(ClatdTest, TestCountOnebits) {
   ASSERT_EQ(1, count_onebits(&i, sizeof(i)));
   i <<= 61;
   ASSERT_EQ(1, count_onebits(&i, sizeof(i)));
-  i |= ((uint64_t) 1 << 33);
+  i |= ((uint64_t)1 << 33);
   ASSERT_EQ(2, count_onebits(&i, sizeof(i)));
   i = 0xf1000202020000f0;
   ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i)));
@@ -637,10 +670,10 @@ TEST_F(ClatdTest, TestGenIIDRandom) {
   Global_Clatd_Config.ipv6_host_id = in6addr_any;
 
   // Generate a boatload of random IIDs.
-  int onebits = 0;
+  int onebits       = 0;
   uint64_t prev_iid = 0;
   for (int i = 0; i < 100000; i++) {
-    struct in6_addr myaddr =  interface_ipv6;
+    struct in6_addr myaddr = interface_ipv6;
 
     config_generate_local_ipv6_subnet(&myaddr);
 
@@ -648,7 +681,7 @@ TEST_F(ClatdTest, TestGenIIDRandom) {
     EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr));
 
     // Check that consecutive IIDs are not the same.
-    uint64_t iid = * (uint64_t*) (&myaddr.s6_addr[8]);
+    uint64_t iid = *(uint64_t *)(&myaddr.s6_addr[8]);
     ASSERT_TRUE(iid != prev_iid)
         << "Two consecutive random IIDs are the same: "
         << std::showbase << std::hex
@@ -657,7 +690,7 @@ TEST_F(ClatdTest, TestGenIIDRandom) {
 
     // Check that the IID is checksum-neutral with the NAT64 prefix and the
     // local prefix.
-    struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet;
+    struct in_addr *ipv4addr     = &Global_Clatd_Config.ipv4_local_subnet;
     struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet;
 
     uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr)));
@@ -732,13 +765,62 @@ TEST_F(ClatdTest, SelectIPv4Address) {
   // Now try using the real function which sees if IP addresses are free using bind().
   // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8.
   config_is_ipv4_address_free = orig_config_is_ipv4_address_free;
-  addr.s_addr = inet_addr("8.8.8.8");
+  addr.s_addr                 = inet_addr("8.8.8.8");
   EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29));
 
   addr.s_addr = inet_addr("127.0.0.1");
   EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29));
 }
 
+TEST_F(ClatdTest, ConfigureTunIp) {
+  addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free;
+  config_is_ipv4_address_free                     = over6_free;
+
+  Global_Clatd_Config.ipv4_local_prefixlen = 29;
+  Global_Clatd_Config.ipv4mtu              = 1472;
+
+  // Create an interface for configure_tun_ip to configure and bring up.
+  TunInterface v4Iface;
+  ASSERT_EQ(0, v4Iface.init());
+  struct tun_data tunnel = makeTunData();
+  strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4));
+
+  configure_tun_ip(&tunnel, nullptr /* v4_addr */);
+  EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr);
+
+  union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET);
+  EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr);
+  free(ip);
+
+  config_is_ipv4_address_free = orig_config_is_ipv4_address_free;
+  v4Iface.destroy();
+}
+
+TEST_F(ClatdTest, ConfigureTunIpManual) {
+  addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free;
+  config_is_ipv4_address_free                     = over6_free;
+
+  Global_Clatd_Config.ipv4_local_prefixlen = 29;
+  Global_Clatd_Config.ipv4mtu              = 1472;
+
+  // Create an interface for configure_tun_ip to configure and bring up.
+  TunInterface v4Iface;
+  ASSERT_EQ(0, v4Iface.init());
+  struct tun_data tunnel = makeTunData();
+  strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4));
+
+  configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */);
+  EXPECT_EQ(inet_addr("192.0.2.1"), Global_Clatd_Config.ipv4_local_subnet.s_addr);
+
+  union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET);
+  ASSERT_NE(nullptr, ip);
+  EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr);
+  free(ip);
+
+  config_is_ipv4_address_free = orig_config_is_ipv4_address_free;
+  v4Iface.destroy();
+}
+
 TEST_F(ClatdTest, DataSanitycheck) {
   // Sanity checks the data.
   uint8_t v4_header[] = { IPV4_UDP_HEADER };
@@ -753,13 +835,13 @@ TEST_F(ClatdTest, DataSanitycheck) {
   // Sanity checks check_packet.
   struct udphdr *udp;
   uint8_t v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
-  udp = (struct udphdr *) (v4_udp_packet + sizeof(struct iphdr));
+  udp                     = (struct udphdr *)(v4_udp_packet + sizeof(struct iphdr));
   fix_udp_checksum(v4_udp_packet);
   ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n";
   check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check");
 
   uint8_t v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
-  udp = (struct udphdr *) (v6_udp_packet + sizeof(struct ip6_hdr));
+  udp                     = (struct udphdr *)(v6_udp_packet + sizeof(struct ip6_hdr));
   fix_udp_checksum(v6_udp_packet);
   ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n";
   check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check");
@@ -773,53 +855,53 @@ TEST_F(ClatdTest, DataSanitycheck) {
   // Sanity checks reassemble_packet.
   uint8_t reassembled[MAXMTU];
   size_t total_length = sizeof(reassembled);
-  reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments),
-                    reassembled, &total_length, "Reassembly sanity check");
+  reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), reassembled,
+                    &total_length, "Reassembly sanity check");
   check_packet(reassembled, total_length, "IPv4 Reassembled packet is valid");
   ASSERT_EQ(sizeof(kReassembledIPv4), total_length) << "IPv4 reassembly sanity check: length\n";
-  ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *) reassembled))
-      << "Sanity check: reassembled packet is a fragment!\n";
+  ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *)reassembled))
+    << "Sanity check: reassembled packet is a fragment!\n";
   check_data_matches(kReassembledIPv4, reassembled, total_length, "IPv4 reassembly sanity check");
 
   total_length = sizeof(reassembled);
-  reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments),
-                    reassembled, &total_length, "IPv6 reassembly sanity check");
-  ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *) reassembled, total_length))
-      << "Sanity check: reassembled packet is a fragment!\n";
+  reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), reassembled,
+                    &total_length, "IPv6 reassembly sanity check");
+  ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *)reassembled, total_length))
+    << "Sanity check: reassembled packet is a fragment!\n";
   check_packet(reassembled, total_length, "IPv6 Reassembled packet is valid");
 }
 
 TEST_F(ClatdTest, PseudoChecksum) {
   uint32_t pseudo_checksum;
 
-  uint8_t v4_header[] = { IPV4_UDP_HEADER };
+  uint8_t v4_header[]        = { IPV4_UDP_HEADER };
   uint8_t v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) };
-  pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *) v4_header, UDP_LEN);
+  pseudo_checksum            = ipv4_pseudo_header_checksum((struct iphdr *)v4_header, UDP_LEN);
   EXPECT_EQ(ip_checksum_finish(pseudo_checksum),
             ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header)))
-            << "ipv4_pseudo_header_checksum incorrect\n";
+    << "ipv4_pseudo_header_checksum incorrect\n";
 
-  uint8_t v6_header[] = { IPV6_UDP_HEADER };
+  uint8_t v6_header[]        = { IPV6_UDP_HEADER };
   uint8_t v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) };
-  pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *) v6_header, UDP_LEN, IPPROTO_UDP);
+  pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *)v6_header, UDP_LEN, IPPROTO_UDP);
   EXPECT_EQ(ip_checksum_finish(pseudo_checksum),
             ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header)))
-            << "ipv6_pseudo_header_checksum incorrect\n";
+    << "ipv6_pseudo_header_checksum incorrect\n";
 }
 
 TEST_F(ClatdTest, TransportChecksum) {
-  uint8_t udphdr[] = { UDP_HEADER };
+  uint8_t udphdr[]  = { UDP_HEADER };
   uint8_t payload[] = { PAYLOAD };
   EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr)))
-            << "UDP partial checksum\n";
+    << "UDP partial checksum\n";
   EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload)))
-            << "Payload partial checksum\n";
+    << "Payload partial checksum\n";
 
-  uint8_t ip[] = { IPV4_UDP_HEADER };
-  uint8_t ip6[] = { IPV6_UDP_HEADER };
-  uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *) ip, UDP_LEN);
-  uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN,
-                                                         IPPROTO_UDP);
+  uint8_t ip[]             = { IPV4_UDP_HEADER };
+  uint8_t ip6[]            = { IPV6_UDP_HEADER };
+  uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *)ip, UDP_LEN);
+  uint32_t ipv6_pseudo_sum =
+    ipv6_pseudo_header_checksum((struct ip6_hdr *)ip6, UDP_LEN, IPPROTO_UDP);
 
   EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n";
   EXPECT_EQ(0x2644bU, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n";
@@ -868,6 +950,9 @@ TEST_F(ClatdTest, AdjustChecksum) {
 }
 
 TEST_F(ClatdTest, Translate) {
+  // This test uses hardcoded packets so the clatd address must be fixed.
+  inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet);
+
   uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
   uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
   fix_udp_checksum(udp_ipv4);
@@ -886,12 +971,13 @@ TEST_F(ClatdTest, Translate) {
 }
 
 TEST_F(ClatdTest, Fragmentation) {
-  check_fragment_translation(kIPv4Fragments, kIPv4FragLengths,
-                             kIPv6Fragments, kIPv6FragLengths,
+  // This test uses hardcoded packets so the clatd address must be fixed.
+  inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet);
+
+  check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, kIPv6Fragments, kIPv6FragLengths,
                              ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation");
 
-  check_fragment_translation(kIPv6Fragments, kIPv6FragLengths,
-                             kIPv4Fragments, kIPv4FragLengths,
+  check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, kIPv4Fragments, kIPv4FragLengths,
                              ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation");
 }
 
@@ -902,12 +988,12 @@ void check_translate_checksum_neutral(const uint8_t *original, size_t original_l
   do_translate_packet(original, original_len, translated, &translated_len, msg);
   EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n";
   // do_translate_packet already checks packets for validity and verifies the checksum.
-  int original_check = get_transport_checksum(original);
+  int original_check   = get_transport_checksum(original);
   int translated_check = get_transport_checksum(translated);
   ASSERT_NE(-1, original_check);
   ASSERT_NE(-1, translated_check);
   ASSERT_EQ(original_check, translated_check)
-      << "Not checksum neutral: original and translated checksums differ\n";
+    << "Not checksum neutral: original and translated checksums differ\n";
 }
 
 TEST_F(ClatdTest, TranslateChecksumNeutral) {
@@ -916,8 +1002,8 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) {
   ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54",
                         &Global_Clatd_Config.ipv6_local_subnet));
   config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet);
-  ASSERT_NE((uint32_t) 0x00000464, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
-  ASSERT_NE((uint32_t) 0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+  ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+  ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
 
   // Check that translating UDP packets is checksum-neutral. First, IPv4.
   uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
@@ -928,9 +1014,104 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) {
   // Now try IPv6.
   uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD };
   // The test packet uses the static IID, not the random IID. Fix up the source address.
-  struct ip6_hdr *ip6 = (struct ip6_hdr *) udp_ipv6;
+  struct ip6_hdr *ip6 = (struct ip6_hdr *)udp_ipv6;
   memcpy(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet, sizeof(ip6->ip6_src));
   fix_udp_checksum(udp_ipv6);
   check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20,
                                    "UDP/IPv4 -> UDP/IPv6 checksum neutral");
 }
+
+TEST_F(ClatdTest, GetInterfaceIp) {
+  union anyip *ip = getinterface_ip(sTun.name().c_str(), AF_INET6);
+  ASSERT_NE(nullptr, ip);
+  in6_addr expected = sTun.srcAddr();
+  in6_addr actual   = ip->ip6;
+  expect_ipv6_addr_equal(&expected, &actual);
+}
+
+void expectSocketBound(int ifindex, int sock) {
+  // Check that the packet socket is bound to the interface. We can't check the socket filter
+  // because there is no way to fetch it from the kernel.
+  sockaddr_ll sll;
+  socklen_t len = sizeof(sll);
+  ASSERT_EQ(0, getsockname(sock, reinterpret_cast<sockaddr *>(&sll), &len));
+  EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol);
+  EXPECT_EQ(ifindex, sll.sll_ifindex);
+}
+
+TEST_F(ClatdTest, ConfigureIpv6Address) {
+  struct tun_data tunnel = makeTunData();
+
+  // Run configure_clat_ipv6_address.
+  ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet));
+  ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), nullptr /* v6_addr */));
+
+  // Check that it generated an IID in the same prefix as the address assigned to the interface,
+  // and that the IID is not the default IID.
+  in6_addr addr = sTun.srcAddr();
+  EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &addr));
+  EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&Global_Clatd_Config.ipv6_local_subnet, &addr));
+  EXPECT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+  EXPECT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+
+  expectSocketBound(sTun.ifindex(), tunnel.read_fd6);
+
+  freeTunData(&tunnel);
+}
+
+TEST_F(ClatdTest, ConfigureIpv6AddressCommandLine) {
+  struct tun_data tunnel = makeTunData();
+
+  ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet));
+
+  const char *addrStr = "2001:db8::f00";
+  in6_addr addr;
+  ASSERT_EQ(1, inet_pton(AF_INET6, addrStr, &addr));
+  ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), addrStr));
+
+  EXPECT_EQ(htonl(0x20010db8), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[0]);
+  EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[1]);
+  EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[2]);
+  EXPECT_EQ(htonl(0x00000f00), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+
+  // Check that the packet socket is bound to the interface. We can't check the socket filter
+  // because there is no way to fetch it from the kernel.
+  sockaddr_ll sll;
+  socklen_t len = sizeof(sll);
+  ASSERT_EQ(0, getsockname(tunnel.read_fd6, reinterpret_cast<sockaddr *>(&sll), &len));
+  EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol);
+  EXPECT_EQ(sll.sll_ifindex, sTun.ifindex());
+
+  expectSocketBound(sTun.ifindex(), tunnel.read_fd6);
+
+  freeTunData(&tunnel);
+}
+
+TEST_F(ClatdTest, Ipv6AddressChanged) {
+  // Configure the clat IPv6 address.
+  struct tun_data tunnel = {
+    .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW),
+    .read_fd6  = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)),
+  };
+  const char *ifname = sTun.name().c_str();
+  ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr));
+  EXPECT_EQ(0, ipv6_address_changed(ifname));
+  EXPECT_EQ(0, ipv6_address_changed(ifname));
+
+  // Change the IP address on the tun interface to a new prefix.
+  char srcaddr[INET6_ADDRSTRLEN];
+  char dstaddr[INET6_ADDRSTRLEN];
+  ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.srcAddr(), srcaddr, sizeof(srcaddr)));
+  ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.dstAddr(), dstaddr, sizeof(dstaddr)));
+  EXPECT_EQ(0, ifc_del_address(ifname, srcaddr, 64));
+  EXPECT_EQ(0, ifc_del_address(ifname, dstaddr, 64));
+
+  // Check that we can tell that the address has changed.
+  EXPECT_EQ(0, ifc_add_address(ifname, "2001:db8::1:2", 64));
+  EXPECT_EQ(1, ipv6_address_changed(ifname));
+  EXPECT_EQ(1, ipv6_address_changed(ifname));
+
+  // Restore the tun interface configuration.
+  sTun.destroy();
+  ASSERT_EQ(0, sTun.init());
+}
diff --git a/config.c b/config.c
deleted file mode 100644 (file)
index b147868..0000000
--- a/config.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright 2011 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.
- *
- * config.c - configuration settings
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <cutils/config_utils.h>
-#include <netutils/ifc.h>
-
-#include "config.h"
-#include "dns64.h"
-#include "logging.h"
-#include "getaddr.h"
-#include "clatd.h"
-#include "checksum.h"
-
-struct clat_config Global_Clatd_Config;
-
-/* function: config_item_str
- * locates the config item and returns the pointer to a string, or NULL on failure.  Caller frees pointer
- * root       - parsed configuration
- * item_name  - name of config item to locate
- * defaultvar - value to use if config item isn't present
- */
-char *config_item_str(cnode *root, const char *item_name, const char *defaultvar) {
-  const char *tmp;
-
-  if(!(tmp = config_str(root, item_name, defaultvar))) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
-    return NULL;
-  }
-  return strdup(tmp);
-}
-
-/* function: config_item_int16_t
- * locates the config item, parses the integer, and returns the pointer ret_val_ptr, or NULL on failure
- * root        - parsed configuration
- * item_name   - name of config item to locate
- * defaultvar  - value to use if config item isn't present
- * ret_val_ptr - pointer for return value storage
- */
-int16_t *config_item_int16_t(cnode *root, const char *item_name, const char *defaultvar, int16_t *ret_val_ptr) {
-  const char *tmp;
-  char *endptr;
-  long int conf_int;
-
-  if(!(tmp = config_str(root, item_name, defaultvar))) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
-    return NULL;
-  }
-
-  errno = 0;
-  conf_int = strtol(tmp,&endptr,10);
-  if(errno > 0) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item is not numeric: %s (error=%s)",item_name,tmp,strerror(errno));
-    return NULL;
-  }
-  if(endptr == tmp || *tmp == '\0') {
-    logmsg(ANDROID_LOG_FATAL,"%s config item is not numeric: %s",item_name,tmp);
-    return NULL;
-  }
-  if(*endptr != '\0') {
-    logmsg(ANDROID_LOG_FATAL,"%s config item contains non-numeric characters: %s",item_name,endptr);
-    return NULL;
-  }
-  if(conf_int > INT16_MAX || conf_int < INT16_MIN) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item is too big/small: %d",item_name,conf_int);
-    return NULL;
-  }
-  *ret_val_ptr = conf_int;
-  return ret_val_ptr;
-}
-
-/* function: config_item_ip
- * locates the config item, parses the ipv4 address, and returns the pointer ret_val_ptr, or NULL on failure
- * root        - parsed configuration
- * item_name   - name of config item to locate
- * defaultvar  - value to use if config item isn't present
- * ret_val_ptr - pointer for return value storage
- */
-struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *defaultvar, struct in_addr *ret_val_ptr) {
-  const char *tmp;
-  int status;
-
-  if(!(tmp = config_str(root, item_name, defaultvar))) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
-    return NULL;
-  }
-
-  status = inet_pton(AF_INET, tmp, ret_val_ptr);
-  if(status <= 0) {
-    logmsg(ANDROID_LOG_FATAL,"invalid IPv4 address specified for %s: %s", item_name, tmp);
-    return NULL;
-  }
-
-  return ret_val_ptr;
-}
-
-/* function: config_item_ip6
- * locates the config item, parses the ipv6 address, and returns the pointer ret_val_ptr, or NULL on failure
- * root        - parsed configuration
- * item_name   - name of config item to locate
- * defaultvar  - value to use if config item isn't present
- * ret_val_ptr - pointer for return value storage
- */
-struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char *defaultvar, struct in6_addr *ret_val_ptr) {
-  const char *tmp;
-  int status;
-
-  if(!(tmp = config_str(root, item_name, defaultvar))) {
-    logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
-    return NULL;
-  }
-
-  status = inet_pton(AF_INET6, tmp, ret_val_ptr);
-  if(status <= 0) {
-    logmsg(ANDROID_LOG_FATAL,"invalid IPv6 address specified for %s: %s", item_name, tmp);
-    return NULL;
-  }
-
-  return ret_val_ptr;
-}
-
-/* function: free_config
- * frees the memory used by the global config variable
- */
-void free_config() {
-  if(Global_Clatd_Config.plat_from_dns64_hostname) {
-    free(Global_Clatd_Config.plat_from_dns64_hostname);
-    Global_Clatd_Config.plat_from_dns64_hostname = NULL;
-  }
-}
-
-/* function: ipv6_prefix_equal
- * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64.
- * a1 - first address
- * a2 - second address
- * returns: 0 if the subnets are different, 1 if they are the same.
- */
-int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) {
-    return !memcmp(a1, a2, 8);
-}
-
-/* function: dns64_detection
- * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer
- * net_id - (optional) netId to use, NETID_UNSET indicates use of default network
- */
-void dns64_detection(unsigned net_id) {
-  int backoff_sleep, status;
-  struct in6_addr tmp_ptr;
-
-  backoff_sleep = 1;
-
-  while(1) {
-    status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,net_id,&tmp_ptr);
-    if(status > 0) {
-      memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr));
-      return;
-    }
-    logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep);
-    sleep(backoff_sleep);
-    backoff_sleep *= 2;
-    if(backoff_sleep >= 120) {
-      backoff_sleep = 120;
-    }
-  }
-}
-
-/* function: gen_random_iid
- * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix
- * myaddr            - IPv6 address to write to
- * ipv4_local_subnet - clat IPv4 address
- * plat_subnet       - NAT64 prefix
- */
-void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet,
-                    struct in6_addr *plat_subnet) {
-  // Fill last 8 bytes of IPv6 address with random bits.
-  arc4random_buf(&myaddr->s6_addr[8], 8);
-
-  // Make the IID checksum-neutral. That is, make it so that:
-  //   checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6)
-  // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4):
-  //   checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix)
-  // Do this by adjusting the two bytes in the middle of the IID.
-
-  uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12];
-
-  uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet));
-  uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) +
-                ip_checksum_add(0, myaddr, sizeof(*myaddr));
-
-  uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2);
-  myaddr->s6_addr[11] = delta >> 8;
-  myaddr->s6_addr[12] = delta & 0xff;
-}
-
-// Factored out to a separate function for testability.
-int connect_is_ipv4_address_free(in_addr_t addr) {
-  int s = socket(AF_INET, SOCK_DGRAM, 0);
-  if (s == -1) {
-    return 0;
-  }
-
-  // Attempt to connect to the address. If the connection succeeds and getsockname returns the same
-  // the address then the address is already assigned to the system and we can't use it.
-  struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 };
-  socklen_t len = sizeof(sin);
-  int inuse = connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0 &&
-              getsockname(s, (struct sockaddr *) &sin, &len) == 0 &&
-              (size_t) len >= sizeof(sin) &&
-              sin.sin_addr.s_addr == addr;
-
-  close(s);
-  return !inuse;
-}
-
-addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free;
-
-/* function: config_select_ipv4_address
- * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order
- * ip        - the IP address from the configuration file
- * prefixlen - the length of the prefix from which addresses may be selected.
- * returns: the IPv4 address, or INADDR_NONE if no addresses were available
- */
-in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) {
-  in_addr_t chosen = INADDR_NONE;
-
-  // Don't accept prefixes that are too large because we scan addresses one by one.
-  if (prefixlen < 16 || prefixlen > 32) {
-      return chosen;
-  }
-
-  // All these are in host byte order.
-  in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen);
-  in_addr_t ipv4 = ntohl(ip->s_addr);
-  in_addr_t first_ipv4 = ipv4;
-  in_addr_t prefix = ipv4 & mask;
-
-  // Pick the first IPv4 address in the pool, wrapping around if necessary.
-  // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0.
-  do {
-     if (config_is_ipv4_address_free(htonl(ipv4))) {
-       chosen = htonl(ipv4);
-       break;
-     }
-     ipv4 = prefix | ((ipv4 + 1) & ~mask);
-  } while (ipv4 != first_ipv4);
-
-  return chosen;
-}
-
-/* function: config_generate_local_ipv6_subnet
- * generates the local ipv6 subnet when given the interface ip
- * requires config.ipv6_host_id
- * interface_ip - in: interface ip, out: local ipv6 host address
- */
-void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) {
-  int i;
-
-  if (Global_Clatd_Config.use_dynamic_iid) {
-    /* Generate a random interface ID. */
-    gen_random_iid(interface_ip,
-                   &Global_Clatd_Config.ipv4_local_subnet,
-                   &Global_Clatd_Config.plat_subnet);
-  } else {
-    /* Use the specified interface ID. */
-    for(i = 2; i < 4; i++) {
-      interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i];
-    }
-  }
-}
-
-/* function: read_config
- * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on failure, 1 on success
- * file             - filename to parse
- * uplink_interface - interface to use to reach the internet and supplier of address space
- * plat_prefix      - (optional) plat prefix to use, otherwise follow config file
- * net_id           - (optional) netId to use, NETID_UNSET indicates use of default network
- */
-int read_config(const char *file, const char *uplink_interface, const char *plat_prefix,
-        unsigned net_id) {
-  cnode *root = config_node("", "");
-  void *tmp_ptr = NULL;
-  unsigned flags;
-
-  if(!root) {
-    logmsg(ANDROID_LOG_FATAL,"out of memory");
-    return 0;
-  }
-
-  memset(&Global_Clatd_Config, '\0', sizeof(Global_Clatd_Config));
-
-  config_load_file(root, file);
-  if(root->first_child == NULL) {
-    logmsg(ANDROID_LOG_FATAL,"Could not read config file %s", file);
-    goto failed;
-  }
-
-  Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface);
-  if (!Global_Clatd_Config.default_pdp_interface)
-    goto failed;
-
-  if(!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu))
-    goto failed;
-
-  if(!config_item_int16_t(root, "ipv4mtu", "-1", &Global_Clatd_Config.ipv4mtu))
-    goto failed;
-
-  if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET,
-                     &Global_Clatd_Config.ipv4_local_subnet))
-    goto failed;
-
-  if(!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN,
-                          &Global_Clatd_Config.ipv4_local_prefixlen))
-    goto failed;
-
-  if(plat_prefix) { // plat subnet is coming from the command line
-    if(inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
-      logmsg(ANDROID_LOG_FATAL,"invalid IPv6 address specified for plat prefix: %s", plat_prefix);
-      goto failed;
-    }
-  } else {
-    tmp_ptr = (void *)config_item_str(root, "plat_from_dns64", "yes");
-    if(!tmp_ptr || strcmp(tmp_ptr, "no") == 0) {
-      free(tmp_ptr);
-
-      if(!config_item_ip6(root, "plat_subnet", NULL, &Global_Clatd_Config.plat_subnet)) {
-        logmsg(ANDROID_LOG_FATAL, "plat_from_dns64 disabled, but no plat_subnet specified");
-        goto failed;
-      }
-    } else {
-      free(tmp_ptr);
-
-      if(!(Global_Clatd_Config.plat_from_dns64_hostname = config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME)))
-        goto failed;
-      dns64_detection(net_id);
-    }
-  }
-
-  if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id))
-    goto failed;
-
-  /* In order to prevent multiple devices attempting to use the same clat address, never use a
-     statically-configured interface ID on a broadcast interface such as wifi. */
-  if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) {
-    ifc_init();
-    ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags);
-    ifc_close();
-    Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0;
-  } else {
-    Global_Clatd_Config.use_dynamic_iid = 1;
-  }
-
-  return 1;
-
-failed:
-  free(root);
-  free_config();
-  return 0;
-}
-
-/* function; dump_config
- * prints the current config
- */
-void dump_config() {
-  char charbuffer[INET6_ADDRSTRLEN];
-
-  logmsg(ANDROID_LOG_DEBUG,"mtu = %d",Global_Clatd_Config.mtu);
-  logmsg(ANDROID_LOG_DEBUG,"ipv4mtu = %d",Global_Clatd_Config.ipv4mtu);
-  logmsg(ANDROID_LOG_DEBUG,"ipv6_local_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer)));
-  logmsg(ANDROID_LOG_DEBUG,"ipv4_local_subnet = %s",inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer)));
-  logmsg(ANDROID_LOG_DEBUG,"ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen);
-  logmsg(ANDROID_LOG_DEBUG,"plat_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer)));
-  logmsg(ANDROID_LOG_DEBUG,"default_pdp_interface = %s",Global_Clatd_Config.default_pdp_interface);
-}
diff --git a/dns64.c b/dns64.c
deleted file mode 100644 (file)
index 4e9252d..0000000
--- a/dns64.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2011 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.
- *
- * dns64.c - find the nat64 prefix with a dns64 lookup
- */
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <strings.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "dns64.h"
-#include "logging.h"
-#include "resolv_netid.h"
-
-/* function: plat_prefix
- * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on failure
- * ipv4_name  - name to lookup
- * net_id     - (optional) netId to use, NETID_UNSET indicates use of default network
- * prefix     - the plat /96 prefix
- */
-int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) {
-  const struct addrinfo hints = {
-    .ai_family = AF_INET6,
-  };
-  int status;
-  struct addrinfo *result = NULL;
-  struct in6_addr plat_addr;
-  char plat_addr_str[INET6_ADDRSTRLEN];
-
-  logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS...");
-
-  status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result);
-  if (status != 0 || result == NULL) {
-    logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s",
-           ipv4_name, status, gai_strerror(status));
-    return 0;
-  }
-
-  // Use only the first result.  If other records are present, possibly with
-  // differing DNS64 prefixes they are ignored (there is very little sensible
-  // that could be done with them at this time anyway).
-
-  if (result->ai_family != AF_INET6) {
-    logmsg(ANDROID_LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family);
-    return 0;
-  }
-  plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr;
-  // Only /96 DNS64 prefixes are supported at this time.
-  plat_addr.s6_addr32[3] = 0;
-  freeaddrinfo(result);
-
-  logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96",
-         inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)));
-  *prefix = plat_addr;
-  return 1;
-}
similarity index 100%
rename from checksum.h
rename to include/checksum.h
diff --git a/include/clatd.h b/include/clatd.h
new file mode 100644 (file)
index 0000000..6fec8ce
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 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.
+ *
+ * clatd.h - main routines used by clatd
+ */
+#ifndef __CLATD_H__
+#define __CLATD_H__
+
+#include <stdlib.h>
+#include <sys/uio.h>
+
+struct tun_data;
+
+#define MAXMTU 1500
+#define PACKETLEN (MAXMTU + sizeof(struct tun_pi))
+#define CLATD_VERSION "1.4"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+// how frequently (in seconds) to poll for an address change while traffic is passing
+#define INTERFACE_POLL_FREQUENCY 30
+
+// how frequently (in seconds) to poll for an address change while there is no traffic
+#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90
+
+/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */
+#define MTU_DELTA 28
+#define MARK_UNSET 0u
+#define NETID_UNSET 0u
+#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
+#define AID_CLAT 1029 /* clat part of nat464 */
+#define AID_VPN  1016  /* vpn system */
+
+void stop_loop();
+void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr);
+void set_capability(uint64_t target_cap);
+void drop_root_but_keep_caps();
+void open_sockets(struct tun_data *tunnel, uint32_t mark);
+int ipv6_address_changed(const char *interface);
+int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface,
+                                const char *src_addr);
+void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr,
+                         const char *v6, struct tun_data *tunnel, unsigned net_id);
+void event_loop(struct tun_data *tunnel);
+
+/* function: parse_int
+ * parses a string as a decimal/hex/octal signed integer
+ *   str - the string to parse
+ *   out - the signed integer to write to, gets clobbered on failure
+ */
+static inline int parse_int(const char *str, int *out) {
+  char *end_ptr;
+  *out = strtol(str, &end_ptr, 0);
+  return *str && !*end_ptr;
+}
+
+/* function: parse_unsigned
+ * parses a string as a decimal/hex/octal unsigned integer
+ *   str - the string to parse
+ *   out - the unsigned integer to write to, gets clobbered on failure
+ */
+static inline int parse_unsigned(const char *str, unsigned *out) {
+  char *end_ptr;
+  *out = strtoul(str, &end_ptr, 0);
+  return *str && !*end_ptr;
+}
+
+#endif /* __CLATD_H__ */
similarity index 56%
rename from clatd.h
rename to include/common.h
index f421f46..e9551ee 100644 (file)
--- a/clatd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 Daniel Drown
+ * Copyright 2018 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.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
- * clatd.h - main system definitions
+ * common.h - common definitions
  */
-#ifndef __CLATD_H__
-#define __CLATD_H__
+#ifndef __CLATD_COMMON_H__
+#define __CLATD_COMMON_H__
 
 #include <sys/uio.h>
 
-#define MAXMTU 1500
-#define PACKETLEN (MAXMTU+sizeof(struct tun_pi))
-#define CLATD_VERSION "1.4"
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-// how frequently (in seconds) to poll for an address change while traffic is passing
-#define INTERFACE_POLL_FREQUENCY 30
-
-// how frequently (in seconds) to poll for an address change while there is no traffic
-#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90
-
 // A clat_packet is an array of iovec structures representing a packet that we are translating.
 // The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain
 // specific parts of the packet. The packet_* functions operate on all the packet segments past a
 // given position.
 typedef enum {
-    CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR,
-    CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR,
-    CLAT_POS_PAYLOAD, CLAT_POS_MAX
+  CLAT_POS_TUNHDR,
+  CLAT_POS_IPHDR,
+  CLAT_POS_FRAGHDR,
+  CLAT_POS_TRANSPORTHDR,
+  CLAT_POS_ICMPERR_IPHDR,
+  CLAT_POS_ICMPERR_FRAGHDR,
+  CLAT_POS_ICMPERR_TRANSPORTHDR,
+  CLAT_POS_PAYLOAD,
+  CLAT_POS_MAX
 } clat_packet_index;
 typedef struct iovec clat_packet[CLAT_POS_MAX];
 
-#endif /* __CLATD_H__ */
+#endif /* __CLATD_COMMON_H__ */
similarity index 95%
rename from config.h
rename to include/config.h
index e31a81d..214cc49 100644 (file)
--- a/config.h
@@ -18,8 +18,8 @@
 #ifndef __CONFIG_H__
 #define __CONFIG_H__
 
-#include <netinet/in.h>
 #include <linux/if.h>
+#include <netinet/in.h>
 
 #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4"
 #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29"
@@ -37,10 +37,10 @@ struct clat_config {
   int use_dynamic_iid;
 };
 
-extern struct clat_config Global_Clatd_Config;
+struct clat_config Global_Clatd_Config;
 
 int read_config(const char *file, const char *uplink_interface, const char *plat_prefix,
-        unsigned net_id);
+                unsigned net_id);
 void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip);
 in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen);
 int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2);
diff --git a/include/config_utils.h b/include/config_utils.h
new file mode 100644 (file)
index 0000000..9d4342d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+#ifndef __CUTILS_CONFIG_UTILS_H
+#define __CUTILS_CONFIG_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cnode cnode;
+
+
+struct cnode {
+       cnode *next;
+       cnode *first_child;
+       cnode *last_child;
+       const char *name;
+       const char *value;
+};
+
+/* parse a text string into a config node tree */
+void config_load(cnode *root, char *data);
+
+/* parse a file into a config node tree */
+void config_load_file(cnode *root, const char *fn);
+
+/* create a single config node */
+cnode *config_node(const char *name, const char *value);
+
+/* locate a named child of a config node */
+cnode *config_find(cnode *root, const char *name);
+
+/* look up a child by name and return the boolean value */
+int config_bool(cnode *root, const char *name, int _default);
+
+/* look up a child by name and return the string value */
+const char *config_str(cnode *root, const char *name, const char *_default);
+
+/* add a named child to a config node (or modify it if it already exists) */
+void config_set(cnode *root, const char *name, const char *value);
+
+void *load_file(const char *fn, unsigned *_sz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644 (file)
index 0000000..21716e1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __DBUS_H__
+#define __DBUS_H__
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define CLAT_DBUS_SERVICE "com.samsung.clatd"
+
+#define CLAT_DBUS_PATH "/"
+#define CLAT_DBUS_CONTROL_PATH "/Control"
+
+typedef struct _clat_dbus ClatDBus;
+
+typedef void (*on_clat_dbus_cb)(ClatDBus *cdbus, gpointer user_data);
+
+int                     clat_dbus_init(on_clat_dbus_cb cb, gpointer user_data);
+GDBusConnection *       clat_dbus_get_connection(ClatDBus *cdbus);
+GDBusInterfaceSkeleton *clat_di_skeleton_new_control(void);
+
+#endif
similarity index 100%
rename from debug.h
rename to include/debug.h
similarity index 100%
rename from dns64.h
rename to include/dns64.h
similarity index 63%
rename from dump.h
rename to include/dump.h
index bb41b3b..c3f0477 100644 (file)
--- a/dump.h
 #ifndef __DUMP_H__
 #define __DUMP_H__
 
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
 void dump_ip(struct iphdr *header);
 void dump_icmp(struct icmphdr *icmp);
-void dump_udp(const struct udphdr *udp, const struct iphdr *ip,
-              const uint8_t *payload, size_t payload_size);
-void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip,
-              const uint8_t *payload, size_t payload_size,
-              const char *options, size_t options_size);
+void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload,
+              size_t payload_size);
+void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload,
+              size_t payload_size, const char *options, size_t options_size);
 
 void dump_ip6(struct ip6_hdr *header);
 void dump_icmp6(struct icmp6_hdr *icmp6);
-void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6,
-               const uint8_t *payload, size_t payload_size);
-void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6,
-               const uint8_t *payload, size_t payload_size,
-               const char *options, size_t options_size);
+void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload,
+               size_t payload_size);
+void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload,
+               size_t payload_size, const char *options, size_t options_size);
 
 void logcat_hexdump(const char *info, const uint8_t *data, size_t len);
 void dump_iovec(const struct iovec *iov, int iov_len);
similarity index 100%
rename from getaddr.h
rename to include/getaddr.h
similarity index 100%
rename from icmp.h
rename to include/icmp.h
diff --git a/include/ifc.h b/include/ifc.h
new file mode 100644 (file)
index 0000000..3b27234
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2008, 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.
+ */
+
+#ifndef _NETUTILS_IFC_H_
+#define _NETUTILS_IFC_H_
+
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+extern int ifc_init(void);
+extern void ifc_close(void);
+
+extern int ifc_get_ifindex(const char *name, int *if_indexp);
+extern int ifc_get_hwaddr(const char *name, void *ptr);
+
+extern int ifc_up(const char *name);
+extern int ifc_down(const char *name);
+
+extern int ifc_enable(const char *ifname);
+extern int ifc_disable(const char *ifname);
+
+#define RESET_IPV4_ADDRESSES 0x01
+#define RESET_IPV6_ADDRESSES 0x02
+#define RESET_IGNORE_INTERFACE_ADDRESS 0x04
+#define RESET_ALL_ADDRESSES  (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
+extern int ifc_reset_connections(const char *ifname, const int reset_mask);
+
+extern int ifc_get_addr(const char *name, in_addr_t *addr);
+extern int ifc_set_addr(const char *name, in_addr_t addr);
+extern int ifc_add_address(const char *name, const char *address,
+                           int prefixlen);
+extern int ifc_del_address(const char *name, const char *address,
+                           int prefixlen);
+extern int ifc_set_prefixLength(const char *name, int prefixLength);
+extern int ifc_set_hwaddr(const char *name, const void *ptr);
+extern int ifc_clear_addresses(const char *name);
+
+extern int ifc_create_default_route(const char *name, in_addr_t addr);
+extern int ifc_remove_default_route(const char *ifname);
+extern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength,
+                        unsigned *flags);
+
+extern int ifc_configure(const char *ifname, in_addr_t address,
+                         uint32_t prefixLength, in_addr_t gateway,
+                         in_addr_t dns1, in_addr_t dns2);
+
+extern in_addr_t prefixLengthToIpv4Netmask(int prefix_length);
+
+__END_DECLS
+
+#endif /* _NETUTILS_IFC_H_ */
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..78fa04d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Network Configuration Module
+ *
+ * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CLATD_LOG_H__
+#define __CLATD_LOG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dlog/dlog.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef LOG_TAG
+#define LOG_TAG "CLATD"
+
+#define LOG_WARN                1
+#define LOG_FATAL               2
+#define LOG_ERROR               3
+#define LOG_DEBUG               5
+#define LOG_INFO                4
+
+#define CLATD_LOG(log_level, format, args ...) \
+       do { \
+               switch (log_level) { \
+               case LOG_WARN: \
+                       SLOGW(format, ## args); \
+                       break; \
+               case LOG_FATAL: \
+                       SLOGF(format, ## args); \
+                       break; \
+               case LOG_ERROR: \
+                       SLOGE(format, ## args); \
+                       break; \
+               case LOG_INFO: \
+                       SLOGI(format, ## args); \
+                       break; \
+               default: \
+                       SLOGD(format, ## args); \
+               } \
+       } while (0)
+
+#define CLATD_SECURE_SLOG(log_level, format, args...) SLOGI(format, ## args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CLATD_LOG_H__ */
similarity index 100%
rename from logging.h
rename to include/logging.h
similarity index 100%
rename from mtu.h
rename to include/mtu.h
similarity index 86%
rename from netlink_msg.h
rename to include/netlink_msg.h
index 13e1f28..84a9d2c 100644 (file)
@@ -27,4 +27,15 @@ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct
 int netlink_sendrecv(struct nl_msg *msg);
 int netlink_set_kernel_only(struct nl_sock *nl_sk);
 
+struct nl_msg {
+       int nm_protocol;
+       int nm_flags;
+       struct sockaddr_nl nm_src;
+       struct sockaddr_nl nm_dst;
+       struct ucred nm_creds;
+       struct nlmsghdr *nm_nlh;
+       size_t nm_size;
+       int nm_refcnt;
+};
+
 #endif
similarity index 100%
rename from ring.h
rename to include/ring.h
similarity index 84%
rename from setif.h
rename to include/setif.h
index d31eed5..2ec503d 100644 (file)
--- a/setif.h
 #ifndef __SETIF_H__
 #define __SETIF_H__
 
-int add_address(const char *ifname, int family, const void *address, int cidr, const void *broadcast);
+int add_address(const char *ifname, int family, const void *address, int cidr,
+                const void *broadcast);
+int delete_address(const char *ifname, int family, const void *address, int prefixlen);
 int if_up(const char *ifname, int mtu);
+int if_down(const char *ifname);
 
 int add_anycast_address(int sock, const struct in6_addr *addr, const char *interface);
 int del_anycast_address(int sock, const struct in6_addr *addr);
diff --git a/include/specific_dns.h b/include/specific_dns.h
new file mode 100644 (file)
index 0000000..64f0922
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * specific_dns
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Youngho Jeon <ykernel.jeon@samsung.com>
+ *
+ * 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.
+ *
+ * specific_dns.h - request to specific dns server to resolve domain name
+ */
+
+#ifndef __SPECIFIC_DNS_H__
+#define __SPECIFIC_DNS_H__
+
+#ifndef bool
+typedef int bool;
+#endif
+
+#include "clatd.h"
+
+int set_ifname(const char* if_name);
+int set_specific_nameserver(const char* servers);
+bool has_specific_nameserver();
+int specific_getaddrinfo(const char *hostname, const struct addrinfo *hint,
+                       struct addrinfo **result);
+
+#endif /* __SPECIFIC_DNS_H__ */
similarity index 88%
rename from translate.h
rename to include/translate.h
index aa8b736..e65e90a 100644 (file)
 #ifndef __TRANSLATE_H__
 #define __TRANSLATE_H__
 
+//#include <linux/icmp.h>
+#include <linux/if_tun.h>
+#include <netinet/icmp6.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
 #include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
-#include <linux/if_tun.h>
+#include <netinet/udp.h>
 
 #include "clatd.h"
+#include "common.h"
 
-#define MAX_TCP_HDR (15 * 4)   // Data offset field is 4 bits and counts in 32-bit words.
+#define MAX_TCP_HDR (15 * 4)  // Data offset field is 4 bits and counts in 32-bit words.
 
 // Calculates the checksum over all the packet components starting from pos.
 uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos);
@@ -75,16 +76,15 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr
 int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len);
 
 // Translate TCP and UDP packets.
-int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
-               uint32_t old_sum, uint32_t new_sum, size_t len);
-int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
-               uint32_t old_sum, uint32_t new_sum, size_t len);
+int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum,
+               uint32_t new_sum, size_t len);
+int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum,
+               uint32_t new_sum, size_t len);
 
 int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
-                  size_t header_size, uint32_t old_sum, uint32_t new_sum,
-                  const uint8_t *payload, size_t payload_size);
+                  size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload,
+                  size_t payload_size);
 int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
-                  uint32_t old_sum, uint32_t new_sum,
-                  const uint8_t *payload, size_t payload_size);
+                  uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size);
 
 #endif /* __TRANSLATE_H__ */
similarity index 60%
rename from tun.h
rename to include/tun.h
index bcdd10e..05f62c1 100644 (file)
--- a/tun.h
 #ifndef __TUN_H__
 #define __TUN_H__
 
+#include <fcntl.h>
 #include <linux/if.h>
+#include <sys/uio.h>
+#include <unistd.h>
 
-#include "clatd.h"
+#include "common.h"
 #include "ring.h"
 
 struct tun_data {
@@ -29,9 +32,19 @@ struct tun_data {
   struct packet_ring ring;
 };
 
+// tun_open and tun_alloc are defined in tun.c and only used by clatd_microbenchmark.c
 int tun_open();
-int tun_alloc(char *dev, int fd);
-int send_tun(int fd, clat_packet out, int iov_len);
-int set_nonblocking(int fd);
+int tun_alloc(char *dev, int fd, size_t len);
+
+/* function: send_tun
+ * sends a clat_packet to a tun interface
+ * fd      - the tun filedescriptor
+ * out     - the packet to send
+ * iov_len - the number of entries in the clat_packet
+ * returns: number of bytes read on success, -1 on failure
+ */
+static inline int send_tun(int fd, clat_packet out, int iov_len) {
+  return writev(fd, out, iov_len);
+}
 
 #endif
diff --git a/introspection/control.xml b/introspection/control.xml
new file mode 100644 (file)
index 0000000..c26990e
--- /dev/null
@@ -0,0 +1,13 @@
+<node>
+       <interface name="com.samsung.clatd.Control">
+               <method name="Start">
+                       <arg direction="in"  type="a{is}" name="list" />
+                       <arg direction="out" type="i" name="result" />
+               </method>
+               <method name="Stop">
+               </method>
+               <signal name="Notify">
+                       <arg type="i" name="status" />
+               </signal>
+       </interface>
+</node>
diff --git a/logging.c b/logging.c
deleted file mode 100644 (file)
index c90b1cf..0000000
--- a/logging.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2011 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.
- *
- * logging.c - print a log message
- */
-
-#include <stdarg.h>
-#include <android/log.h>
-
-#include "logging.h"
-#include "debug.h"
-
-/* function: logmsg
- * prints a log message to android's log buffer
- * prio - the log message priority
- * fmt  - printf format specifier
- * ...  - printf format arguments
- */
-void logmsg(int prio, const char *fmt, ...) {
-  va_list ap;
-
-  va_start(ap, fmt);
-  __android_log_vprint(prio, "clatd", fmt, ap);
-  va_end(ap);
-}
-
-/* function: logmsg_dbg
- * prints a log message to android's log buffer if CLAT_DEBUG is set
- * prio - the log message priority
- * fmt  - printf format specifier
- * ...  - printf format arguments
- */
-#if CLAT_DEBUG
-void logmsg_dbg(int prio, const char *fmt, ...) {
-  va_list ap;
-
-  va_start(ap, fmt);
-  __android_log_vprint(prio, "clatd", fmt, ap);
-  va_end(ap);
-}
-#else
-void logmsg_dbg(__attribute__((unused)) int prio, __attribute__((unused)) const char *fmt, ...) {}
-#endif
diff --git a/packaging/clat.spec b/packaging/clat.spec
new file mode 100644 (file)
index 0000000..fd7b884
--- /dev/null
@@ -0,0 +1,70 @@
+Name:          clat
+Summary:       CLAT Daemon
+Version:       1.4
+Release:       1
+Source:        %{name}-%{version}.tar.gz
+Source1:       dbus-1/com.samsung.clatd.service
+Source2:       dbus-1/com.samsung.clatd.conf
+License:       Apache-2.0
+Group:         System/Network
+BuildRequires: cmake
+BuildRequires: pkgconfig(libnl-2.0)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(libcares)
+BuildRequires: libcap-devel
+
+%description
+This package provides CLAT Daemon, which 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.
+
+%package devel
+Summary:  CLAT Daemon (Development)
+Group:    System/Network
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+CLAT Daemon (Development)
+
+%prep
+%setup -q
+
+%build
+export CFLAGS="$CFLAGS -O2 -Wall"
+export CXXFLAGS="$CXXFLAGS -O2 -Wall"
+
+%if 0%{?sec_build_binary_debug_enable}
+export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
+export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
+%endif
+
+cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix}
+make %{?_smp_mflags}
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}%{_bindir}/
+mkdir -p %{buildroot}%{_sysconfdir}/
+cp -v res/clatd.conf %{buildroot}%{_sysconfdir}/clatd.conf
+%make_install
+
+mkdir -p %{buildroot}%{_datadir}/dbus-1/system-services
+#cp %{SOURCE1} %{buildroot}%{_libdir}/systemd/system/mobileap-agent.service
+#cp com.samsung.clatd.service %{buildroot}%{_datadir}/dbus-1/system-services/com.samsung.clatd.service
+cp %{SOURCE1} %{buildroot}%{_datadir}/dbus-1/system-services/com.samsung.clatd.service
+
+mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d/
+cp -v %{SOURCE2} %{buildroot}%{_sysconfdir}/dbus-1/system.d/com.samsung.clatd.conf
+#cp -v com.samsung.clatd.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/com.samsung.clatd.conf
+
+
+%files
+%manifest clatd.manifest
+%attr(644,-,-) %{_sysconfdir}/*.conf
+%attr(644,root,root) %{_datadir}/dbus-1/system-services/*
+%attr(644,root,root) %{_sysconfdir}/dbus-1/system.d/com.samsung.clatd.conf
+%{_bindir}/clatd
+%license LICENSE
diff --git a/packaging/com.samsung.clatd.conf b/packaging/com.samsung.clatd.conf
new file mode 100644 (file)
index 0000000..8a8ecab
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="com.samsung.clatd"/>
+        <allow send_destination="com.samsung.clatd"/>
+    </policy>
+       <policy user="network_fw">
+        <allow own="com.samsung.clatd"/>
+        <allow send_destination="com.samsung.clatd"/>
+       </policy>
+    <policy context="default">
+        <deny own="com.samsung.clatd"/>
+        <deny send_destination="com.samsung.clatd" send_type="method_call"/>
+    </policy>
+</busconfig>
diff --git a/packaging/com.samsung.clatd.service b/packaging/com.samsung.clatd.service
new file mode 100644 (file)
index 0000000..2c71db6
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.samsung.clatd
+Exec=/usr/bin/clatd
+User=root
similarity index 100%
rename from clatd.conf
rename to res/clatd.conf
similarity index 57%
rename from checksum.c
rename to src/checksum.c
index 23a7c02..5613779 100644 (file)
@@ -22,6 +22,7 @@
 #include <netinet/tcp.h>
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
+//#include <linux/icmp.h>
 
 #include "checksum.h"
 
  * data        - the data to add to the checksum
  * len         - length of data
  */
-uint32_t ip_checksum_add(uint32_t current, const void *data, int len) {
-  uint32_t checksum = current;
-  int left = len;
-  const uint16_t *data_16 = data;
-
-  while(left > 1) {
-    checksum += *data_16;
-    data_16++;
-    left -= 2;
-  }
-  if(left) {
-    checksum += *(uint8_t *)data_16;
-  }
-
-  return checksum;
+uint32_t ip_checksum_add(uint32_t current, const void *data, int len)
+{
+       uint32_t checksum = current;
+       int left = len;
+       const uint16_t *data_16 = data;
+
+       while (left > 1) {
+               checksum += *data_16;
+               data_16++;
+               left -= 2;
+       }
+       if (left) {
+               checksum += *(uint8_t *)data_16;
+       }
+
+       return checksum;
 }
 
 /* function: ip_checksum_fold
@@ -53,11 +55,12 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) {
  * temp_sum - sum from ip_checksum_add
  * returns: the folded checksum in network byte order
  */
-uint16_t ip_checksum_fold(uint32_t temp_sum) {
-  while(temp_sum > 0xffff)
-    temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+uint16_t ip_checksum_fold(uint32_t temp_sum)
+{
+       while (temp_sum > 0xffff)
+               temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
 
-  return temp_sum;
+       return temp_sum;
 }
 
 /* function: ip_checksum_finish
@@ -65,8 +68,9 @@ uint16_t ip_checksum_fold(uint32_t temp_sum) {
  * temp_sum - sum from ip_checksum_add
  * returns: a header checksum value in network byte order
  */
-uint16_t ip_checksum_finish(uint32_t temp_sum) {
-  return ~ip_checksum_fold(temp_sum);
+uint16_t ip_checksum_finish(uint32_t temp_sum)
+{
+       return ~ip_checksum_fold(temp_sum);
 }
 
 /* function: ip_checksum
@@ -74,11 +78,12 @@ uint16_t ip_checksum_finish(uint32_t temp_sum) {
  * data - data to checksum
  * len  - length of data
  */
-uint16_t ip_checksum(const void *data, int len) {
-  uint32_t temp_sum;
+uint16_t ip_checksum(const void *data, int len)
+{
+       uint32_t temp_sum;
 
-  temp_sum = ip_checksum_add(0,data,len);
-  return ip_checksum_finish(temp_sum);
+       temp_sum = ip_checksum_add(0, data, len);
+       return ip_checksum_finish(temp_sum);
 }
 
 /* function: ipv6_pseudo_header_checksum
@@ -87,18 +92,19 @@ uint16_t ip_checksum(const void *data, int len) {
  * len      - the transport length (transport header + payload)
  * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
  */
-uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) {
-  uint32_t checksum_len, checksum_next;
-  checksum_len = htonl((uint32_t) len);
-  checksum_next = htonl(protocol);
-
-  uint32_t current = 0;
-  current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
-  current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
-  current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
-  current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
-
-  return current;
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol)
+{
+       uint32_t checksum_len, checksum_next;
+       checksum_len = htonl((uint32_t)len);
+       checksum_next = htonl(protocol);
+
+       uint32_t current = 0;
+       current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
+       current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
+       current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
+       current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
+
+       return current;
 }
 
 /* function: ipv4_pseudo_header_checksum
@@ -106,19 +112,20 @@ uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, ui
  * ip      - the ipv4 header
  * len     - the transport length (transport header + payload)
  */
-uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) {
-  uint16_t temp_protocol, temp_length;
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len)
+{
+       uint16_t temp_protocol, temp_length;
 
-  temp_protocol = htons(ip->protocol);
-  temp_length = htons(len);
+       temp_protocol = htons(ip->protocol);
+       temp_length = htons(len);
 
-  uint32_t current = 0;
-  current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
-  current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
-  current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
-  current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
+       uint32_t current = 0;
+       current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
+       current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
+       current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
+       current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
 
-  return current;
+       return current;
 }
 
 /* function: ip_checksum_adjust
@@ -128,15 +135,16 @@ uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) {
  * new_hdr_sum - the pseudo-header checksum of the translated packet
  * returns: the new header checksum in network byte order
  */
-uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
-  // Algorithm suggested in RFC 1624.
-  // http://tools.ietf.org/html/rfc1624#section-3
-  checksum = ~checksum;
-  uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
-  uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
-  if (folded_sum > folded_old) {
-    return ~(folded_sum - folded_old);
-  } else {
-    return ~(folded_sum - folded_old - 1);  // end-around borrow
-  }
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum)
+{
+       // Algorithm suggested in RFC 1624.
+       // http://tools.ietf.org/html/rfc1624#section-3
+       checksum = ~checksum;
+       uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+       uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+       if (folded_sum > folded_old) {
+               return ~(folded_sum - folded_old);
+       } else {
+               return ~(folded_sum - folded_old - 1); // end-around borrow
+       }
 }
diff --git a/src/clatd.c b/src/clatd.c
new file mode 100644 (file)
index 0000000..7333632
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ * 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.
+ *
+ * clatd.c - tun interface setup and main event loop
+ */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <linux/filter.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_tun.h>
+//#include <net/if.h>
+#include <sys/capability.h>
+#include <sys/uio.h>
+#include <glib.h>
+#include <net/route.h>
+//#include <private/android_filesystem_config.h>
+
+#include "clatd.h"
+#include "config.h"
+#include "dump.h"
+#include "getaddr.h"
+#include "log.h"
+#include "mtu.h"
+//#include "resolv_netid.h"
+#include "ring.h"
+#include "setif.h"
+#include "translate.h"
+#include "tun.h"
+
+/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */
+#define MTU_DELTA 28
+
+volatile sig_atomic_t running = 1;
+
+/* function: stop_loop
+ * signal handler: stop the event loop
+ */
+void stop_loop() { running = 0; }
+
+int set_nonblocking(int fd)
+{
+       int flags = fcntl(fd, F_GETFL);
+       if (flags == -1) {
+               return flags;
+       }
+       return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static int __get_interface_index(const char *interface_name)
+{
+       struct ifreq ifr;
+       int sock = 0;
+       int result = 0;
+
+       if (interface_name == NULL) {
+               CLATD_LOG(LOG_ERROR,"Inteface name is NULL");
+               return -1;
+       }
+
+       errno = 0;
+       sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sock < 0) {
+               CLATD_LOG(LOG_ERROR,"Failed to create socket : %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name) - 1);
+       result = ioctl(sock, SIOCGIFINDEX, &ifr);
+       close(sock);
+
+       if (result < 0) {
+               CLATD_LOG(LOG_ERROR,"Failed to get ifr index: %s", strerror(errno));
+               return -1;
+       }
+
+       return ifr.ifr_ifindex;
+}
+
+static int __add_default_route(gchar *ip_addr, gchar *subnet, gchar *interface, gint address_family)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr_in;
+       int sock;
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       ifr.ifr_ifindex = __get_interface_index(interface);
+
+       if (ifr.ifr_ifindex < 0)
+               return -1;
+
+       strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+       memset(&rt, 0, sizeof(rt));
+
+       rt.rt_flags = RTF_UP;
+
+       memset(&addr_in, 0, sizeof(struct sockaddr_in));
+       addr_in.sin_family = address_family;
+       addr_in.sin_addr.s_addr = inet_addr(ip_addr);
+       memcpy(&rt.rt_dst, &addr_in, sizeof(rt.rt_dst));
+
+       memset(&addr_in, 0, sizeof(struct sockaddr_in));
+       addr_in.sin_family = address_family;
+       addr_in.sin_addr.s_addr = inet_addr(subnet);
+       memcpy(&rt.rt_genmask, &addr_in, sizeof(rt.rt_genmask));
+
+       rt.rt_dev = ifr.ifr_name;
+       rt.rt_metric = 1001;
+
+       errno = 0;
+       sock = socket(PF_INET, SOCK_DGRAM, 0);
+
+       if (sock < 0) {
+               CLATD_LOG(LOG_ERROR,"Failed to create socket : %s", strerror(errno));
+               return -1;
+       }
+
+       if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+               CLATD_LOG(LOG_ERROR,"Failed to set route address : %s", strerror(errno));
+               close(sock);
+               return -1;
+       }
+
+       close(sock);
+
+       return 1;
+}
+
+static gboolean add_default_route(gchar *ip_addr, gchar *netmask,
+               gchar *interface, gint address_family)
+{
+       CLATD_LOG(LOG_ERROR,"ip_addr(%s), netmask(%s), interface(%s)", ip_addr, netmask, interface);
+
+       if (address_family == AF_INET) {
+               if (ip_addr == NULL || netmask == NULL || interface == NULL) {
+                       CLATD_LOG(LOG_ERROR,"Invalid parameter");
+                       return FALSE;
+               }
+
+               if (__add_default_route(ip_addr, netmask, interface, address_family) < 0) {
+                       CLATD_LOG(LOG_ERROR,"Failed to add default route");
+                       return FALSE;
+               }
+       }
+
+       CLATD_LOG(LOG_INFO, "Successfully added default route");
+       return TRUE;
+}
+
+/* function: configure_packet_socket
+ * Binds the packet socket and attaches the receive filter to it.
+ *   sock - the socket to configure
+ */
+int configure_packet_socket(int sock) {
+  struct sockaddr_ll sll = {
+    .sll_family   = AF_PACKET,
+    .sll_protocol = htons(ETH_P_IPV6),
+    .sll_ifindex  = if_nametoindex(Global_Clatd_Config.default_pdp_interface),
+    .sll_pkttype  = PACKET_OTHERHOST,  // The 464xlat IPv6 address is not assigned to the kernel.
+  };
+  if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) {
+    CLATD_LOG(LOG_FATAL, "binding packet socket: %s", strerror(errno));
+    return 0;
+  }
+
+  uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32;
+
+  // clang-format off
+  struct sock_filter filter_code[] = {
+    // Load the first four bytes of the IPv6 destination address (starts 24 bytes in).
+    // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads
+    // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it
+    // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other
+    // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet).
+    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  24),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[0]), 0, 7),
+    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  28),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[1]), 0, 5),
+    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  32),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[2]), 0, 3),
+    BPF_STMT(BPF_LD  | BPF_W   | BPF_ABS,  36),
+    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    htonl(ipv6[3]), 0, 1),
+    BPF_STMT(BPF_RET | BPF_K,              PACKETLEN),
+    BPF_STMT(BPF_RET | BPF_K,              0),
+  };
+  // clang-format on
+  struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code };
+
+  if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
+    CLATD_LOG(LOG_FATAL, "attach packet filter failed: %s", strerror(errno));
+    return 0;
+  }
+
+  return 1;
+}
+
+/* function: ipv4_address_generate
+ * picks a free IPv4 address from the local subnet or exits if there are no free addresses
+ *   returns: the IPv4 address as an in_addr_t
+ */
+static in_addr_t ipv4_address_generate() {
+  // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically,
+  // there is a race here - if another clatd calls config_select_ipv4_address after we do, but
+  // before we call add_address, it can end up having the same IP address as we do. But the time
+  // window in which this can happen is extremely small, and even if we end up with a duplicate
+  // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go
+  // down.
+  in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet,
+                                                   Global_Clatd_Config.ipv4_local_prefixlen);
+  if (localaddr == INADDR_NONE) {
+    CLATD_LOG(LOG_FATAL, "No free IPv4 address in %s/%d",
+           inet_ntoa(Global_Clatd_Config.ipv4_local_subnet),
+           Global_Clatd_Config.ipv4_local_prefixlen);
+  }
+  return localaddr;
+}
+
+/* function: ipv4_address_from_cmdline
+ * configures the IPv4 address specified on the command line, or exits if the address is not valid
+ *   v4_addr - a string, the IPv4 address
+ *   returns: the IPv4 address as an in_addr_t
+ */
+static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) {
+  in_addr_t localaddr = INADDR_NONE;
+  if (!inet_pton(AF_INET, v4_addr, &localaddr)) {
+    CLATD_LOG(LOG_FATAL, "Invalid IPv4 address %s", v4_addr);
+  }
+  return localaddr;
+}
+
+/* function: configure_tun_ip
+ * configures the ipv4 and ipv6 addresses on the tunnel interface
+ *   tunnel - tun device data
+ */
+void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr) {
+  if (v4_addr) {
+    Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr);
+  } else {
+    Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate();
+  }
+
+  char addrstr[INET_ADDRSTRLEN];
+  inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr));
+  CLATD_LOG(LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4);
+
+  // Configure the interface before bringing it up. As soon as we bring the interface up, the
+  // framework will be notified and will assume the interface's configuration has been finalized.
+  int status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32,
+                           &Global_Clatd_Config.ipv4_local_subnet);
+  if (status < 0) {
+    CLATD_LOG(LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status));
+    return;
+  }
+
+  status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu);
+  if (status < 0) {
+    CLATD_LOG(LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status));
+    return;
+  }
+}
+
+/* function: set_capability
+ * set the permitted, effective and inheritable capabilities of the current
+ * thread
+ */
+void set_capability(uint64_t target_cap) {
+  struct __user_cap_header_struct header = {
+    .version = _LINUX_CAPABILITY_VERSION_3,
+    .pid     = 0  // 0 = change myself
+  };
+  struct __user_cap_data_struct cap[_LINUX_CAPABILITY_U32S_3] = {};
+
+  cap[0].permitted = cap[0].effective = cap[0].inheritable = target_cap;
+  cap[1].permitted = cap[1].effective = cap[1].inheritable = target_cap >> 32;
+
+  if (capset(&header, cap) < 0) {
+    CLATD_LOG(LOG_FATAL, "capset failed: %s", strerror(errno));
+    return;
+  }
+}
+
+/* function: drop_root_but_keep_caps
+ * drops root privs but keeps the needed capabilities
+ */
+void drop_root_but_keep_caps() {
+  gid_t groups[] = { AID_INET, AID_VPN };
+  if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) < 0) {
+    CLATD_LOG(LOG_FATAL, "setgroups failed: %s", strerror(errno));
+    return;
+  }
+
+  prctl(PR_SET_KEEPCAPS, 1);
+
+  if (setresgid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) {
+    CLATD_LOG(LOG_FATAL, "setresgid failed: %s", strerror(errno));
+    return;
+  }
+  if (setresuid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) {
+    CLATD_LOG(LOG_FATAL, "setresuid failed: %s", strerror(errno));
+    return;
+  }
+
+  // keep CAP_NET_RAW capability to open raw socket, and CAP_IPC_LOCK for mmap
+  // to lock memory.
+  set_capability((1 << CAP_NET_ADMIN) |
+                 (1 << CAP_NET_RAW) |
+                 (1 << CAP_IPC_LOCK));
+}
+
+/* function: open_sockets
+ * opens a packet socket to receive IPv6 packets and a raw socket to send them
+ *   tunnel - tun device data
+ *   mark - the socket mark to use for the sending raw socket
+ */
+void open_sockets(struct tun_data *tunnel, uint32_t mark) {
+  int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW);
+  if (rawsock < 0) {
+    CLATD_LOG(LOG_FATAL, "raw socket failed: %s", strerror(errno));
+    return;
+  }
+
+  int off = 0;
+  if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) {
+    CLATD_LOG(LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno));
+  }
+  if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+    CLATD_LOG(LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno));
+  }
+
+  tunnel->write_fd6 = rawsock;
+
+  tunnel->read_fd6 = ring_create(tunnel);
+  if (tunnel->read_fd6 < 0) {
+    return;
+  }
+}
+
+int ipv6_address_changed(const char *interface) {
+  union anyip *interface_ip;
+
+  interface_ip = getinterface_ip(interface, AF_INET6);
+  if (!interface_ip) {
+    CLATD_LOG(LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface);
+    return 1;
+  }
+
+  if (!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) {
+    char oldstr[INET6_ADDRSTRLEN];
+    char newstr[INET6_ADDRSTRLEN];
+    inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, oldstr, sizeof(oldstr));
+    inet_ntop(AF_INET6, &interface_ip->ip6, newstr, sizeof(newstr));
+    CLATD_LOG(LOG_INFO, "IPv6 prefix on %s changed: %s -> %s", interface, oldstr, newstr);
+    free(interface_ip);
+    return 1;
+  } else {
+    free(interface_ip);
+    return 0;
+  }
+}
+
+/* function: clat_ipv6_address_from_interface
+ * picks the clat IPv6 address based on the interface address
+ *   interface - uplink interface name
+ *   returns: 1 on success, 0 on failure
+ */
+static int clat_ipv6_address_from_interface(const char *interface) {
+  union anyip *interface_ip;
+
+  // TODO: check that the prefix length is /64.
+  interface_ip = getinterface_ip(interface, AF_INET6);
+  if (!interface_ip) {
+    CLATD_LOG(LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface);
+    return 0;
+  }
+
+  // Generate an interface ID.
+  config_generate_local_ipv6_subnet(&interface_ip->ip6);
+
+  Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6;
+  free(interface_ip);
+  return 1;
+}
+
+/* function: clat_ipv6_address_from_cmdline
+ * parses the clat IPv6 address from the command line
+ *   v4_addr - a string, the IPv6 address
+ *   returns: 1 on success, 0 on failure
+ */
+static int clat_ipv6_address_from_cmdline(const char *v6_addr) {
+  if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) {
+    CLATD_LOG(LOG_FATAL, "Invalid source address %s", v6_addr);
+    return 0;
+  }
+
+  return 1;
+}
+
+/* function: configure_clat_ipv6_address
+ * picks the clat IPv6 address and configures packet translation to use it.
+ *   tunnel - tun device data
+ *   interface - uplink interface name
+ *   returns: 1 on success, 0 on failure
+ */
+int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface,
+                                const char *v6_addr) {
+  int ret;
+  if (v6_addr) {
+    ret = clat_ipv6_address_from_cmdline(v6_addr);
+  } else {
+    ret = clat_ipv6_address_from_interface(interface);
+  }
+  if (!ret) return 0;
+
+  char addrstr[INET6_ADDRSTRLEN];
+  inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr));
+  CLATD_LOG(LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface);
+
+  // Start translating packets to the new prefix.
+  add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface);
+
+  // Update our packet socket filter to reflect the new 464xlat IP address.
+  if (!configure_packet_socket(tunnel->read_fd6)) {
+    // Things aren't going to work. Bail out and hope we have better luck next time.
+    // We don't log an error here because configure_packet_socket has already done so.
+    return 0;
+  }
+
+  return 1;
+}
+
+/* function: configure_interface
+ * reads the configuration and applies it to the interface
+ *   uplink_interface - network interface to use to reach the ipv6 internet
+ *   plat_prefix      - PLAT prefix to use
+ *   tunnel           - tun device data
+ *   net_id           - NetID to use, NETID_UNSET indicates use of default network
+ */
+void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr,
+               const char *v6_addr, struct tun_data *tunnel, unsigned net_id) {
+#if defined TIZEN_EXT
+       if (!read_config("/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) {
+#else
+       if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) {
+#endif
+               CLATD_LOG(LOG_FATAL, "read_config failed");
+               return;
+       }
+
+       if (Global_Clatd_Config.mtu > MAXMTU) {
+               CLATD_LOG(LOG_WARN, "Max MTU is %d, requested %d", MAXMTU, Global_Clatd_Config.mtu);
+               Global_Clatd_Config.mtu = MAXMTU;
+       }
+       if (Global_Clatd_Config.mtu <= 0) {
+               Global_Clatd_Config.mtu = getifmtu(Global_Clatd_Config.default_pdp_interface);
+               CLATD_LOG(LOG_WARN, "ifmtu=%d", Global_Clatd_Config.mtu);
+       }
+       if (Global_Clatd_Config.mtu < 1280) {
+               CLATD_LOG(LOG_WARN, "mtu too small = %d", Global_Clatd_Config.mtu);
+               Global_Clatd_Config.mtu = 1280;
+       }
+
+       if (Global_Clatd_Config.ipv4mtu <= 0 ||
+                       Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) {
+               Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA;
+               CLATD_LOG(LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu);
+       }
+
+#if defined TIZEN_EXT
+       int error = tun_alloc(tunnel->device4, tunnel->fd4, sizeof(tunnel->device4));
+       if (error < 0) {
+               CLATD_LOG(LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno));
+               return;
+       }
+
+       error = set_nonblocking(tunnel->fd4);
+       if (error < 0) {
+               CLATD_LOG(LOG_FATAL, "set_nonblocking failed: %s", strerror(errno));
+               return;
+       }
+#endif
+
+       configure_tun_ip(tunnel, v4_addr);
+
+       add_default_route("0.0.0.0", "0.0.0.0", tunnel->device4, AF_INET);
+
+       if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) {
+               return;
+       }
+}
+
+/* function: read_packet
+ * reads a packet from the tunnel fd and translates it
+ *   read_fd  - file descriptor to read original packet from
+ *   write_fd - file descriptor to write translated packet to
+ *   to_ipv6  - whether the packet is to be translated to ipv6 or ipv4
+ */
+void read_packet(int read_fd, int write_fd, int to_ipv6) {
+  ssize_t readlen;
+  uint8_t buf[PACKETLEN], *packet;
+
+  readlen = read(read_fd, buf, PACKETLEN);
+
+  if (readlen < 0) {
+    if (errno != EAGAIN) {
+      CLATD_LOG(LOG_WARN, "read_packet/read error: %s", strerror(errno));
+    }
+    return;
+  } else if (readlen == 0) {
+    CLATD_LOG(LOG_WARN, "read_packet/tun interface removed");
+    running = 0;
+    return;
+  }
+
+  struct tun_pi *tun_header = (struct tun_pi *)buf;
+  if (readlen < (ssize_t)sizeof(*tun_header)) {
+    CLATD_LOG(LOG_WARN, "read_packet/short read: got %ld bytes", readlen);
+    return;
+  }
+
+  uint16_t proto = ntohs(tun_header->proto);
+  if (proto != ETH_P_IP) {
+    CLATD_LOG(LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto);
+    return;
+  }
+
+  if (tun_header->flags != 0) {
+    CLATD_LOG(LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags);
+  }
+
+  packet = (uint8_t *)(tun_header + 1);
+  readlen -= sizeof(*tun_header);
+  translate_packet(write_fd, to_ipv6, packet, readlen);
+}
+
+/* function: event_loop
+ * reads packets from the tun network interface and passes them down the stack
+ *   tunnel - tun device data
+ */
+void event_loop(struct tun_data *tunnel) {
+  time_t last_interface_poll;
+  struct pollfd wait_fd[] = {
+    { tunnel->read_fd6, POLLIN, 0 },
+    { tunnel->fd4, POLLIN, 0 },
+  };
+
+  // start the poll timer
+  last_interface_poll = time(NULL);
+
+  while (running) {
+    if (poll(wait_fd, ARRAY_SIZE(wait_fd), NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) {
+      if (errno != EINTR) {
+        CLATD_LOG(LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno));
+      }
+    } else {
+      if (wait_fd[0].revents & POLLIN) {
+        ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */);
+      }
+      // If any other bit is set, assume it's due to an error (i.e. POLLERR).
+      if (wait_fd[0].revents & ~POLLIN) {
+        // ring_read doesn't clear the error indication on the socket.
+        recv(tunnel->read_fd6, NULL, 0, MSG_PEEK);
+        CLATD_LOG(LOG_WARN, "event_loop: clearing error on read_fd6: %s", strerror(errno));
+      }
+
+      // Call read_packet if the socket has data to be read, but also if an
+      // error is waiting. If we don't call read() after getting POLLERR, a
+      // subsequent poll() will return immediately with POLLERR again,
+      // causing this code to spin in a loop. Calling read() will clear the
+      // socket error flag instead.
+      if (wait_fd[1].revents) {
+        read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */);
+      }
+    }
+
+    time_t now = time(NULL);
+    if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) {
+      if (ipv6_address_changed(Global_Clatd_Config.default_pdp_interface)) {
+        break;
+      }
+    }
+  }
+}
diff --git a/src/clatd_microbenchmark.c b/src/clatd_microbenchmark.c
new file mode 100644 (file)
index 0000000..ea58a8a
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright 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.
+ *
+ * clatd_microbenchmark.c - micro-benchmark for clatd tun send path
+ *
+ * Run with:
+ *
+ * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark
+ * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark
+ *
+ */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "netutils/checksum.h"
+#include "tun.h"
+
+#define DEVICENAME "clat4"
+
+#define PORT 51339
+#define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr))
+#define NUMPACKETS 1000000
+#define SEC_TO_NANOSEC (1000 * 1000 * 1000)
+
+void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) {
+  sin->sin_family      = AF_INET;
+  sin->sin_port        = 0;
+  sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+void die(const char *str) {
+  perror(str);
+  exit(1);
+}
+
+int setup_tun() {
+  int fd = tun_open();
+  if (fd == -1) die("tun_open");
+
+  char dev[IFNAMSIZ] = DEVICENAME;
+  int ret            = tun_alloc(dev, fd, sizeof(dev));
+  if (ret == -1) die("tun_alloc");
+  struct ifreq ifr = {
+    .ifr_name = DEVICENAME,
+  };
+
+  int s = socket(AF_INET, SOCK_DGRAM, 0);
+  init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "192.0.0.4");
+  if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR");
+  init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "255.255.255.248");
+  if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK");
+  if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS");
+  ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
+  if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS");
+  return fd;
+}
+
+int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) {
+  struct tun_pi tun = { 0, htons(ETH_P_IP) };
+  struct udphdr udp = {
+    .source = htons(1234),
+    .dest   = htons(PORT),
+    .len    = htons(len + sizeof(udp)),
+    .check  = 0,
+  };
+  struct iphdr ip = {
+    .version  = 4,
+    .ihl      = 5,
+    .tot_len  = htons(len + sizeof(ip) + sizeof(udp)),
+    .frag_off = htons(IP_DF),
+    .ttl      = 55,
+    .protocol = IPPROTO_UDP,
+    .saddr    = htonl(0xc0000006),  // 192.0.0.6
+    .daddr    = htonl(0xc0000004),  // 192.0.0.4
+  };
+  clat_packet out = {
+    { &tun, sizeof(tun) },  // tun header
+    { &ip, sizeof(ip) },    // IP header
+    { NULL, 0 },            // Fragment header
+    { &udp, sizeof(udp) },  // Transport header
+    { NULL, 0 },            // ICMP error IP header
+    { NULL, 0 },            // ICMP error fragment header
+    { NULL, 0 },            // ICMP error transport header
+    { payload, len },       // Payload
+  };
+
+  ip.check = ip_checksum(&ip, sizeof(ip));
+
+  uint32_t sum;
+  sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len));
+  sum = ip_checksum_add(sum, &udp, sizeof(udp));
+  sum += payload_checksum;
+  udp.check = ip_checksum_finish(sum);
+
+  return send_tun(fd, out, sizeof(out) / sizeof(out[0]));
+}
+
+double timedelta(const struct timespec tv1, const struct timespec tv2) {
+  struct timespec end = tv2;
+  if (end.tv_nsec < tv1.tv_nsec) {
+    end.tv_sec -= 1;
+    end.tv_nsec += SEC_TO_NANOSEC;
+  }
+  double seconds = (end.tv_sec - tv1.tv_sec);
+  seconds += (((double)(end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC);
+  return seconds;
+}
+
+void benchmark(const char *name, int fd, int s, int num, int do_read, uint8_t payload[], int len,
+               uint32_t payload_sum) {
+  int i;
+  char buf[4096];
+  struct timespec tv1, tv2;
+  int write_err = 0, read_err = 0;
+  clock_gettime(CLOCK_MONOTONIC, &tv1);
+  for (i = 0; i < num; i++) {
+    if (send_packet(fd, payload, len, payload_sum) == -1) write_err++;
+    if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) {
+      read_err++;
+      if (errno == ETIMEDOUT) {
+        printf("Timed out after %d packets!\n", i);
+        break;
+      }
+    }
+  }
+  clock_gettime(CLOCK_MONOTONIC, &tv2);
+  double seconds = timedelta(tv1, tv2);
+  int pps        = (int)(i / seconds);
+  double mbps    = (i * PAYLOADSIZE / 1000000 * 8 / seconds);
+  printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps);
+  printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", read_err, (float)read_err / i * 100,
+         write_err, (float)write_err / i * 100);
+}
+
+int open_socket() {
+  int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+
+  int on = 1;
+  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR");
+
+  struct timeval tv = { 1, 0 };
+  if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO");
+
+  struct sockaddr_in addr = {
+    .sin_family = AF_INET,
+    .sin_port   = ntohs(PORT),
+    .sin_addr   = { INADDR_ANY },
+  };
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) die("bind");
+
+  return sock;
+}
+
+int main() {
+  int fd   = setup_tun();
+  int sock = open_socket();
+
+  int i;
+  uint8_t payload[PAYLOADSIZE];
+  for (i = 0; i < (int)sizeof(payload); i++) {
+    payload[i] = (uint8_t)i;
+  }
+  uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload));
+
+  // Check things are working.
+  char buf[4096];
+  if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet");
+  if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom");
+
+  benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum);
+  close(fd);
+
+  fd = setup_tun();
+  benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum);
+  close(fd);
+
+  fd = setup_tun();
+  benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum);
+  close(fd);
+
+  return 0;
+}
diff --git a/src/config.c b/src/config.c
new file mode 100644 (file)
index 0000000..057664c
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2011 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.
+ *
+ * config.c - configuration settings
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+//#include <cutils/config_utils.h>
+//#include <checksum.h>
+//#include <netutils/ifc.h>
+
+#include "config_utils.h"
+#include "checksum.h"
+#include "clatd.h"
+#include "config.h"
+#include "dns64.h"
+#include "getaddr.h"
+#include "ifc.h"
+#include "log.h"
+
+/* function: config_item_str
+ * locates the config item and returns the pointer to a string, or NULL on failure.  Caller frees
+ * pointer
+ *   root       - parsed configuration
+ *   item_name  - name of config item to locate
+ *   defaultvar - value to use if config item isn't present
+ */
+char *config_item_str(cnode *root, const char *item_name, const char *defaultvar) {
+  const char *tmp;
+
+  if (!(tmp = config_str(root, item_name, defaultvar))) {
+    CLATD_LOG(LOG_FATAL, "%s config item needed", item_name);
+    return NULL;
+  }
+  return strdup(tmp);
+}
+
+/* function: config_item_int16_t
+ * locates the config item, parses the integer, and returns the pointer ret_val_ptr, or NULL on
+ * failure
+ *   root        - parsed configuration
+ *   item_name   - name of config item to locate
+ *   defaultvar  - value to use if config item isn't present
+ *   ret_val_ptr - pointer for return value storage
+ */
+int16_t *config_item_int16_t(cnode *root, const char *item_name, const char *defaultvar,
+                             int16_t *ret_val_ptr) {
+  const char *tmp;
+  char *endptr;
+  long int conf_int;
+
+  if (!(tmp = config_str(root, item_name, defaultvar))) {
+    CLATD_LOG(LOG_FATAL, "%s config item needed", item_name);
+    return NULL;
+  }
+
+  errno    = 0;
+  conf_int = strtol(tmp, &endptr, 10);
+  if (errno > 0) {
+    CLATD_LOG(LOG_FATAL, "%s config item is not numeric: %s (error=%s)", item_name, tmp,
+           strerror(errno));
+    return NULL;
+  }
+  if (endptr == tmp || *tmp == '\0') {
+    CLATD_LOG(LOG_FATAL, "%s config item is not numeric: %s", item_name, tmp);
+    return NULL;
+  }
+  if (*endptr != '\0') {
+    CLATD_LOG(LOG_FATAL, "%s config item contains non-numeric characters: %s", item_name,
+           endptr);
+    return NULL;
+  }
+  if (conf_int > INT16_MAX || conf_int < INT16_MIN) {
+    CLATD_LOG(LOG_FATAL, "%s config item is too big/small: %d", item_name, conf_int);
+    return NULL;
+  }
+  *ret_val_ptr = conf_int;
+  return ret_val_ptr;
+}
+
+/* function: config_item_ip
+ * locates the config item, parses the ipv4 address, and returns the pointer ret_val_ptr, or NULL on
+ * failure
+ *   root        - parsed configuration
+ *   item_name   - name of config item to locate
+ *   defaultvar  - value to use if config item isn't present
+ *   ret_val_ptr - pointer for return value storage
+ */
+struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *defaultvar,
+                               struct in_addr *ret_val_ptr) {
+  const char *tmp;
+  int status;
+
+  if (!(tmp = config_str(root, item_name, defaultvar))) {
+    CLATD_LOG(LOG_FATAL, "%s config item needed", item_name);
+    return NULL;
+  }
+
+  status = inet_pton(AF_INET, tmp, ret_val_ptr);
+  if (status <= 0) {
+    CLATD_LOG(LOG_FATAL, "invalid IPv4 address specified for %s: %s", item_name, tmp);
+    return NULL;
+  }
+
+  return ret_val_ptr;
+}
+
+/* function: config_item_ip6
+ * locates the config item, parses the ipv6 address, and returns the pointer ret_val_ptr, or NULL on
+ * failure
+ *   root        - parsed configuration
+ *   item_name   - name of config item to locate
+ *   defaultvar  - value to use if config item isn't present
+ *   ret_val_ptr - pointer for return value storage
+ */
+struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char *defaultvar,
+                                 struct in6_addr *ret_val_ptr) {
+  const char *tmp;
+  int status;
+
+  if (!(tmp = config_str(root, item_name, defaultvar))) {
+    CLATD_LOG(LOG_FATAL, "%s config item needed", item_name);
+    return NULL;
+  }
+
+  status = inet_pton(AF_INET6, tmp, ret_val_ptr);
+  if (status <= 0) {
+    CLATD_LOG(LOG_FATAL, "invalid IPv6 address specified for %s: %s", item_name, tmp);
+    return NULL;
+  }
+
+  return ret_val_ptr;
+}
+
+/* function: free_config
+ * frees the memory used by the global config variable
+ */
+void free_config() {
+  if (Global_Clatd_Config.plat_from_dns64_hostname) {
+    free(Global_Clatd_Config.plat_from_dns64_hostname);
+    Global_Clatd_Config.plat_from_dns64_hostname = NULL;
+  }
+}
+
+/* function: ipv6_prefix_equal
+ * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64.
+ *   a1 - first address
+ *   a2 - second address
+ *   returns: 0 if the subnets are different, 1 if they are the same.
+ */
+int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); }
+
+/* function: dns64_detection
+ * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response
+ * with a query backoff timer
+ *   net_id - (optional) netId to use, NETID_UNSET indicates use of default network
+ */
+void dns64_detection(unsigned net_id) {
+  int backoff_sleep, status;
+  struct in6_addr tmp_ptr;
+
+  backoff_sleep = 1;
+
+  while (1) {
+    status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname, net_id, &tmp_ptr);
+    if (status > 0) {
+      memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr));
+      return;
+    }
+    CLATD_LOG(LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep);
+    sleep(backoff_sleep);
+    backoff_sleep *= 2;
+    if (backoff_sleep >= 1800) {
+      // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the
+      // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only
+      // to IPv6 with NAT64).
+      backoff_sleep = 1800;
+    }
+  }
+}
+
+/* function: gen_random_iid
+ * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix
+ *   myaddr            - IPv6 address to write to
+ *   ipv4_local_subnet - clat IPv4 address
+ *   plat_subnet       - NAT64 prefix
+ */
+void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet,
+                    struct in6_addr *plat_subnet) {
+  // Fill last 8 bytes of IPv6 address with random bits.
+#if !defined TIZEN_EXT
+  arc4random_buf(&myaddr->s6_addr[8], 8);
+#endif
+
+  // Make the IID checksum-neutral. That is, make it so that:
+  //   checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6)
+  // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4):
+  //   checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix)
+  // Do this by adjusting the two bytes in the middle of the IID.
+
+  uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12];
+
+  uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet));
+  uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) +
+                ip_checksum_add(0, myaddr, sizeof(*myaddr));
+
+  uint16_t delta      = ip_checksum_adjust(middlebytes, c1, c2);
+  myaddr->s6_addr[11] = delta >> 8;
+  myaddr->s6_addr[12] = delta & 0xff;
+}
+
+// Factored out to a separate function for testability.
+int connect_is_ipv4_address_free(in_addr_t addr) {
+  int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+  if (s == -1) {
+    return 0;
+  }
+
+  // Attempt to connect to the address. If the connection succeeds and getsockname returns the same
+  // the address then the address is already assigned to the system and we can't use it.
+  struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 };
+  socklen_t len          = sizeof(sin);
+  int inuse              = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 &&
+              getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) &&
+              sin.sin_addr.s_addr == addr;
+
+  close(s);
+  return !inuse;
+}
+
+addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free;
+
+/* function: config_select_ipv4_address
+ * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order
+ *   ip        - the IP address from the configuration file
+ *   prefixlen - the length of the prefix from which addresses may be selected.
+ *   returns: the IPv4 address, or INADDR_NONE if no addresses were available
+ */
+in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) {
+  in_addr_t chosen = INADDR_NONE;
+
+  // Don't accept prefixes that are too large because we scan addresses one by one.
+  if (prefixlen < 16 || prefixlen > 32) {
+    return chosen;
+  }
+
+  // All these are in host byte order.
+  in_addr_t mask       = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen);
+  in_addr_t ipv4       = ntohl(ip->s_addr);
+  in_addr_t first_ipv4 = ipv4;
+  in_addr_t prefix     = ipv4 & mask;
+
+  // Pick the first IPv4 address in the pool, wrapping around if necessary.
+  // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0.
+  do {
+    if (config_is_ipv4_address_free(htonl(ipv4))) {
+      chosen = htonl(ipv4);
+      break;
+    }
+    ipv4 = prefix | ((ipv4 + 1) & ~mask);
+  } while (ipv4 != first_ipv4);
+
+  return chosen;
+}
+
+/* function: config_generate_local_ipv6_subnet
+ * generates the local ipv6 subnet when given the interface ip requires config.ipv6_host_id
+ *   interface_ip - in: interface ip, out: local ipv6 host address
+ */
+void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) {
+  int i;
+
+  if (Global_Clatd_Config.use_dynamic_iid) {
+    /* Generate a random interface ID. */
+    gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet,
+                   &Global_Clatd_Config.plat_subnet);
+  } else {
+    /* Use the specified interface ID. */
+    for (i = 2; i < 4; i++) {
+      interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i];
+    }
+  }
+}
+
+/* function: read_config
+ * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on
+ * failure, 1 on success
+ *   file             - filename to parse
+ *   uplink_interface - interface to use to reach the internet and supplier of address space
+ *   plat_prefix      - (optional) plat prefix to use, otherwise follow config file
+ *   net_id           - (optional) netId to use, NETID_UNSET indicates use of default network
+ */
+int read_config(const char *file, const char *uplink_interface, const char *plat_prefix,
+                unsigned net_id) {
+  cnode *root   = config_node("", "");
+  void *tmp_ptr = NULL;
+  unsigned flags;
+
+  if (!root) {
+    CLATD_LOG(LOG_FATAL, "out of memory");
+    return 0;
+  }
+
+  memset(&Global_Clatd_Config, '\0', sizeof(Global_Clatd_Config));
+
+  config_load_file(root, file);
+  if (root->first_child == NULL) {
+    CLATD_LOG(LOG_FATAL, "Could not read config file %s", file);
+    goto failed;
+  }
+
+  Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface);
+  if (!Global_Clatd_Config.default_pdp_interface) goto failed;
+
+  if (!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu)) goto failed;
+
+  if (!config_item_int16_t(root, "ipv4mtu", "-1", &Global_Clatd_Config.ipv4mtu)) goto failed;
+
+  if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET,
+                      &Global_Clatd_Config.ipv4_local_subnet))
+    goto failed;
+
+  if (!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN,
+                           &Global_Clatd_Config.ipv4_local_prefixlen))
+    goto failed;
+
+  if (plat_prefix) {  // plat subnet is coming from the command line
+    if (inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
+      CLATD_LOG(LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix);
+      goto failed;
+    }
+  } else {
+    tmp_ptr = (void *)config_item_str(root, "plat_from_dns64", "yes");
+    if (!tmp_ptr || strcmp(tmp_ptr, "no") == 0) {
+      free(tmp_ptr);
+
+      if (!config_item_ip6(root, "plat_subnet", NULL, &Global_Clatd_Config.plat_subnet)) {
+        CLATD_LOG(LOG_FATAL, "plat_from_dns64 disabled, but no plat_subnet specified");
+        goto failed;
+      }
+    } else {
+      free(tmp_ptr);
+
+      if (!(Global_Clatd_Config.plat_from_dns64_hostname =
+              config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME)))
+        goto failed;
+      dns64_detection(net_id);
+    }
+  }
+
+  if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed;
+
+  /* In order to prevent multiple devices attempting to use the same clat address, never use a
+     statically-configured interface ID on a broadcast interface such as wifi. */
+  if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) {
+    ifc_init();
+    ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags);
+    ifc_close();
+    Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0;
+  } else {
+    Global_Clatd_Config.use_dynamic_iid = 1;
+  }
+
+  return 1;
+
+failed:
+  free(root);
+  free_config();
+  return 0;
+}
+
+/* function; dump_config
+ * prints the current config
+ */
+void dump_config() {
+  char charbuffer[INET6_ADDRSTRLEN];
+
+  CLATD_LOG(LOG_DEBUG, "mtu = %d", Global_Clatd_Config.mtu);
+  CLATD_LOG(LOG_DEBUG, "ipv4mtu = %d", Global_Clatd_Config.ipv4mtu);
+  CLATD_LOG(
+    LOG_DEBUG, "ipv6_local_subnet = %s",
+    inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer)));
+  CLATD_LOG(
+    LOG_DEBUG, "ipv4_local_subnet = %s",
+    inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer)));
+  CLATD_LOG(LOG_DEBUG, "ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen);
+  CLATD_LOG(LOG_DEBUG, "plat_subnet = %s",
+         inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer)));
+  CLATD_LOG(LOG_DEBUG, "default_pdp_interface = %s",
+         Global_Clatd_Config.default_pdp_interface);
+}
diff --git a/src/config_utils.c b/src/config_utils.c
new file mode 100644 (file)
index 0000000..af0576e
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2007 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config_utils.h"
+
+cnode *config_node(const char *name, const char *value)
+{
+       cnode *node;
+
+       node = calloc(sizeof(cnode), 1);
+       if (node) {
+               node->name = name ? name : "";
+               node->value = value ? value : "";
+       }
+
+       return node;
+}
+
+cnode *config_find(cnode *root, const char *name)
+{
+       cnode *node, *match = NULL;
+
+       /* we walk the whole list, as we need to return the last (newest) entry */
+       for (node = root->first_child; node; node = node->next)
+               if (!strcmp(node->name, name))
+                       match = node;
+
+       return match;
+}
+
+static cnode *_config_create(cnode *root, const char *name)
+{
+       cnode *node;
+
+       node = config_node(name, NULL);
+
+       if (root->last_child)
+               root->last_child->next = node;
+       else
+               root->first_child = node;
+
+       root->last_child = node;
+
+       return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+       cnode *node;
+
+       node = config_find(root, name);
+       if (!node)
+               return _default;
+
+       switch (node->value[0]) {
+       case 'y':
+       case 'Y':
+       case '1':
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+const char *config_str(cnode *root, const char *name, const char *_default)
+{
+       cnode *node;
+
+       node = config_find(root, name);
+       if (!node)
+               return _default;
+       return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+       cnode *node;
+
+       node = config_find(root, name);
+       if (node)
+               node->value = value;
+       else {
+               node = _config_create(root, name);
+               node->value = value;
+       }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct {
+       char *data;
+       char *text;
+       int len;
+       char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+       char c;
+       char *s;
+       char *data;
+
+       data = cs->data;
+
+       if (cs->next != 0) {
+               c = cs->next;
+               cs->next = 0;
+               goto got_c;
+       }
+
+ restart:
+       for (;; ) {
+               c = *data++;
+ got_c:
+               if (isspace(c))
+                       continue;
+
+               switch (c) {
+               case 0:
+                       return T_EOF;
+
+               case '#':
+                       for (;; ) {
+                               switch (*data) {
+                               case 0:
+                                       cs->data = data;
+                                       return T_EOF;
+
+                               case '\n':
+                                       cs->data = data + 1;
+                                       goto restart;
+
+                               default:
+                                       data++;
+                               }
+                       }
+                       break;
+
+               case '.':
+                       cs->data = data;
+                       return T_DOT;
+
+               case '{':
+                       cs->data = data;
+                       return T_OBRACE;
+
+               case '}':
+                       cs->data = data;
+                       return T_CBRACE;
+
+               default:
+                       s = data - 1;
+
+                       if (value) {
+                               for (;; ) {
+                                       if (*data == 0) {
+                                               cs->data = data;
+                                               break;
+                                       }
+                                       if (*data == '\n') {
+                                               cs->data = data + 1;
+                                               *data-- = 0;
+                                               break;
+                                       }
+                                       data++;
+                               }
+
+                               /* strip trailing whitespace */
+                               while (data > s) {
+                                       if (!isspace(*data)) break;
+                                       *data-- = 0;
+                               }
+
+                               goto got_text;
+                       } else {
+                               for (;; ) {
+                                       if (isspace(*data)) {
+                                               *data = 0;
+                                               cs->data = data + 1;
+                                               goto got_text;
+                                       }
+                                       switch (*data) {
+                                       case 0:
+                                               cs->data = data;
+                                               goto got_text;
+
+                                       case '.':
+                                       case '{':
+                                       case '}':
+                                               cs->next = *data;
+                                               *data = 0;
+                                               cs->data = data + 1;
+                                               goto got_text;
+
+                                       default:
+                                               data++;
+                                       }
+                               }
+                       }
+               }
+       }
+
+ got_text:
+       cs->text = s;
+       return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+       int tok = _lex(cs, value);
+       printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+              tok == T_TEXT ? cs->text : "");
+       return tok;
+}
+#else
+#define lex(cs, v) _lex(cs, v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+       for (;; ) {
+               switch (lex(cs, 0)) {
+               case T_TEXT:
+                       if (parse_expr(cs, node)) return -1;
+                       continue;
+
+               case T_CBRACE:
+                       return 0;
+
+               default:
+                       return -1;
+               }
+       }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+       cnode *node;
+
+       /* last token was T_TEXT */
+       node = config_find(root, cs->text);
+       if (!node || *node->value)
+               node = _config_create(root, cs->text);
+
+       for (;; ) {
+               switch (lex(cs, 1)) {
+               case T_DOT:
+                       if (lex(cs, 0) != T_TEXT)
+                               return -1;
+                       node = _config_create(node, cs->text);
+                       continue;
+
+               case T_TEXT:
+                       node->value = cs->text;
+                       return 0;
+
+               case T_OBRACE:
+                       return parse_block(cs, node);
+
+               default:
+                       return -1;
+               }
+       }
+}
+
+void config_load(cnode *root, char *data)
+{
+       if (data != 0) {
+               cstate cs;
+               cs.data = data;
+               cs.next = 0;
+
+               for (;; ) {
+                       switch (lex(&cs, 0)) {
+                       case T_TEXT:
+                               if (parse_expr(&cs, root))
+                                       return;
+                               break;
+
+                       default:
+                               return;
+                       }
+               }
+       }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+       char *data;
+       data = load_file(fn, 0);
+       config_load(root, data);
+}
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..64016b7
--- /dev/null
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "generated-code.h"
+
+struct _clat_dbus {
+       GDBusObjectManagerServer *gdoms;
+       GDBusConnection *conn;
+       struct _async_cb_data {
+               on_clat_dbus_cb cb;
+               gpointer data;
+       } *acd;
+};
+
+static void on_bus_acquired(GDBusConnection *conn, const gchar *name,
+                           gpointer user_data)
+{
+       struct _clat_dbus *cdbus = 0;
+       struct _async_cb_data *acd = 0;
+
+       cdbus = user_data;
+       cdbus->conn = conn;
+       acd = cdbus->acd;
+
+       CLATD_LOG(LOG_FATAL, "acquired");
+
+       g_dbus_object_manager_server_set_connection(cdbus->gdoms, cdbus->conn);
+
+       acd->cb(cdbus, acd->data);
+
+       g_free(acd);
+}
+
+int clat_dbus_init(on_clat_dbus_cb cb, gpointer user_data)
+{
+       struct _clat_dbus *cdbus = 0;
+       struct _async_cb_data *acd = 0;
+       guint id = 0;
+
+       cdbus = g_new0(struct _clat_dbus, 1);
+       acd = g_new0(struct _async_cb_data, 1);
+       acd->data = user_data;
+       acd->cb = cb;
+
+       cdbus->gdoms = g_dbus_object_manager_server_new(CLAT_DBUS_PATH);
+       cdbus->acd = acd;
+
+       id = g_bus_own_name(G_BUS_TYPE_SYSTEM, CLAT_DBUS_SERVICE,
+                           G_BUS_NAME_OWNER_FLAGS_REPLACE, on_bus_acquired, 0, 0,
+                           cdbus, 0);
+
+       if (!id) {
+               g_free(acd);
+               g_free(cdbus);
+               return -1;
+       }
+
+       return 0;
+}
+
+GDBusConnection *clat_dbus_get_connection(struct _clat_dbus *cdbus)
+{
+       g_return_val_if_fail(cdbus != 0, 0);
+       return cdbus->conn;
+}
+
+GDBusInterfaceSkeleton *clat_di_skeleton_new_control()
+{
+       return G_DBUS_INTERFACE_SKELETON(clatd_control_skeleton_new());
+}
+
+
diff --git a/src/dns64.c b/src/dns64.c
new file mode 100644 (file)
index 0000000..12fb2cb
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2011 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.
+ *
+ * dns64.c - find the nat64 prefix with a dns64 lookup
+ */
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "dns64.h"
+#include "log.h"
+#include "clatd.h"
+#include "specific_dns.h"
+#if !defined TIZEN_EXT
+#include "NetdClient.h"
+#include "resolv_netid.h"
+#endif
+
+/* function: plat_prefix
+ * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on
+ * failure
+ *   ipv4_name  - name to lookup
+ *   net_id     - (optional) netId to use, NETID_UNSET indicates use of default network
+ *   prefix     - the plat /96 prefix
+ */
+int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix)
+{
+       const struct addrinfo hints = {
+               .ai_family = AF_INET6,
+       };
+       int status;
+       struct addrinfo *result = NULL;
+       struct in6_addr plat_addr;
+       char plat_addr_str[INET6_ADDRSTRLEN];
+
+       CLATD_LOG(LOG_INFO, "Detecting NAT64 prefix from DNS...");
+
+#if defined TIZEN_EXT
+       if (has_specific_nameserver())
+               status = specific_getaddrinfo(ipv4_name, &hints, &result);
+       else
+               status = getaddrinfo(ipv4_name, NULL, &hints, &result);
+#else
+       // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled).
+       if (net_id != NETID_UNSET) {
+               net_id |= NETID_USE_LOCAL_NAMESERVERS;
+       }
+
+       status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result);
+#endif
+       if (status != 0 || result == NULL) {
+               CLATD_LOG(LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", ipv4_name, status,
+                               gai_strerror(status));
+               return 0;
+       }
+
+       // Use only the first result.  If other records are present, possibly with
+       // differing DNS64 prefixes they are ignored (there is very little sensible
+       // that could be done with them at this time anyway).
+
+       if (result->ai_family != AF_INET6) {
+               CLATD_LOG(LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family);
+               return 0;
+       }
+       plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr;
+       // Only /96 DNS64 prefixes are supported at this time.
+       plat_addr.s6_addr32[3] = 0;
+       freeaddrinfo(result);
+
+       CLATD_LOG(LOG_INFO, "Detected NAT64 prefix %s/96",
+                       inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)));
+       *prefix = plat_addr;
+       return 1;
+}
similarity index 51%
rename from dump.c
rename to src/dump.c
index 27b75d1..9cbdd7a 100644 (file)
--- a/dump.c
  *
  * dump.c - print various headers for debugging
  */
-#include <stdio.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 
 #include <arpa/inet.h>
+//#include <linux/icmp.h>
+#include <netinet/icmp6.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
 #include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
+#include <netinet/udp.h>
 
-#include "debug.h"
 #include "checksum.h"
+
 #include "clatd.h"
-#include "logging.h"
+#include "debug.h"
+#include "log.h"
 
 #if CLAT_DEBUG
 
@@ -44,29 +45,29 @@ void dump_ip(struct iphdr *header) {
   frag_flags = ntohs(header->frag_off);
 
   printf("IP packet\n");
-  printf("header_len = %x\n",header->ihl);
-  printf("version = %x\n",header->version);
-  printf("tos = %x\n",header->tos);
-  printf("tot_len = %x\n",ntohs(header->tot_len));
-  printf("id = %x\n",ntohs(header->id));
+  printf("header_len = %x\n", header->ihl);
+  printf("version = %x\n", header->version);
+  printf("tos = %x\n", header->tos);
+  printf("tot_len = %x\n", ntohs(header->tot_len));
+  printf("id = %x\n", ntohs(header->id));
   printf("frag: ");
-  if(frag_flags & IP_RF) {
+  if (frag_flags & IP_RF) {
     printf("(RF) ");
   }
-  if(frag_flags & IP_DF) {
+  if (frag_flags & IP_DF) {
     printf("DF ");
   }
-  if(frag_flags & IP_MF) {
+  if (frag_flags & IP_MF) {
     printf("MF ");
   }
-  printf("offset = %x\n",frag_flags & IP_OFFMASK);
-  printf("ttl = %x\n",header->ttl);
-  printf("protocol = %x\n",header->protocol);
-  printf("checksum = %x\n",ntohs(header->check));
+  printf("offset = %x\n", frag_flags & IP_OFFMASK);
+  printf("ttl = %x\n", header->ttl);
+  printf("protocol = %x\n", header->protocol);
+  printf("checksum = %x\n", ntohs(header->check));
   inet_ntop(AF_INET, &header->saddr, addrstr, sizeof(addrstr));
-  printf("saddr = %s\n",addrstr);
+  printf("saddr = %s\n", addrstr);
   inet_ntop(AF_INET, &header->daddr, addrstr, sizeof(addrstr));
-  printf("daddr = %s\n",addrstr);
+  printf("daddr = %s\n", addrstr);
 }
 
 /* print ip6 header */
@@ -74,138 +75,138 @@ void dump_ip6(struct ip6_hdr *header) {
   char addrstr[INET6_ADDRSTRLEN];
 
   printf("ipv6\n");
-  printf("version = %x\n",header->ip6_vfc >> 4);
-  printf("traffic class = %x\n",header->ip6_flow >> 20);
-  printf("flow label = %x\n",ntohl(header->ip6_flow & 0x000fffff));
-  printf("payload len = %x\n",ntohs(header->ip6_plen));
-  printf("next header = %x\n",header->ip6_nxt);
-  printf("hop limit = %x\n",header->ip6_hlim);
+  printf("version = %x\n", header->ip6_vfc >> 4);
+  printf("traffic class = %x\n", header->ip6_flow >> 20);
+  printf("flow label = %x\n", ntohl(header->ip6_flow & 0x000fffff));
+  printf("payload len = %x\n", ntohs(header->ip6_plen));
+  printf("next header = %x\n", header->ip6_nxt);
+  printf("hop limit = %x\n", header->ip6_hlim);
 
   inet_ntop(AF_INET6, &header->ip6_src, addrstr, sizeof(addrstr));
-  printf("source = %s\n",addrstr);
+  printf("source = %s\n", addrstr);
 
   inet_ntop(AF_INET6, &header->ip6_dst, addrstr, sizeof(addrstr));
-  printf("dest = %s\n",addrstr);
+  printf("dest = %s\n", addrstr);
 }
 
 /* print icmp header */
 void dump_icmp(struct icmphdr *icmp) {
   printf("ICMP\n");
 
-  printf("icmp.type = %x ",icmp->type);
-  if(icmp->type == ICMP_ECHOREPLY) {
+  printf("icmp.type = %x ", icmp->type);
+  if (icmp->type == ICMP_ECHOREPLY) {
     printf("echo reply");
-  } else if(icmp->type == ICMP_ECHO) {
+  } else if (icmp->type == ICMP_ECHO) {
     printf("echo request");
   } else {
     printf("other");
   }
   printf("\n");
-  printf("icmp.code = %x\n",icmp->code);
-  printf("icmp.checksum = %x\n",ntohs(icmp->checksum));
-  if(icmp->type == ICMP_ECHOREPLY || icmp->type == ICMP_ECHO) {
-    printf("icmp.un.echo.id = %x\n",ntohs(icmp->un.echo.id));
-    printf("icmp.un.echo.sequence = %x\n",ntohs(icmp->un.echo.sequence));
+  printf("icmp.code = %x\n", icmp->code);
+  printf("icmp.checksum = %x\n", ntohs(icmp->checksum));
+  if (icmp->type == ICMP_ECHOREPLY || icmp->type == ICMP_ECHO) {
+    printf("icmp.un.echo.id = %x\n", ntohs(icmp->un.echo.id));
+    printf("icmp.un.echo.sequence = %x\n", ntohs(icmp->un.echo.sequence));
   }
 }
 
 /* print icmp6 header */
 void dump_icmp6(struct icmp6_hdr *icmp6) {
   printf("ICMP6\n");
-  printf("type = %x",icmp6->icmp6_type);
-  if(icmp6->icmp6_type == ICMP6_ECHO_REQUEST) {
+  printf("type = %x", icmp6->icmp6_type);
+  if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) {
     printf("(echo request)");
-  } else if(icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
+  } else if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
     printf("(echo reply)");
   }
   printf("\n");
-  printf("code = %x\n",icmp6->icmp6_code);
+  printf("code = %x\n", icmp6->icmp6_code);
 
-  printf("checksum = %x\n",icmp6->icmp6_cksum);
+  printf("checksum = %x\n", icmp6->icmp6_cksum);
 
-  if((icmp6->icmp6_type == ICMP6_ECHO_REQUEST) || (icmp6->icmp6_type == ICMP6_ECHO_REPLY)) {
-    printf("icmp6_id = %x\n",icmp6->icmp6_id);
-    printf("icmp6_seq = %x\n",icmp6->icmp6_seq);
+  if ((icmp6->icmp6_type == ICMP6_ECHO_REQUEST) || (icmp6->icmp6_type == ICMP6_ECHO_REPLY)) {
+    printf("icmp6_id = %x\n", icmp6->icmp6_id);
+    printf("icmp6_seq = %x\n", icmp6->icmp6_seq);
   }
 }
 
 /* print udp header */
-void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum,
-                      const uint8_t *payload, size_t payload_size) {
+void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const uint8_t *payload,
+                      size_t payload_size) {
   uint16_t my_checksum;
 
   temp_checksum = ip_checksum_add(temp_checksum, udp, sizeof(struct udphdr));
   temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size);
-  my_checksum = ip_checksum_finish(temp_checksum);
+  my_checksum   = ip_checksum_finish(temp_checksum);
 
   printf("UDP\n");
-  printf("source = %x\n",ntohs(udp->source));
-  printf("dest = %x\n",ntohs(udp->dest));
-  printf("len = %x\n",ntohs(udp->len));
-  printf("check = %x (mine %x)\n",udp->check,my_checksum);
+  printf("source = %x\n", ntohs(udp->source));
+  printf("dest = %x\n", ntohs(udp->dest));
+  printf("len = %x\n", ntohs(udp->len));
+  printf("check = %x (mine %x)\n", udp->check, my_checksum);
 }
 
 /* print ipv4/udp header */
-void dump_udp(const struct udphdr *udp, const struct iphdr *ip,
-              const uint8_t *payload, size_t payload_size) {
+void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload,
+              size_t payload_size) {
   uint32_t temp_checksum;
   temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size);
   dump_udp_generic(udp, temp_checksum, payload, payload_size);
 }
 
 /* print ipv6/udp header */
-void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6,
-               const uint8_t *payload, size_t payload_size) {
+void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload,
+               size_t payload_size) {
   uint32_t temp_checksum;
   temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP);
   dump_udp_generic(udp, temp_checksum, payload, payload_size);
 }
 
 /* print tcp header */
-void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) {
+void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size,
+                      uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) {
   uint16_t my_checksum;
 
   temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr));
-  if(options) {
+  if (options) {
     temp_checksum = ip_checksum_add(temp_checksum, options, options_size);
   }
   temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size);
-  my_checksum = ip_checksum_finish(temp_checksum);
+  my_checksum   = ip_checksum_finish(temp_checksum);
 
   printf("TCP\n");
-  printf("source = %x\n",ntohs(tcp->source));
-  printf("dest = %x\n",ntohs(tcp->dest));
-  printf("seq = %x\n",ntohl(tcp->seq));
-  printf("ack = %x\n",ntohl(tcp->ack_seq));
-  printf("d_off = %x\n",tcp->doff);
-  printf("res1 = %x\n",tcp->res1);
+  printf("source = %x\n", ntohs(tcp->source));
+  printf("dest = %x\n", ntohs(tcp->dest));
+  printf("seq = %x\n", ntohl(tcp->seq));
+  printf("ack = %x\n", ntohl(tcp->ack_seq));
+  printf("d_off = %x\n", tcp->doff);
+  printf("res1 = %x\n", tcp->res1);
 #ifdef __BIONIC__
-  printf("CWR = %x\n",tcp->cwr);
-  printf("ECE = %x\n",tcp->ece);
+  printf("CWR = %x\n", tcp->cwr);
+  printf("ECE = %x\n", tcp->ece);
 #else
-  printf("CWR/ECE = %x\n",tcp->res2);
+  printf("CWR/ECE = %x\n", tcp->res2);
 #endif
-  printf("urg = %x  ack = %x  psh = %x  rst = %x  syn = %x  fin = %x\n",
-      tcp->urg, tcp->ack, tcp->psh, tcp->rst, tcp->syn, tcp->fin);
-  printf("window = %x\n",ntohs(tcp->window));
-  printf("check = %x [mine %x]\n",tcp->check,my_checksum);
-  printf("urgent = %x\n",tcp->urg_ptr);
+  printf("urg = %x  ack = %x  psh = %x  rst = %x  syn = %x  fin = %x\n", tcp->urg, tcp->ack,
+         tcp->psh, tcp->rst, tcp->syn, tcp->fin);
+  printf("window = %x\n", ntohs(tcp->window));
+  printf("check = %x [mine %x]\n", tcp->check, my_checksum);
+  printf("urgent = %x\n", tcp->urg_ptr);
 
-  if(options) {
+  if (options) {
     size_t i;
 
     printf("options: ");
-    for(i=0; i<options_size; i++) {
-      printf("%x ",*(options+i));
+    for (i = 0; i < options_size; i++) {
+      printf("%x ", *(options + i));
     }
     printf("\n");
   }
 }
 
 /* print ipv4/tcp header */
-void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip,
-              const uint8_t *payload, size_t payload_size,
-              const uint8_t *options, size_t options_size) {
+void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload,
+              size_t payload_size, const uint8_t *options, size_t options_size) {
   uint32_t temp_checksum;
 
   temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size);
@@ -213,27 +214,27 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip,
 }
 
 /* print ipv6/tcp header */
-void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6,
-               const uint8_t *payload, size_t payload_size,
-               const uint8_t *options, size_t options_size) {
+void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload,
+               size_t payload_size, const uint8_t *options, size_t options_size) {
   uint32_t temp_checksum;
 
-  temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP);
+  temp_checksum =
+    ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP);
   dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
 }
 
 /* generic hex dump */
 void logcat_hexdump(const char *info, const uint8_t *data, size_t len) {
-  char output[PACKETLEN*3+2];
+  char output[PACKETLEN * 3 + 2];
   size_t i;
 
   output[0] = '\0';
-  for(i = 0; i < len && i < PACKETLEN; i++) {
-    snprintf(output + i*3, 4, " %02x", data[i]);
+  for (i = 0; i < len && i < PACKETLEN; i++) {
+    snprintf(output + i * 3, 4, " %02x", data[i]);
   }
-  output[len*3+3] = '\0';
+  output[len * 3 + 3] = '\0';
 
-  logmsg(ANDROID_LOG_WARN,"info %s len %d data%s", info, len, output);
+  CLATD_LOG(LOG_WARN, "info %s len %d data%s", info, len, output);
 }
 
 void dump_iovec(const struct iovec *iov, int iov_len) {
similarity index 76%
rename from getaddr.c
rename to src/getaddr.c
index 5cae78b..086a259 100644 (file)
--- a/getaddr.c
  *
  * getaddr.c - get a locally configured address
  */
+#include <net/if.h>
 #include <netinet/in.h>
-#include <strings.h>
 #include <string.h>
-#include <net/if.h>
+#include <strings.h>
 
+#include <linux/if_addr.h>
 #include <linux/rtnetlink.h>
 #include <netlink/handlers.h>
 #include <netlink/msg.h>
 
 #include "getaddr.h"
+#include "log.h"
 #include "netlink_msg.h"
-#include "logging.h"
 
 // shared state between getinterface_ip and getaddr_cb
 struct target {
@@ -38,8 +39,8 @@ struct target {
 
 /* function: getaddr_cb
  * callback for getinterface_ip
- * msg  - netlink message
- * data - (struct target) info for which address we're looking for
+ *   msg  - netlink message
+ *   data - (struct target) info for which address we're looking for
  */
 static int getaddr_cb(struct nl_msg *msg, void *data) {
   struct ifaddrmsg *ifa_p;
@@ -50,24 +51,22 @@ static int getaddr_cb(struct nl_msg *msg, void *data) {
   ifa_p = (struct ifaddrmsg *)nlmsg_data(nlmsg_hdr(msg));
   rta_p = (struct rtattr *)IFA_RTA(ifa_p);
 
-  if(ifa_p->ifa_index != targ_p->ifindex)
-    return NL_OK;
+  if (ifa_p->ifa_index != targ_p->ifindex) return NL_OK;
 
-  if(ifa_p->ifa_scope != RT_SCOPE_UNIVERSE)
-    return NL_OK;
+  if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return NL_OK;
 
   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) {
+    switch (rta_p->rta_type) {
       case IFA_ADDRESS:
-        if((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) {
+        if ((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) {
           memcpy(&targ_p->ip.ip6, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
           targ_p->foundip = 1;
           return NL_OK;
         }
         break;
       case IFA_LOCAL:
-        if(targ_p->family == AF_INET) {
+        if (targ_p->family == AF_INET) {
           memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
           targ_p->foundip = 1;
           return NL_OK;
@@ -81,9 +80,9 @@ static int getaddr_cb(struct nl_msg *msg, void *data) {
 
 /* function: error_handler
  * error callback for getinterface_ip
- * nla  - source of the error message
- * err  - netlink message
- * arg  - (struct target) info for which address we're looking for
+ *   nla  - source of the error message
+ *   err  - netlink message
+ *   arg  - (struct target) info for which address we're looking for
  */
 static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla,
                          __attribute__((unused)) struct nlmsgerr *err,
@@ -92,9 +91,10 @@ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla,
 }
 
 /* function: getinterface_ip
- * finds the first global non-privacy IP of the given family for the given interface, or returns NULL.  caller frees pointer
- * interface - interface to look for
- * family    - family
+ * finds the first global non-privacy IP of the given family for the given interface, or returns
+ * NULL.  caller frees pointer
+ *   interface - interface to look for
+ *   family    - family
  */
 union anyip *getinterface_ip(const char *interface, int family) {
   struct ifaddrmsg ifa;
@@ -102,18 +102,18 @@ union anyip *getinterface_ip(const char *interface, int family) {
   struct target targ;
   union anyip *retval = NULL;
 
-  targ.family = family;
+  targ.family  = family;
   targ.foundip = 0;
   targ.ifindex = if_nametoindex(interface);
-  if(targ.ifindex == 0) {
-    return NULL; // interface not found
+  if (targ.ifindex == 0) {
+    return NULL;  // interface not found
   }
 
   memset(&ifa, 0, sizeof(ifa));
   ifa.ifa_family = targ.family;
 
   callbacks = nl_cb_alloc(NL_CB_DEFAULT);
-  if(!callbacks) {
+  if (!callbacks) {
     goto cleanup;
   }
   nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, getaddr_cb, &targ);
@@ -122,18 +122,17 @@ union anyip *getinterface_ip(const char *interface, int family) {
   // sends message and waits for a response
   send_ifaddrmsg(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &ifa, callbacks);
 
-  if(targ.foundip) {
+  if (targ.foundip) {
     retval = malloc(sizeof(union anyip));
-    if(!retval) {
-      logmsg(ANDROID_LOG_FATAL,"getinterface_ip/out of memory");
+    if (!retval) {
+      CLATD_LOG(LOG_FATAL, "getinterface_ip/out of memory");
       goto cleanup;
     }
     memcpy(retval, &targ.ip, sizeof(union anyip));
   }
 
 cleanup:
-  if(callbacks)
-    nl_cb_put(callbacks);
+  if (callbacks) nl_cb_put(callbacks);
 
   return retval;
 }
similarity index 85%
rename from icmp.c
rename to src/icmp.c
index 75a4a4d..6ec286e 100644 (file)
--- a/icmp.c
  * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
  */
 
+//#include <linux/icmp.h>
+#include <netinet/icmp6.h>
 #include <netinet/in.h>
 #include <netinet/ip_icmp.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
 
-#include "logging.h"
 #include "icmp.h"
+#include "log.h"
 
 /* function: icmp_guess_ttl
  * Guesses the number of hops a received packet has traversed based on its TTL.
@@ -44,17 +44,13 @@ uint8_t icmp_guess_ttl(uint8_t ttl) {
  * Determines whether an ICMP type is an error message.
  * type: the ICMP type
  */
-int is_icmp_error(uint8_t type) {
-  return type == 3 || type == 11 || type == 12;
-}
+int is_icmp_error(uint8_t type) { return type == 3 || type == 11 || type == 12; }
 
 /* function: is_icmp6_error
  * Determines whether an ICMPv6 type is an error message.
  * type: the ICMPv6 type
  */
-int is_icmp6_error(uint8_t type) {
-  return type < 128;
-}
+int is_icmp6_error(uint8_t type) { return type < 128; }
 
 /* function: icmp_to_icmp6_type
  * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
@@ -79,7 +75,7 @@ uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
   }
 
   // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
-  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
+  CLATD_LOG(LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
   return ICMP6_PARAM_PROB;
 }
 
@@ -112,10 +108,10 @@ uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
         case ICMP_UNREACH_PRECEDENCE_CUTOFF:
           return ICMP6_DST_UNREACH_ADMIN;
 
-        // Otherwise, we don't understand this ICMP type/code combination. Fall through.
+          // Otherwise, we don't understand this ICMP type/code combination. Fall through.
       }
   }
-  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
+  CLATD_LOG(LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
   return 0;
 }
 
@@ -139,7 +135,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
   }
 
   // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
-  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
+  CLATD_LOG(LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
   return ICMP_PARAMETERPROB;
 }
 
@@ -172,10 +168,10 @@ uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
         case ICMP6_DST_UNREACH_NOPORT:
           return ICMP_UNREACH_PORT;
 
-        // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
+          // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
       }
   }
 
-  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
+  CLATD_LOG(LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
   return 0;
 }
diff --git a/src/ifc_utils.c b/src/ifc_utils.c
new file mode 100644 (file)
index 0000000..3833384
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * Copyright 2008, 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 <arpa/inet.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/netlink.h>
+#include <linux/route.h>
+#include <linux/ipv6_route.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+//#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+
+#if defined TIZEN_EXT
+#include "log.h"
+#include "ifc.h"
+#define PROPERTY_KEY_MAX   32
+#else
+#define LOG_TAG "NetUtils"
+#include <cutils/properties.h>
+#include <log/log.h>
+#endif
+
+#if defined(__ANDROID__)
+/* SIOCKILLADDR is an Android extension. */
+#define SIOCKILLADDR 0x8939
+#endif
+
+static int ifc_ctl_sock = -1;
+static int ifc_ctl_sock6 = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#if !defined TIZEN_EXT
+void printerr(char *fmt, ...);
+#define DBG 0
+#endif
+
+#define INET_ADDRLEN 4
+#define INET6_ADDRLEN 16
+
+in_addr_t prefixLengthToIpv4Netmask(int prefix_length)
+{
+       in_addr_t mask = 0;
+
+       // C99 (6.5.7): shifts of 32 bits have undefined results
+       if (prefix_length <= 0 || prefix_length > 32) {
+               return 0;
+       }
+
+       mask = ~mask << (32 - prefix_length);
+       mask = htonl(mask);
+
+       return mask;
+}
+
+int ipv4NetmaskToPrefixLength(in_addr_t mask)
+{
+       int prefixLength = 0;
+       uint32_t m = (uint32_t)ntohl(mask);
+       while (m & 0x80000000) {
+               prefixLength++;
+               m = m << 1;
+       }
+       return prefixLength;
+}
+
+static const char *ipaddr_to_string(in_addr_t addr)
+{
+       struct in_addr in_addr;
+
+       in_addr.s_addr = addr;
+       return inet_ntoa(in_addr);
+}
+
+int string_to_ip(const char *string, struct sockaddr_storage *ss)
+{
+       struct addrinfo hints, *ai;
+       int ret;
+
+       if (ss == NULL) {
+               return -EFAULT;
+       }
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_flags = AI_NUMERICHOST;
+       hints.ai_socktype = SOCK_DGRAM;
+
+       ret = getaddrinfo(string, NULL, &hints, &ai);
+       if (ret == 0) {
+               memcpy(ss, ai->ai_addr, ai->ai_addrlen);
+               freeaddrinfo(ai);
+       }
+
+       return ret;
+}
+
+int ifc_init(void)
+{
+       int ret;
+
+       pthread_mutex_lock(&ifc_sock_mutex);
+       if (ifc_ctl_sock == -1) {
+               ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+               if (ifc_ctl_sock < 0) {
+#if defined TIZEN_EXT
+                       CLATD_LOG(LOG_ERROR, "socket() failed: %s", strerror(errno));
+#else
+                       printerr("socket() failed: %s\n", strerror(errno));
+#endif
+               }
+       }
+
+       ret = ifc_ctl_sock < 0 ? -1 : 0;
+#if !defined TIZEN_EXT
+       if (DBG) printerr("ifc_init_returning %d", ret);
+#endif
+       return ret;
+}
+
+int ifc_init6(void)
+{
+       pthread_mutex_lock(&ifc_sock6_mutex);
+       if (ifc_ctl_sock6 == -1) {
+               ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+               if (ifc_ctl_sock6 < 0) {
+#if defined TIZEN_EXT
+                       CLATD_LOG(LOG_ERROR, "socket() failed: %s", strerror(errno));
+#else
+                       printerr("socket() failed: %s\n", strerror(errno));
+#endif
+               }
+       }
+       return ifc_ctl_sock6 < 0 ? -1 : 0;
+}
+
+void ifc_close(void)
+{
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_close");
+#endif
+    if (ifc_ctl_sock != -1) {
+        (void)close(ifc_ctl_sock);
+        ifc_ctl_sock = -1;
+    }
+    pthread_mutex_unlock(&ifc_sock_mutex);
+}
+
+void ifc_close6(void)
+{
+    if (ifc_ctl_sock6 != -1) {
+        (void)close(ifc_ctl_sock6);
+        ifc_ctl_sock6 = -1;
+    }
+    pthread_mutex_unlock(&ifc_sock6_mutex);
+}
+
+static void ifc_init_ifr(const char *name, struct ifreq *ifr)
+{
+    memset(ifr, 0, sizeof(struct ifreq));
+    strncpy(ifr->ifr_name, name, IFNAMSIZ);
+    ifr->ifr_name[IFNAMSIZ - 1] = 0;
+}
+
+int ifc_get_hwaddr(const char *name, void *ptr)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
+    if(r < 0) return -1;
+
+    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+    return 0;
+}
+
+int ifc_get_ifindex(const char *name, int *if_indexp)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
+    if(r < 0) return -1;
+
+    *if_indexp = ifr.ifr_ifindex;
+    return 0;
+}
+
+static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
+    ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+    return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
+}
+
+int ifc_up(const char *name)
+{
+    int ret = ifc_set_flags(name, IFF_UP, 0);
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_up(%s) = %d", name, ret);
+#endif
+    return ret;
+}
+
+int ifc_down(const char *name)
+{
+    int ret = ifc_set_flags(name, 0, IFF_UP);
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_down(%s) = %d", name, ret);
+#endif
+    return ret;
+}
+
+static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
+{
+    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    sin->sin_addr.s_addr = addr;
+}
+
+int ifc_set_addr(const char *name, in_addr_t addr)
+{
+    struct ifreq ifr;
+    int ret;
+
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, addr);
+
+    ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_set_addr(%s, xx) = %d", name, ret);
+#endif
+    return ret;
+}
+
+/*
+ * Adds or deletes an IP address on an interface.
+ *
+ * Action is one of:
+ * - RTM_NEWADDR (to add a new address)
+ * - RTM_DELADDR (to delete an existing address)
+ *
+ * Returns zero on success and negative errno on failure.
+ */
+int ifc_act_on_address(int action, const char *name, const char *address,
+                       int prefixlen) {
+    int ifindex, s, len, ret;
+    struct sockaddr_storage ss;
+    int saved_errno;
+    void *addr;
+    size_t addrlen;
+    struct {
+        struct nlmsghdr n;
+        struct ifaddrmsg r;
+        // Allow for IPv6 address, headers, IPv4 broadcast addr and padding.
+        char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+                     NLMSG_ALIGN(sizeof(struct rtattr)) +
+                     NLMSG_ALIGN(INET6_ADDRLEN) +
+                     NLMSG_ALIGN(sizeof(struct rtattr)) +
+                     NLMSG_ALIGN(INET_ADDRLEN)];
+    } req;
+    struct rtattr *rta;
+    struct nlmsghdr *nh;
+    struct nlmsgerr *err;
+    char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+             NLMSG_ALIGN(sizeof(struct nlmsgerr)) +
+             NLMSG_ALIGN(sizeof(struct nlmsghdr))];
+
+    // Get interface ID.
+    ifindex = if_nametoindex(name);
+    if (ifindex == 0) {
+        return -errno;
+    }
+
+    // Convert string representation to sockaddr_storage.
+    ret = string_to_ip(address, &ss);
+    if (ret) {
+        return ret;
+    }
+
+    // Determine address type and length.
+    if (ss.ss_family == AF_INET) {
+        struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
+        addr = &sin->sin_addr;
+        addrlen = INET_ADDRLEN;
+    } else if (ss.ss_family == AF_INET6) {
+        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
+        addr = &sin6->sin6_addr;
+        addrlen = INET6_ADDRLEN;
+    } else {
+        return -EAFNOSUPPORT;
+    }
+
+    // Fill in netlink structures.
+    memset(&req, 0, sizeof(req));
+
+    // Netlink message header.
+    req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
+    req.n.nlmsg_type = action;
+    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+    req.n.nlmsg_pid = getpid();
+
+    // Interface address message header.
+    req.r.ifa_family = ss.ss_family;
+    req.r.ifa_prefixlen = prefixlen;
+    req.r.ifa_index = ifindex;
+
+    // Routing attribute. Contains the actual IP address.
+    rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));
+    rta->rta_type = IFA_LOCAL;
+    rta->rta_len = RTA_LENGTH(addrlen);
+    req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
+    memcpy(RTA_DATA(rta), addr, addrlen);
+
+    // Add an explicit IFA_BROADCAST for IPv4 RTM_NEWADDRs.
+    if (ss.ss_family == AF_INET && action == RTM_NEWADDR) {
+        rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));
+        rta->rta_type = IFA_BROADCAST;
+        rta->rta_len = RTA_LENGTH(addrlen);
+        req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
+        ((struct in_addr *)addr)->s_addr |= htonl((1<<(32-prefixlen))-1);
+        memcpy(RTA_DATA(rta), addr, addrlen);
+    }
+
+    s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+    if (s < 0) {
+        return -errno;
+    }
+
+    if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
+        saved_errno = errno;
+        close(s);
+        return -saved_errno;
+    }
+
+    len = recv(s, buf, sizeof(buf), 0);
+    saved_errno = errno;
+    close(s);
+    if (len < 0) {
+        return -saved_errno;
+    }
+
+    // Parse the acknowledgement to find the return code.
+    nh = (struct nlmsghdr *) buf;
+    if (!NLMSG_OK(nh, (unsigned) len) || nh->nlmsg_type != NLMSG_ERROR) {
+        return -EINVAL;
+    }
+    err = NLMSG_DATA(nh);
+
+    // Return code is negative errno.
+    return err->error;
+}
+
+int ifc_add_address(const char *name, const char *address, int prefixlen) {
+    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
+}
+
+int ifc_del_address(const char *name, const char * address, int prefixlen) {
+    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
+}
+
+/*
+ * Clears IPv6 addresses on the specified interface.
+ */
+int ifc_clear_ipv6_addresses(const char *name) {
+    char rawaddrstr[INET6_ADDRSTRLEN], addrstr[INET6_ADDRSTRLEN];
+    unsigned int prefixlen;
+    int lasterror = 0, i, j, ret;
+    char ifname[64];  // Currently, IFNAMSIZ = 16.
+    FILE *f = fopen("/proc/net/if_inet6", "r");
+    if (!f) {
+        return -errno;
+    }
+
+    // Format:
+    // 20010db8000a0001fc446aa4b5b347ed 03 40 00 01    wlan0
+    while (fscanf(f, "%32s %*02x %02x %*02x %*02x %63s\n",
+                  rawaddrstr, &prefixlen, ifname) == 3) {
+        // Is this the interface we're looking for?
+        if (strcmp(name, ifname)) {
+            continue;
+        }
+
+        // Put the colons back into the address.
+        for (i = 0, j = 0; i < 32; i++, j++) {
+            addrstr[j] = rawaddrstr[i];
+            if (i % 4 == 3) {
+                addrstr[++j] = ':';
+            }
+        }
+        addrstr[j - 1] = '\0';
+
+        // Don't delete the link-local address as well, or it will disable IPv6
+        // on the interface.
+        if (strncmp(addrstr, "fe80:", 5) == 0) {
+            continue;
+        }
+
+        ret = ifc_del_address(ifname, addrstr, prefixlen);
+        if (ret) {
+            ALOGE("Deleting address %s/%d on %s: %s", addrstr, prefixlen, ifname,
+                 strerror(-ret));
+            lasterror = ret;
+        }
+    }
+
+    fclose(f);
+    return lasterror;
+}
+
+/*
+ * Clears IPv4 addresses on the specified interface.
+ */
+void ifc_clear_ipv4_addresses(const char *name) {
+    unsigned count, addr;
+    ifc_init();
+    for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {
+        if (ifc_get_addr(name, &addr) < 0)
+            break;
+        if (addr)
+            ifc_set_addr(name, 0);
+    }
+    ifc_close();
+}
+
+/*
+ * Clears all IP addresses on the specified interface.
+ */
+int ifc_clear_addresses(const char *name) {
+    ifc_clear_ipv4_addresses(name);
+    return ifc_clear_ipv6_addresses(name);
+}
+
+int ifc_set_hwaddr(const char *name, const void *ptr)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+    memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN);
+    return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr);
+}
+
+int ifc_set_mask(const char *name, in_addr_t mask)
+{
+    struct ifreq ifr;
+    int ret;
+
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, mask);
+
+    ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_set_mask(%s, xx) = %d", name, ret);
+#endif
+    return ret;
+}
+
+int ifc_set_prefixLength(const char *name, int prefixLength)
+{
+    struct ifreq ifr;
+    // TODO - support ipv6
+    if (prefixLength > 32 || prefixLength < 0) return -1;
+
+    in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength);
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, mask);
+
+    return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+}
+
+int ifc_get_addr(const char *name, in_addr_t *addr)
+{
+    struct ifreq ifr;
+    int ret = 0;
+
+    ifc_init_ifr(name, &ifr);
+    if (addr != NULL) {
+        ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr);
+        if (ret < 0) {
+            *addr = 0;
+        } else {
+            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+        }
+    }
+    return ret;
+}
+
+int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    if (addr != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {
+            *addr = 0;
+        } else {
+            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+        }
+    }
+
+    if (prefixLength != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {
+            *prefixLength = 0;
+        } else {
+            *prefixLength = ipv4NetmaskToPrefixLength(
+                    ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr);
+        }
+    }
+
+    if (flags != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {
+            *flags = 0;
+        } else {
+            *flags = ifr.ifr_flags;
+        }
+    }
+
+    return 0;
+}
+
+int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length,
+      struct in_addr gw)
+{
+    struct rtentry rt;
+    int result;
+    in_addr_t netmask;
+
+    memset(&rt, 0, sizeof(rt));
+
+    rt.rt_dst.sa_family = AF_INET;
+    rt.rt_dev = (void*) ifname;
+
+    netmask = prefixLengthToIpv4Netmask(prefix_length);
+    init_sockaddr_in(&rt.rt_genmask, netmask);
+    init_sockaddr_in(&rt.rt_dst, dst.s_addr);
+    rt.rt_flags = RTF_UP;
+
+    if (prefix_length == 32) {
+        rt.rt_flags |= RTF_HOST;
+    }
+
+    if (gw.s_addr != 0) {
+        rt.rt_flags |= RTF_GATEWAY;
+        init_sockaddr_in(&rt.rt_gateway, gw.s_addr);
+    }
+
+    ifc_init();
+
+    if (ifc_ctl_sock < 0) {
+        ifc_close();
+        return -errno;
+    }
+
+    result = ioctl(ifc_ctl_sock, action, &rt);
+    if (result < 0) {
+        if (errno == EEXIST) {
+            result = 0;
+        } else {
+            result = -errno;
+        }
+    }
+    ifc_close();
+    return result;
+}
+
+/* deprecated - v4 only */
+int ifc_create_default_route(const char *name, in_addr_t gw)
+{
+    struct in_addr in_dst, in_gw;
+
+    in_dst.s_addr = 0;
+    in_gw.s_addr = gw;
+
+    int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw);
+#if !defined TIZEN_EXT
+    if (DBG) printerr("ifc_create_default_route(%s, %d) = %d", name, gw, ret);
+#endif
+    return ret;
+}
+
+// Needed by code in hidden partner repositories / branches, so don't delete.
+int ifc_enable(const char *ifname)
+{
+    int result;
+
+    ifc_init();
+    result = ifc_up(ifname);
+    ifc_close();
+    return result;
+}
+
+// Needed by code in hidden partner repositories / branches, so don't delete.
+int ifc_disable(const char *ifname)
+{
+    unsigned addr, count;
+    int result;
+
+    ifc_init();
+    result = ifc_down(ifname);
+
+    ifc_set_addr(ifname, 0);
+    for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {
+       if (ifc_get_addr(ifname, &addr) < 0)
+            break;
+       if (addr)
+          ifc_set_addr(ifname, 0);
+    }
+
+    ifc_close();
+    return result;
+}
+
+int ifc_reset_connections(const char *ifname, const int reset_mask)
+{
+#if defined(__ANDROID__)
+    int result, success;
+    in_addr_t myaddr = 0;
+    struct ifreq ifr;
+    struct in6_ifreq ifr6;
+
+    if (reset_mask & RESET_IPV4_ADDRESSES) {
+        /* IPv4. Clear connections on the IP address. */
+        ifc_init();
+        if (!(reset_mask & RESET_IGNORE_INTERFACE_ADDRESS)) {
+            ifc_get_info(ifname, &myaddr, NULL, NULL);
+        }
+        ifc_init_ifr(ifname, &ifr);
+        init_sockaddr_in(&ifr.ifr_addr, myaddr);
+        result = ioctl(ifc_ctl_sock, SIOCKILLADDR,  &ifr);
+        ifc_close();
+    } else {
+        result = 0;
+    }
+
+    if (reset_mask & RESET_IPV6_ADDRESSES) {
+        /*
+         * IPv6. On Linux, when an interface goes down it loses all its IPv6
+         * addresses, so we don't know which connections belonged to that interface
+         * So we clear all unused IPv6 connections on the device by specifying an
+         * empty IPv6 address.
+         */
+        ifc_init6();
+        // This implicitly specifies an address of ::, i.e., kill all IPv6 sockets.
+        memset(&ifr6, 0, sizeof(ifr6));
+        success = ioctl(ifc_ctl_sock6, SIOCKILLADDR,  &ifr6);
+        if (result == 0) {
+            result = success;
+        }
+        ifc_close6();
+    }
+
+    return result;
+#else
+    return 0;
+#endif
+}
+
+/*
+ * Removes the default route for the named interface.
+ */
+int ifc_remove_default_route(const char *ifname)
+{
+       struct rtentry rt;
+       int result;
+
+       ifc_init();
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_dev = (void *)ifname;
+       rt.rt_flags = RTF_UP|RTF_GATEWAY;
+       init_sockaddr_in(&rt.rt_dst, 0);
+       if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {
+#if defined TIZEN_EXT
+               CLATD_LOG(LOG_ERROR, "failed to remove default route for %s: %s", ifname, strerror(errno));
+#else
+               ALOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
+#endif
+       }
+       ifc_close();
+       return result;
+}
+
+int
+ifc_configure(const char *ifname,
+               in_addr_t address,
+               uint32_t prefixLength,
+               in_addr_t gateway,
+               in_addr_t dns1,
+               in_addr_t dns2)
+{
+#if !defined TIZEN_EXT
+       char dns_prop_name[PROPERTY_KEY_MAX];
+#endif
+
+       ifc_init();
+
+       if (ifc_up(ifname)) {
+#if defined TIZEN_EXT
+               CLATD_LOG(LOG_ERROR, "failed to turn on interface %s: %s", ifname, strerror(errno));
+#else
+               printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
+#endif
+               ifc_close();
+               return -1;
+       }
+       if (ifc_set_addr(ifname, address)) {
+#if defined TIZEN_EXT
+               CLATD_LOG(LOG_ERROR, "failed to set ipaddr %s: %s", ipaddr_to_string(address), strerror(errno));
+#else
+               printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
+#endif
+               ifc_close();
+               return -1;
+       }
+       if (ifc_set_prefixLength(ifname, prefixLength)) {
+#if defined TIZEN_EXT
+               CLATD_LOG(LOG_ERROR, "failed to set prefixLength %d: %s", prefixLength, strerror(errno));
+#else
+               printerr("failed to set prefixLength %d: %s\n", prefixLength, strerror(errno));
+#endif
+               ifc_close();
+               return -1;
+       }
+       if (ifc_create_default_route(ifname, gateway)) {
+#if defined TIZEN_EXT
+               CLATD_LOG(LOG_ERROR, "failed to set default route %s: %s", ipaddr_to_string(gateway), strerror(errno));
+#else
+               printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
+#endif
+               ifc_close();
+               return -1;
+       }
+
+       ifc_close();
+
+#if !defined TIZEN_EXT
+       snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
+       property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
+       snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
+       property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
+#endif
+       return 0;
+}
similarity index 73%
rename from ipv4.c
rename to src/ipv4.c
index 4b0db39..e0fe648 100644 (file)
--- a/ipv4.c
  */
 #include <string.h>
 
-#include "translate.h"
 #include "checksum.h"
-#include "logging.h"
+
 #include "debug.h"
 #include "dump.h"
+#include "log.h"
+#include "translate.h"
 
 /* function: icmp_packet
  * translates an icmp packet
@@ -36,12 +37,12 @@ int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *ic
   const uint8_t *payload;
   size_t payload_size;
 
-  if(len < sizeof(struct icmphdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)");
+  if (len < sizeof(struct icmphdr)) {
+    CLATD_LOG(LOG_ERROR, "icmp_packet/(too small)");
     return 0;
   }
 
-  payload = (const uint8_t *) (icmp + 1);
+  payload      = (const uint8_t *)(icmp + 1);
   payload_size = len - sizeof(struct icmphdr);
 
   return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size);
@@ -55,8 +56,8 @@ int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *ic
  * returns: the highest position in the output clat_packet that's filled in
  */
 int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) {
-  const struct iphdr *header = (struct iphdr *) packet;
-  struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base;
+  const struct iphdr *header = (struct iphdr *)packet;
+  struct ip6_hdr *ip6_targ   = (struct ip6_hdr *)out[pos].iov_base;
   struct ip6_frag *frag_hdr;
   size_t frag_hdr_len;
   uint8_t nxthdr;
@@ -65,23 +66,23 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   uint32_t old_sum, new_sum;
   int iov_len;
 
-  if(len < sizeof(struct iphdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header");
+  if (len < sizeof(struct iphdr)) {
+    CLATD_LOG(LOG_ERROR, "ip_packet/too short for an ip header");
     return 0;
   }
 
-  if(header->ihl < 5) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl);
+  if (header->ihl < 5) {
+    CLATD_LOG(LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl);
     return 0;
   }
 
-  if((size_t) header->ihl * 4 > len) { // ip header length larger than entire packet
-    logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set too large: %x", header->ihl);
+  if ((size_t)header->ihl * 4 > len) {  // ip header length larger than entire packet
+    CLATD_LOG(LOG_ERROR, "ip_packet/ip header length set too large: %x", header->ihl);
     return 0;
   }
 
-  if(header->version != 4) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version);
+  if (header->version != 4) {
+    CLATD_LOG(LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version);
     return 0;
   }
 
@@ -90,8 +91,8 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
    * translate the options.
    */
 
-  next_header = packet + header->ihl*4;
-  len_left = len - header->ihl * 4;
+  next_header = packet + header->ihl * 4;
+  len_left    = len - header->ihl * 4;
 
   nxthdr = header->protocol;
   if (nxthdr == IPPROTO_ICMP) {
@@ -115,26 +116,26 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr);
 
   // If the IPv4 packet is fragmented, add a Fragment header.
-  frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base;
-  frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header);
+  frag_hdr             = (struct ip6_frag *)out[pos + 1].iov_base;
+  frag_hdr_len         = maybe_fill_frag_header(frag_hdr, ip6_targ, header);
   out[pos + 1].iov_len = frag_hdr_len;
 
   if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) {
     // Non-first fragment. Copy the rest of the packet as is.
     iov_len = generic_packet(out, pos + 2, next_header, len_left);
   } else if (nxthdr == IPPROTO_ICMPV6) {
-    iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left);
+    iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *)next_header, new_sum, len_left);
   } else if (nxthdr == IPPROTO_TCP) {
-    iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum,
-                         len_left);
+    iov_len =
+      tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left);
   } else if (nxthdr == IPPROTO_UDP) {
-    iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum,
-                         len_left);
+    iov_len =
+      udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left);
   } else if (nxthdr == IPPROTO_GRE) {
     iov_len = generic_packet(out, pos + 2, next_header, len_left);
   } else {
 #if CLAT_DEBUG
-    logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol);
+    CLATD_LOG(LOG_ERROR, "ip_packet/unknown protocol: %x", header->protocol);
     logcat_hexdump("ipv4/protocol", packet, len);
 #endif
     return 0;
similarity index 82%
rename from ipv6.c
rename to src/ipv6.c
index b485313..48001fd 100644 (file)
--- a/ipv6.c
 
 #include <arpa/inet.h>
 
-#include "translate.h"
 #include "checksum.h"
-#include "logging.h"
-#include "dump.h"
+
 #include "config.h"
 #include "debug.h"
+#include "dump.h"
+#include "log.h"
+#include "translate.h"
 
 /* function: icmp6_packet
  * takes an icmp6 packet and sets it up for translation
@@ -39,12 +40,12 @@ int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr
   const uint8_t *payload;
   size_t payload_size;
 
-  if(len < sizeof(struct icmp6_hdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
+  if (len < sizeof(struct icmp6_hdr)) {
+    CLATD_LOG(LOG_ERROR, "icmp6_packet/(too small)");
     return 0;
   }
 
-  payload = (const uint8_t *) (icmp6 + 1);
+  payload      = (const uint8_t *)(icmp6 + 1);
   payload_size = len - sizeof(struct icmp6_hdr);
 
   return icmp6_to_icmp(out, pos, icmp6, payload, payload_size);
@@ -62,7 +63,7 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i
 
   inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
   inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
-  logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
+  CLATD_LOG(LOG_ERROR, fmt, srcstr, dststr);
 }
 #else
 #define log_bad_address(fmt, src, dst)
@@ -76,8 +77,8 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i
  * returns: the highest position in the output clat_packet that's filled in
  */
 int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) {
-  const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
-  struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base;
+  const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet;
+  struct iphdr *ip_targ     = (struct iphdr *)out[pos].iov_base;
   struct ip6_frag *frag_hdr = NULL;
   uint8_t protocol;
   const uint8_t *next_header;
@@ -85,14 +86,14 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   uint32_t old_sum, new_sum;
   int iov_len;
 
-  if(len < sizeof(struct ip6_hdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
+  if (len < sizeof(struct ip6_hdr)) {
+    CLATD_LOG(LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
     return 0;
   }
 
-  if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+  if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
     log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
-    return 0; // silently ignore
+    return 0;  // silently ignore
   }
 
   // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless
@@ -111,7 +112,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   }
 
   next_header = packet + sizeof(struct ip6_hdr);
-  len_left = len - sizeof(struct ip6_hdr);
+  len_left    = len - sizeof(struct ip6_hdr);
 
   protocol = ip6->ip6_nxt;
 
@@ -125,9 +126,9 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   // If there's a Fragment header, parse it and decide what the next header is.
   // Do this before calculating the pseudo-header checksum because it updates the next header value.
   if (protocol == IPPROTO_FRAGMENT) {
-    frag_hdr = (struct ip6_frag *) next_header;
+    frag_hdr = (struct ip6_frag *)next_header;
     if (len_left < sizeof(*frag_hdr)) {
-      logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
+      CLATD_LOG(LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
       return 0;
     }
 
@@ -139,7 +140,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
 
   // ICMP and ICMPv6 have different protocol numbers.
   if (protocol == IPPROTO_ICMPV6) {
-    protocol = IPPROTO_ICMP;
+    protocol          = IPPROTO_ICMP;
     ip_targ->protocol = IPPROTO_ICMP;
   }
 
@@ -155,18 +156,18 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
   if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) {
     iov_len = generic_packet(out, pos + 2, next_header, len_left);
   } else if (protocol == IPPROTO_ICMP) {
-    iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left);
+    iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *)next_header, len_left);
   } else if (protocol == IPPROTO_TCP) {
-    iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum,
-                         len_left);
+    iov_len =
+      tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left);
   } else if (protocol == IPPROTO_UDP) {
-    iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum,
-                         len_left);
+    iov_len =
+      udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left);
   } else if (protocol == IPPROTO_GRE) {
     iov_len = generic_packet(out, pos + 2, next_header, len_left);
   } else {
 #if CLAT_DEBUG
-    logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
+    CLATD_LOG(LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
     logcat_hexdump("ipv6/nxthdr", packet, len);
 #endif
     return 0;
@@ -174,6 +175,6 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s
 
   // Set the length and calculate the checksum.
   ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos));
-  ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr));
+  ip_targ->check   = ip_checksum(ip_targ, sizeof(struct iphdr));
   return iov_len;
 }
diff --git a/src/load_file.c b/src/load_file.c
new file mode 100644 (file)
index 0000000..ec9c5a1
--- /dev/null
@@ -0,0 +1,55 @@
+/* libs/cutils/load_file.c
+ **
+ ** Copyright 2006, 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "config_utils.h"
+void *load_file(const char *fn, unsigned *_sz)
+{
+       char *data;
+       int sz;
+       int fd;
+
+       data = 0;
+       fd = open(fn, O_RDONLY);
+       if (fd < 0) return 0;
+
+       sz = lseek(fd, 0, SEEK_END);
+       if (sz < 0) goto oops;
+
+       if (lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+       data = (char *)malloc(sz + 1);
+       if (data == 0) goto oops;
+
+       if (read(fd, data, sz) != sz) goto oops;
+       close(fd);
+       data[sz] = 0;
+
+       if (_sz) *_sz = sz;
+       return data;
+
+ oops:
+       close(fd);
+       if (data != 0) free(data);
+       return NULL;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..9c5baa9
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2018 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.
+ *
+ * main.c - main function
+ */
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <unistd.h>
+#include <signal.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+//#include "resolv_netid.h"
+
+#include "clatd.h"
+#include "common.h"
+#include "config.h"
+#include "dbus.h"
+#include "log.h"
+#include "setif.h"
+#include "tun.h"
+#include "specific_dns.h"
+
+#define DEVICEPREFIX "v4-"
+
+/* function: print_help
+ * in case the user is running this on the command line
+ */
+void print_help() {
+  printf("android-clat arguments:\n");
+  printf("-i [uplink interface]\n");
+  printf("-p [plat prefix]\n");
+  printf("-4 [IPv4 address]\n");
+  printf("-6 [IPv6 address]\n");
+  printf("-n [NetId]\n");
+  printf("-m [socket mark]\n");
+  printf("-t [tun file descriptor number]\n");
+}
+
+#if defined(TIZEN_EXT)
+static void  _graceful_terminate(gpointer data);
+static gboolean on_sigterm(gpointer user_data);
+
+struct _clat {
+       GMainLoop *loop;
+       ClatDBus *cdbus;
+       GDBusInterfaceSkeleton *di;
+       struct tun_data tunnel;
+       gboolean state;
+};
+
+/* function: stop_loop
+ * signal handler: stop the event loop
+ */
+static gboolean on_stop(gpointer user_data)
+{
+       struct _clat *cl = user_data;
+       CLATD_LOG(LOG_FATAL, "stop clat event loop");
+
+       g_main_loop_quit(cl->loop);
+
+       return FALSE;
+}
+
+/*
+ * allocate and setup the tun device, then run the event loop
+ */
+static gboolean _method_start(GDBusInterfaceSkeleton *di,
+                             GDBusMethodInvocation *invoc, GVariant *list, gpointer user_data)
+{
+       struct _clat *cl = 0;
+       GVariantIter *iter = 0;
+       gint option = 0;
+       gchar *value = 0;
+
+       CLATD_LOG(LOG_FATAL, "_method_start");
+
+       char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL;
+       char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL;
+       unsigned net_id = NETID_UNSET;
+       uint32_t mark = MARK_UNSET;
+       unsigned len = 0;
+
+       cl = user_data;
+
+       if (cl->state == TRUE) {
+               CLATD_LOG(LOG_FATAL, "CLATD already on going");
+               goto already_on_going;
+       }
+
+       g_variant_get(list, "a{is}", &iter);
+       if (iter == NULL) {
+               CLATD_LOG(LOG_FATAL,"Failed to get iter");
+               goto error_case;
+       }
+
+       while (g_variant_iter_next(iter, "{is}",&option , &value)) {
+               switch (option) {
+               case 'i':
+                       uplink_interface = value;
+                       set_ifname(uplink_interface);
+                       break;
+               case 'p':
+                       plat_prefix = value;
+                       break;
+               case '4':
+                       v4_addr = value;
+                       break;
+               case '6':
+                       v6_addr = value;
+                       break;
+               case 'n':
+                       net_id_str = value;
+                       break;
+               case 'm':
+                       mark_str = value;
+                       break;
+               case 't':
+                       tunfd_str = value;
+                       break;
+               case 'd':
+                       CLATD_LOG(LOG_INFO, "DNS address : %s", value);
+                       if (set_specific_nameserver(value) != 0) {
+                               CLATD_LOG(LOG_ERROR, "Failed to input nameserver address");
+                               goto error_case;
+                       }
+
+                       break;
+               default:
+                       CLATD_LOG(LOG_FATAL, "Unknown option -%c. Exiting.", (char)option);
+                       goto error_case;
+               }
+       }
+
+       if (uplink_interface == NULL) {
+               CLATD_LOG(LOG_FATAL, "clatd called without an interface");
+               goto error_case;
+       }
+
+       if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) {
+               CLATD_LOG(LOG_FATAL, "invalid NetID %s", net_id_str);
+               goto error_case;
+       }
+
+       if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) {
+               CLATD_LOG(LOG_FATAL, "invalid mark %s", mark_str);
+               goto error_case;
+       }
+
+       if (tunfd_str != NULL && !parse_int(tunfd_str, &cl->tunnel.fd4)) {
+               CLATD_LOG(LOG_FATAL, "invalid tunfd %s", tunfd_str);
+               goto error_case;
+       }
+
+       len = snprintf(cl->tunnel.device4, sizeof(cl->tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface);
+       if (len >= sizeof(cl->tunnel.device4)) {
+               CLATD_LOG(LOG_FATAL, "interface name too long '%s'", cl->tunnel.device4);
+               goto error_case;
+       }
+
+       CLATD_LOG(LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s",
+                       CLATD_VERSION, uplink_interface,net_id_str ? net_id_str : "(none)",
+                       mark_str ? mark_str : "(none)", 
+                       plat_prefix ? plat_prefix : "(none)",
+                       v4_addr ? v4_addr : "(none)",
+                       v6_addr ? v6_addr : "(none)");
+
+       if (!has_specific_nameserver())
+               // run under a regular user but keep needed capabilities
+               drop_root_but_keep_caps();
+
+       // open our raw sockets before dropping privs
+       open_sockets(&cl->tunnel, mark);
+
+       // keeps only admin capability
+       set_capability(1 << CAP_NET_ADMIN);
+
+       // we can create tun devices as non-root because we're in the VPN group.
+       cl->tunnel.fd4 = tun_open();
+       if (cl->tunnel.fd4 < 0) {
+               CLATD_LOG(LOG_FATAL, "tun_open4 failed: %s", strerror(errno));
+               goto error_case;
+       }
+
+       // When run from netd, the environment variable ANDROID_DNS_MODE is set to
+       // "local", but that only works for the netd process itself.
+       unsetenv("ANDROID_DNS_MODE");
+
+       configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &cl->tunnel, net_id);
+
+       // Loop until someone sends us a signal or brings down the tun interface.
+       g_unix_signal_add(SIGTERM, on_sigterm, cl);
+
+       event_loop(&cl->tunnel);
+
+       if (has_specific_nameserver())
+               // run under a regular user but keep needed capabilities
+               drop_root_but_keep_caps();
+
+       cl->state = TRUE;
+       CLATD_LOG(LOG_FATAL, "launched");
+
+       g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", 0));
+
+       return TRUE;
+
+error_case:
+       g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", -1));
+       _graceful_terminate(cl);
+       return FALSE;
+
+already_on_going:
+       g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", 1));
+       return TRUE;
+}
+
+static gboolean _method_stop(GDBusInterfaceSkeleton *di,
+                            GDBusMethodInvocation *invoc, gpointer user_data)
+{
+       struct _clat *cl = 0;
+
+       cl = user_data;
+
+//< RNTFIX::to make clat4 interface persistently
+       if (if_down(cl->tunnel.device4) < 0) {
+               CLATD_LOG(LOG_FATAL, "if_down failed: %s", strerror(errno));
+       }
+       if (delete_address(cl->tunnel.device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32) < 0) {
+               CLATD_LOG(LOG_FATAL, "delete_address failed: %s", strerror(errno));
+       }
+
+//> RNTFIX::to make clat4 interface persistently
+
+       //CLATD_LOG(LOG_INFO, "Shutting down clat on %s", uplink_interface);
+       CLATD_LOG(LOG_INFO, "Shutting down clat ");
+       del_anycast_address(cl->tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
+       g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", 0));
+       on_stop(cl);
+       return TRUE;
+}
+
+static void  _graceful_terminate(gpointer data)
+{
+       struct _clat *cl = NULL;
+       cl = (struct _clat *)data;
+       CLATD_LOG(LOG_INFO, "terminate gracfully");
+
+       errno = 0;
+       if (if_down(cl->tunnel.device4) < 0)
+               CLATD_LOG(LOG_FATAL, "if_down failed: %s", strerror(errno));
+
+       errno = 0;
+       if (delete_address(cl->tunnel.device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32) < 0)
+               CLATD_LOG(LOG_FATAL, "delete_address failed: %s", strerror(errno));
+
+       CLATD_LOG(LOG_INFO, "Shutting down clat on %s", cl->tunnel.device4);
+       del_anycast_address(cl->tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
+       on_stop(cl);
+}
+
+static gboolean on_sigterm(gpointer user_data)
+{
+       struct _clat *cl = user_data;
+       CLATD_LOG(LOG_FATAL, "on_sigterm");
+       _graceful_terminate(cl);
+       return FALSE;
+}
+
+static void on_init(ClatDBus *cdbus, gpointer user_data)
+{
+       struct _clat *cl = 0;
+       GError *error = 0;
+       gboolean ret = FALSE;
+
+       cl = user_data;
+       cl->cdbus = cdbus;
+       cl->di = clat_di_skeleton_new_control();
+       g_signal_connect(cl->di, "handle-start", G_CALLBACK(_method_start), cl);
+       g_signal_connect(cl->di, "handle-stop", G_CALLBACK(_method_stop), cl);
+
+       ret = g_dbus_interface_skeleton_export(cl->di, clat_dbus_get_connection(cl->cdbus), "/com/samsung/clatd", &error);
+       if (!ret) {
+               if (error) {
+                       CLATD_LOG(LOG_FATAL, "dbus err msg (%s)", error->message);
+                       g_error_free(error);
+               }
+       }
+}
+
+int main(void)
+{
+       CLATD_LOG(LOG_INFO, "starting");
+       struct _clat *cl = 0;
+       int ret = 0;
+
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+       g_type_init();
+#endif
+
+       cl = g_new0(struct _clat, 1);
+       cl->loop = g_main_loop_new(0, FALSE);
+
+       ret = clat_dbus_init(on_init, cl);
+       if (ret < 0) {
+               CLATD_LOG(LOG_ERROR, "dbus init error");
+               return -1;
+       }
+       CLATD_LOG(LOG_INFO, "ready");
+       g_main_loop_run(cl->loop);
+       CLATD_LOG(LOG_INFO, "terminated");
+       return 0;
+}
+#else
+
+/* function: main
+ * allocate and setup the tun device, then run the event loop
+ */
+int main(int argc, char **argv) {
+  struct tun_data tunnel;
+  int opt;
+  char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL;
+  char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL;
+  unsigned net_id = NETID_UNSET;
+  uint32_t mark   = MARK_UNSET;
+  unsigned len;
+
+  while ((opt = getopt(argc, argv, "i:p:4:6:n:m:t:h")) != -1) {
+    switch (opt) {
+      case 'i':
+        uplink_interface = optarg;
+        break;
+      case 'p':
+        plat_prefix = optarg;
+        break;
+      case '4':
+        v4_addr = optarg;
+        break;
+      case '6':
+        v6_addr = optarg;
+        break;
+      case 'n':
+        net_id_str = optarg;
+        break;
+      case 'm':
+        mark_str = optarg;
+        break;
+      case 't':
+        tunfd_str = optarg;
+        break;
+      case 'h':
+        print_help();
+        exit(0);
+      default:
+        logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt);
+        exit(1);
+    }
+  }
+
+  if (uplink_interface == NULL) {
+    logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
+    exit(1);
+  }
+
+  if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) {
+    logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str);
+    exit(1);
+  }
+
+  if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) {
+    logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str);
+    exit(1);
+  }
+
+  if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) {
+    logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str);
+    exit(1);
+  }
+  if (!tunnel.fd4) {
+    logmsg(ANDROID_LOG_FATAL, "no tunfd specified on commandline.");
+    exit(1);
+  }
+
+  len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface);
+  if (len >= sizeof(tunnel.device4)) {
+    logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4);
+    exit(1);
+  }
+
+  logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s plat=%s v4=%s v6=%s",
+         CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)",
+         mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)",
+         v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)");
+
+  // run under a regular user but keep needed capabilities
+  drop_root_but_keep_caps();
+
+  // open our raw sockets before dropping privs
+  open_sockets(&tunnel, mark);
+
+  // keeps only admin capability
+  set_capability(1 << CAP_NET_ADMIN);
+
+  // When run from netd, the environment variable ANDROID_DNS_MODE is set to
+  // "local", but that only works for the netd process itself. Removing the
+  // following line causes XLAT failure in permissive mode.
+  unsetenv("ANDROID_DNS_MODE");
+
+  configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id);
+
+  // Drop all remaining capabilities.
+  set_capability(0);
+
+  // Loop until someone sends us a signal or brings down the tun interface.
+  if (signal(SIGTERM, stop_loop) == SIG_ERR) {
+    logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
+    exit(1);
+  }
+
+  event_loop(&tunnel);
+
+  logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface);
+  del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
+  return 0;
+}
+#endif /* TIZEN_EXT */
diff --git a/mtu.c b/src/mtu.c
similarity index 87%
rename from mtu.c
rename to src/mtu.c
index 975bf0e..472bd4e 100644 (file)
--- a/mtu.c
+++ b/src/mtu.c
  * mtu.c - get interface mtu
  */
 
-#include <string.h>
+#include <net/if.h>
 #include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
+#include <string.h>
 #include <sys/ioctl.h>
-#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "mtu.h"
 
@@ -33,14 +34,16 @@ int getifmtu(const char *ifname) {
   int fd;
   struct ifreq if_mtu;
 
-  fd = socket(AF_INET, SOCK_STREAM, 0);
-  if(fd < 0) {
+  fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  if (fd < 0) {
     return -1;
   }
   strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ);
   if_mtu.ifr_name[IFNAMSIZ - 1] = '\0';
-  if(ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) {
+  if (ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) {
+    close(fd);
     return -1;
   }
+  close(fd);
   return if_mtu.ifr_mtu;
 }
similarity index 85%
rename from netlink_callbacks.c
rename to src/netlink_callbacks.c
index a79aa76..804976d 100644 (file)
@@ -15,8 +15,8 @@
  *
  * netlink_callbacks.c - generic callbacks for netlink responses
  */
-#include <netinet/in.h>
 #include <net/if.h>
+#include <netinet/in.h>
 
 #include <linux/rtnetlink.h>
 #include <netlink/handlers.h>
@@ -29,7 +29,7 @@
  */
 static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) {
   int *retval = data;
-  *retval = 0;
+  *retval     = 0;
   return NL_OK;
 }
 
@@ -39,26 +39,26 @@ static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) {
  * err  - netlink error message
  * arg  - pointer to an int, stores the error code
  */
-static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla,
-                         struct nlmsgerr *err, void *arg) {
+static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, struct nlmsgerr *err,
+                         void *arg) {
   int *retval = arg;
-  if(err->error < 0) {
+  if (err->error < 0) {
     *retval = err->error;
   } else {
-    *retval = 0; // NLMSG_ERROR used as reply type on no error
+    *retval = 0;  // NLMSG_ERROR used as reply type on no error
   }
   return NL_OK;
 }
 
 /* function: alloc_ack_callbacks
- * allocates a set of netlink callbacks.  returns NULL on failure.  callbacks will modify retval with <0 meaning failure
- * retval - shared state between caller and callback functions
+ * allocates a set of netlink callbacks.  returns NULL on failure.  callbacks will modify retval
+ * with <0 meaning failure retval - shared state between caller and callback functions
  */
 struct nl_cb *alloc_ack_callbacks(int *retval) {
   struct nl_cb *callbacks;
 
   callbacks = nl_cb_alloc(NL_CB_DEFAULT);
-  if(!callbacks) {
+  if (!callbacks) {
     return NULL;
   }
   nl_cb_set(callbacks, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, retval);
similarity index 86%
rename from netlink_msg.c
rename to src/netlink_msg.c
index 958559c..53c1f6a 100644 (file)
  * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink
  */
 
-#include <netinet/in.h>
+#include <errno.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <netinet/in.h>
 #include <string.h>
-#include <errno.h>
 
-#include <netlink-local.h>
-#include <netlink-types.h>
-#include <netlink/socket.h>
-#include <netlink/netlink.h>
+//#include <netlink-private/object-api.h>
+//#include <netlink-private/types.h>
 #include <netlink/msg.h>
+#include <netlink/netlink.h>
+#include <netlink/socket.h>
 
-#include "netlink_msg.h"
 #include "netlink_callbacks.h"
+#include "netlink_msg.h"
 
 /* function: family_size
  * returns the size of the address structure for the given family, or 0 on error
  * family - AF_INET or AF_INET6
  */
 size_t inet_family_size(int family) {
-  if(family == AF_INET) {
+  if (family == AF_INET) {
     return sizeof(struct in_addr);
-  } else if(family == AF_INET6) {
+  } else if (family == AF_INET6) {
     return sizeof(struct in6_addr);
   } else {
     return 0;
@@ -52,11 +52,12 @@ size_t inet_family_size(int family) {
  * payload_struct - pointer to a struct to add to netlink message
  * payload_len    - bytelength of structure
  */
-struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, size_t payload_len) {
+struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct,
+                                   size_t payload_len) {
   struct nl_msg *msg;
 
   msg = nlmsg_alloc();
-  if(!msg) {
+  if (!msg) {
     return NULL;
   }
 
@@ -65,9 +66,9 @@ struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_
     return NULL;
   }
 
-  msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len);
+  msg->nm_nlh->nlmsg_len   = NLMSG_LENGTH(payload_len);
   msg->nm_nlh->nlmsg_flags = flags;
-  msg->nm_nlh->nlmsg_type = type;
+  msg->nm_nlh->nlmsg_type  = type;
 
   memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len);
 
@@ -116,7 +117,7 @@ int netlink_set_kernel_only(struct nl_sock *nl_sk) {
   }
 
   int sockfd = nl_socket_get_fd(nl_sk);
-  return connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
+  return connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
 }
 
 /* function: send_netlink_msg
@@ -128,23 +129,18 @@ void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) {
   struct nl_sock *nl_sk;
 
   nl_sk = nl_socket_alloc();
-  if(!nl_sk)
-    goto cleanup;
+  if (!nl_sk) goto cleanup;
 
-  if(nl_connect(nl_sk, NETLINK_ROUTE) != 0)
-    goto cleanup;
+  if (nl_connect(nl_sk, NETLINK_ROUTE) != 0) goto cleanup;
 
-  if(nl_send_auto_complete(nl_sk, msg) < 0)
-    goto cleanup;
+  if (nl_send_auto_complete(nl_sk, msg) < 0) goto cleanup;
 
-  if(netlink_set_kernel_only(nl_sk) < 0)
-    goto cleanup;
+  if (netlink_set_kernel_only(nl_sk) < 0) goto cleanup;
 
   nl_recvmsgs(nl_sk, callbacks);
 
 cleanup:
-  if(nl_sk)
-    nl_socket_free(nl_sk);
+  if (nl_sk) nl_socket_free(nl_sk);
 }
 
 /* function: send_ifaddrmsg
@@ -158,8 +154,7 @@ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct
   struct nl_msg *msg = NULL;
 
   msg = nlmsg_alloc_ifaddr(type, flags, ifa);
-  if(!msg)
-    return;
+  if (!msg) return;
 
   send_netlink_msg(msg, callbacks);
 
@@ -172,10 +167,10 @@ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct
  */
 int netlink_sendrecv(struct nl_msg *msg) {
   struct nl_cb *callbacks = NULL;
-  int retval = -EIO;
+  int retval              = -EIO;
 
   callbacks = alloc_ack_callbacks(&retval);
-  if(!callbacks) {
+  if (!callbacks) {
     return -ENOMEM;
   }
 
similarity index 62%
rename from ring.c
rename to src/ring.c
index 5e99fd5..72c8350 100644 (file)
--- a/ring.c
  * ring.c - packet ring buffer functions
  */
 
-#include <errno.h>
-#include <string.h>
 #include <arpa/inet.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
+#include <errno.h>
 #include <linux/if.h>
 #include <linux/if_packet.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
 
-#include "logging.h"
+#include "log.h"
 #include "ring.h"
 #include "translate.h"
 #include "tun.h"
 
 int ring_create(struct tun_data *tunnel) {
-  int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
+  int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
   if (packetsock < 0) {
-    logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno));
+    CLATD_LOG(LOG_FATAL, "packet socket failed: %s", strerror(errno));
     return -1;
   }
 
   int ver = TPACKET_V2;
-  if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *) &ver, sizeof(ver))) {
-    logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno));
+  if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *)&ver, sizeof(ver))) {
+    CLATD_LOG(LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno));
     return -1;
   }
 
   int on = 1;
-  if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *) &on, sizeof(on))) {
-    logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno));
+  if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *)&on, sizeof(on))) {
+    CLATD_LOG(LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno));
   }
 
   struct packet_ring *ring = &tunnel->ring;
-  ring->numblocks = TP_NUM_BLOCKS;
+  ring->numblocks          = TP_NUM_BLOCKS;
 
   int total_frames = TP_FRAMES * ring->numblocks;
 
   struct tpacket_req req = {
-      .tp_frame_size = TP_FRAME_SIZE,  // Frame size.
-      .tp_block_size = TP_BLOCK_SIZE,  // Frames per block.
-      .tp_block_nr = ring->numblocks,  // Number of blocks.
-      .tp_frame_nr = total_frames,     // Total frames.
+    .tp_frame_size = TP_FRAME_SIZE,    // Frame size.
+    .tp_block_size = TP_BLOCK_SIZE,    // Frames per block.
+    .tp_block_nr   = ring->numblocks,  // Number of blocks.
+    .tp_frame_nr   = total_frames,     // Total frames.
   };
 
   if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) {
-    logmsg(ANDROID_LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno));
+    CLATD_LOG(LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno));
     return -1;
   }
 
   size_t buflen = TP_BLOCK_SIZE * ring->numblocks;
-  ring->base = mmap(NULL, buflen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED|MAP_POPULATE,
+  ring->base    = mmap(NULL, buflen, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE,
                     packetsock, 0);
   if (ring->base == MAP_FAILED) {
-    logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno));
+    CLATD_LOG(LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno));
     return -1;
   }
 
-  ring->block = 0;
-  ring->slot = 0;
+  ring->block    = 0;
+  ring->slot     = 0;
   ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE;
-  ring->next = (struct tpacket2_hdr *) ring->base;
+  ring->next     = (struct tpacket2_hdr *)ring->base;
 
-  logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p",
-         total_frames, buflen, ring->base);
+  CLATD_LOG(LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", total_frames,
+         buflen, ring->base);
 
   return packetsock;
 }
@@ -87,8 +87,8 @@ int ring_create(struct tun_data *tunnel) {
  * advances to the next position in the packet ring
  * ring - packet ring buffer
  */
-static struct tpacket2_hdrring_advance(struct packet_ring *ring) {
-  uint8_t *next = (uint8_t *) ring->next;
+static struct tpacket2_hdr *ring_advance(struct packet_ring *ring) {
+  uint8_t *next = (uint8_t *)ring->next;
 
   ring->slot++;
   next += TP_FRAME_SIZE;
@@ -101,11 +101,11 @@ static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) {
       next += TP_FRAME_GAP;
     } else {
       ring->block = 0;
-      next = (uint8_t *) ring->base;
+      next        = (uint8_t *)ring->base;
     }
   }
 
-  ring->next = (struct tpacket2_hdr *) next;
+  ring->next = (struct tpacket2_hdr *)next;
   return ring->next;
 }
 
@@ -118,9 +118,9 @@ static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) {
 void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) {
   struct tpacket2_hdr *tp = ring->next;
   if (tp->tp_status & TP_STATUS_USER) {
-    uint8_t *packet = ((uint8_t *) tp) + tp->tp_net;
+    uint8_t *packet = ((uint8_t *)tp) + tp->tp_net;
     translate_packet(write_fd, to_ipv6, packet, tp->tp_len);
     tp->tp_status = TP_STATUS_KERNEL;
-    tp = ring_advance(ring);
+    tp            = ring_advance(ring);
   }
 }
similarity index 57%
rename from setif.c
rename to src/setif.c
index 07f5bac..0b84f1f 100644 (file)
--- a/setif.c
  * setif.c - network interface configuration
  */
 #include <errno.h>
-#include <netinet/in.h>
 #include <net/if.h>
+#include <netinet/in.h>
 
 #include <linux/rtnetlink.h>
 #include <netlink/handlers.h>
 #include <netlink/msg.h>
 
-#include "logging.h"
+#include "log.h"
 #include "netlink_msg.h"
 
-#define DEBUG_OPTNAME(a) case (a): { optname = #a; break; }
+#define DEBUG_OPTNAME(a)                                                                           \
+  case (a): {                                                                                      \
+    optname = #a;                                                                                  \
+    break;                                                                                         \
+  }
 
 /* function: add_address
  * adds an IP address to/from an interface, returns 0 on success and <0 on failure
  * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0)
  * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6)
  */
-int add_address(const char *ifname, int family, const void *address, int prefixlen, const void *broadcast) {
+int add_address(const char *ifname, int family, const void *address, int prefixlen,
+                const void *broadcast) {
   int retval;
   size_t addr_size;
   struct ifaddrmsg ifa;
   struct nl_msg *msg = NULL;
 
   addr_size = inet_family_size(family);
-  if(addr_size == 0) {
+  if (addr_size == 0) {
     retval = -EAFNOSUPPORT;
     goto cleanup;
   }
@@ -53,29 +58,30 @@ int add_address(const char *ifname, int family, const void *address, int prefixl
     retval = -ENODEV;
     goto cleanup;
   }
-  ifa.ifa_family = family;
+  ifa.ifa_family    = family;
   ifa.ifa_prefixlen = prefixlen;
-  ifa.ifa_scope = RT_SCOPE_UNIVERSE;
+  ifa.ifa_scope     = RT_SCOPE_UNIVERSE;
 
-  msg = nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa);
-  if(!msg) {
+  msg =
+    nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa);
+  if (!msg) {
     retval = -ENOMEM;
     goto cleanup;
   }
 
-  if(nla_put(msg, IFA_LOCAL, addr_size, address) < 0) {
+  if (nla_put(msg, IFA_LOCAL, addr_size, address) < 0) {
     retval = -ENOMEM;
     goto cleanup;
   }
-  if(family == AF_INET6) {
+  if (family == AF_INET6) {
     // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS
-    if(nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) {
+    if (nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) {
       retval = -ENOMEM;
       goto cleanup;
     }
-  } else if(family == AF_INET) {
+  } else if (family == AF_INET) {
     // AF_INET gets IFA_LOCAL + IFA_BROADCAST
-    if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) {
+    if (nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) {
       retval = -ENOMEM;
       goto cleanup;
     }
@@ -87,8 +93,7 @@ int add_address(const char *ifname, int family, const void *address, int prefixl
   retval = netlink_sendrecv(msg);
 
 cleanup:
-  if(msg)
-    nlmsg_free(msg);
+  if (msg) nlmsg_free(msg);
 
   return retval;
 }
@@ -109,15 +114,15 @@ int if_up(const char *ifname, int mtu) {
     goto cleanup;
   }
   ifi.ifi_change = IFF_UP;
-  ifi.ifi_flags = IFF_UP;
+  ifi.ifi_flags  = IFF_UP;
 
   msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi);
-  if(!msg) {
+  if (!msg) {
     retval = -ENOMEM;
     goto cleanup;
   }
 
-  if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) {
+  if (nla_put(msg, IFLA_MTU, 4, &mtu) < 0) {
     retval = -ENOMEM;
     goto cleanup;
   }
@@ -125,8 +130,7 @@ int if_up(const char *ifname, int mtu) {
   retval = netlink_sendrecv(msg);
 
 cleanup:
-  if(msg)
-    nlmsg_free(msg);
+  if (msg) nlmsg_free(msg);
 
   return retval;
 }
@@ -146,7 +150,7 @@ static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int
 
   ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq));
   if (ret) {
-    logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno));
+    CLATD_LOG(LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno));
   }
 
   return ret;
@@ -163,7 +167,7 @@ int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) {
 
   ifindex = if_nametoindex(ifname);
   if (!ifindex) {
-    logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname);
+    CLATD_LOG(LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname);
     return -ENODEV;
   }
 
@@ -178,3 +182,89 @@ int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) {
 int del_anycast_address(int sock, struct in6_addr *addr) {
   return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0);
 }
+
+//< RNTFIX::to make clat4 interface persistently
+int delete_address(const char *ifname, int family, const void *address, int prefixlen)
+{
+       int retval;
+       size_t addr_size;
+       struct ifaddrmsg ifa;
+       struct nl_msg *msg = NULL;
+
+       addr_size = inet_family_size(family);
+       if (addr_size == 0) {
+               retval = -EAFNOSUPPORT;
+               goto cleanup;
+       }
+
+       memset(&ifa, 0, sizeof(ifa));
+       if (!(ifa.ifa_index = if_nametoindex(ifname))) {
+               retval = -ENODEV;
+               goto cleanup;
+       }
+       ifa.ifa_family = family;
+       ifa.ifa_prefixlen = prefixlen;
+       ifa.ifa_scope = RT_SCOPE_UNIVERSE;
+
+       msg = nlmsg_alloc_ifaddr(RTM_DELADDR, NLM_F_ACK | NLM_F_REQUEST, &ifa);
+       if (!msg) {
+               retval = -ENOMEM;
+               goto cleanup;
+       }
+
+       if (nla_put(msg, IFA_LOCAL, addr_size, address) < 0) {
+               retval = -ENOMEM;
+               goto cleanup;
+       }
+       if (family == AF_INET6) {
+               // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS
+               if (nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) {
+                       retval = -ENOMEM;
+                       goto cleanup;
+               }
+       } else if (family == AF_INET) {
+               // Ignore broadcast
+       } else {
+               retval = -EAFNOSUPPORT;
+               goto cleanup;
+       }
+
+       retval = netlink_sendrecv(msg);
+
+ cleanup:
+       if (msg)
+               nlmsg_free(msg);
+
+       return retval;
+}
+
+int if_down(const char *ifname)
+{
+       int retval = -1;
+       struct ifinfomsg ifi;
+       struct nl_msg *msg = NULL;
+
+       memset(&ifi, 0, sizeof(ifi));
+       if (!(ifi.ifi_index = if_nametoindex(ifname))) {
+               retval = -ENODEV;
+               goto cleanup;
+       }
+
+       ifi.ifi_change = IFF_UP;
+       ifi.ifi_flags = ~IFF_UP;
+
+       msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi);
+       if (!msg) {
+               retval = -ENOMEM;
+               goto cleanup;
+       }
+
+       retval = netlink_sendrecv(msg);
+
+ cleanup:
+       if (msg)
+               nlmsg_free(msg);
+
+       return retval;
+}
+//> RNTFIX::to make clat4 interface persistently
similarity index 82%
rename from translate.c
rename to src/translate.c
index ddc9bac..0ad5dac 100644 (file)
  */
 #include <string.h>
 
-#include "icmp.h"
-#include "translate.h"
 #include "checksum.h"
+
 #include "clatd.h"
+#include "common.h"
 #include "config.h"
-#include "logging.h"
 #include "debug.h"
+#include "icmp.h"
+#include "log.h"
+#include "translate.h"
 #include "tun.h"
 
 /* function: packet_checksum
@@ -97,7 +99,7 @@ struct in6_addr ipv4_addr_to_ipv6_addr(uint32_t addr4) {
     return Global_Clatd_Config.ipv6_local_subnet;
   } else {
     // Assumes a /96 plat subnet.
-    addr6 = Global_Clatd_Config.plat_subnet;
+    addr6              = Global_Clatd_Config.plat_subnet;
     addr6.s6_addr32[3] = addr4;
     return addr6;
   }
@@ -125,15 +127,15 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
   int ttl_guess;
   memset(ip, 0, sizeof(struct iphdr));
 
-  ip->ihl = 5;
-  ip->version = 4;
-  ip->tos = 0;
-  ip->tot_len = htons(sizeof(struct iphdr) + payload_len);
-  ip->id = 0;
+  ip->ihl      = 5;
+  ip->version  = 4;
+  ip->tos      = 0;
+  ip->tot_len  = htons(sizeof(struct iphdr) + payload_len);
+  ip->id       = 0;
   ip->frag_off = htons(IP_DF);
-  ip->ttl = old_header->ip6_hlim;
+  ip->ttl      = old_header->ip6_hlim;
   ip->protocol = protocol;
-  ip->check = 0;
+  ip->check    = 0;
 
   ip->saddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_src);
   ip->daddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_dst);
@@ -141,7 +143,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
   // Third-party ICMPv6 message. This may have been originated by an native IPv6 address.
   // In that case, the source IPv6 address can't be translated and we need to make up an IPv4
   // source address. For now, use 255.0.0.<ttl>, which at least looks useful in traceroute.
-  if ((uint32_t) ip->saddr == INADDR_NONE) {
+  if ((uint32_t)ip->saddr == INADDR_NONE) {
     ttl_guess = icmp_guess_ttl(old_header->ip6_hlim);
     ip->saddr = htonl((0xff << 24) + ttl_guess);
   }
@@ -158,9 +160,9 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol
                      const struct iphdr *old_header) {
   memset(ip6, 0, sizeof(struct ip6_hdr));
 
-  ip6->ip6_vfc = 6 << 4;
+  ip6->ip6_vfc  = 6 << 4;
   ip6->ip6_plen = htons(payload_len);
-  ip6->ip6_nxt = protocol;
+  ip6->ip6_nxt  = protocol;
   ip6->ip6_hlim = old_header->ttl;
 
   ip6->ip6_src = ipv4_addr_to_ipv6_addr(old_header->saddr);
@@ -178,13 +180,13 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol
 size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
                               const struct iphdr *old_header) {
   uint16_t frag_flags = ntohs(old_header->frag_off);
-  uint16_t frag_off = frag_flags & IP_OFFMASK;
+  uint16_t frag_off   = frag_flags & IP_OFFMASK;
   if (frag_off == 0 && (frag_flags & IP_MF) == 0) {
     // Not a fragment.
     return 0;
   }
 
-  frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt;
+  frag_hdr->ip6f_nxt      = ip6_targ->ip6_nxt;
   frag_hdr->ip6f_reserved = 0;
   // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits.
   frag_hdr->ip6f_offlg = htons(frag_off << 3);
@@ -192,7 +194,7 @@ size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_tar
     frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG;
   }
   frag_hdr->ip6f_ident = htonl(ntohs(old_header->id));
-  ip6_targ->ip6_nxt = IPPROTO_FRAGMENT;
+  ip6_targ->ip6_nxt    = IPPROTO_FRAGMENT;
 
   return sizeof(*frag_hdr);
 }
@@ -210,7 +212,7 @@ uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ
     frag_off |= IP_MF;
   }
   ip_targ->frag_off = htons(frag_off);
-  ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+  ip_targ->id       = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
   ip_targ->protocol = frag_hdr->ip6f_nxt;
   return frag_hdr->ip6f_nxt;
 }
@@ -232,15 +234,13 @@ int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *
 
   memset(icmp6_targ, 0, sizeof(struct icmp6_hdr));
 
-  icmp6_type = icmp_to_icmp6_type(icmp->type, icmp->code);
+  icmp6_type             = icmp_to_icmp6_type(icmp->type, icmp->code);
   icmp6_targ->icmp6_type = icmp6_type;
   icmp6_targ->icmp6_code = icmp_to_icmp6_code(icmp->type, icmp->code);
 
   out[pos].iov_len = sizeof(struct icmp6_hdr);
 
-  if (pos == CLAT_POS_TRANSPORTHDR &&
-      is_icmp_error(icmp->type) &&
-      icmp6_type != ICMP6_PARAM_PROB) {
+  if (pos == CLAT_POS_TRANSPORTHDR && is_icmp_error(icmp->type) && icmp6_type != ICMP6_PARAM_PROB) {
     // An ICMP error we understand, one level deep.
     // Translate the nested packet (the one that caused the error).
     clat_packet_len = ipv4_packet(out, pos + 1, payload, payload_size);
@@ -254,11 +254,11 @@ int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *
     checksum = checksum + htons(20);
   } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
     // Ping packet.
-    icmp6_targ->icmp6_id = icmp->un.echo.id;
-    icmp6_targ->icmp6_seq = icmp->un.echo.sequence;
-    out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload;
-    out[CLAT_POS_PAYLOAD].iov_len = payload_size;
-    clat_packet_len = CLAT_POS_PAYLOAD + 1;
+    icmp6_targ->icmp6_id           = icmp->un.echo.id;
+    icmp6_targ->icmp6_seq          = icmp->un.echo.sequence;
+    out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+    out[CLAT_POS_PAYLOAD].iov_len  = payload_size;
+    clat_packet_len                = CLAT_POS_PAYLOAD + 1;
   } else {
     // Unknown type/code. The type/code conversion functions have already logged an error.
     return 0;
@@ -286,27 +286,26 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr
 
   memset(icmp_targ, 0, sizeof(struct icmphdr));
 
-  icmp_type = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code);
+  icmp_type       = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code);
   icmp_targ->type = icmp_type;
   icmp_targ->code = icmp6_to_icmp_code(icmp6->icmp6_type, icmp6->icmp6_code);
 
   out[pos].iov_len = sizeof(struct icmphdr);
 
-  if (pos == CLAT_POS_TRANSPORTHDR &&
-      is_icmp6_error(icmp6->icmp6_type) &&
+  if (pos == CLAT_POS_TRANSPORTHDR && is_icmp6_error(icmp6->icmp6_type) &&
       icmp_type != ICMP_PARAMETERPROB) {
     // An ICMPv6 error we understand, one level deep.
     // Translate the nested packet (the one that caused the error).
     clat_packet_len = ipv6_packet(out, pos + 1, payload, payload_size);
   } else if (icmp_type == ICMP_ECHO || icmp_type == ICMP_ECHOREPLY) {
     // Ping packet.
-    icmp_targ->un.echo.id = icmp6->icmp6_id;
-    icmp_targ->un.echo.sequence = icmp6->icmp6_seq;
-    out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload;
-    out[CLAT_POS_PAYLOAD].iov_len = payload_size;
-    clat_packet_len = CLAT_POS_PAYLOAD + 1;
+    icmp_targ->un.echo.id          = icmp6->icmp6_id;
+    icmp_targ->un.echo.sequence    = icmp6->icmp6_seq;
+    out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+    out[CLAT_POS_PAYLOAD].iov_len  = payload_size;
+    clat_packet_len                = CLAT_POS_PAYLOAD + 1;
   } else {
-      // Unknown type/code. The type/code conversion functions have already logged an error.
+    // Unknown type/code. The type/code conversion functions have already logged an error.
     return 0;
   }
 
@@ -325,9 +324,9 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr
  * returns: the highest position in the output clat_packet that's filled in
  */
 int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len) {
-  out[pos].iov_len = 0;
-  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload;
-  out[CLAT_POS_PAYLOAD].iov_len = len;
+  out[pos].iov_len               = 0;
+  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+  out[CLAT_POS_PAYLOAD].iov_len  = len;
 
   return CLAT_POS_PAYLOAD + 1;
 }
@@ -340,17 +339,17 @@ int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payloa
  * new_sum  - pseudo-header checksum of new header
  * len      - size of ip payload
  */
-int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
-               uint32_t old_sum, uint32_t new_sum, size_t len) {
+int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum,
+               uint32_t new_sum, size_t len) {
   const uint8_t *payload;
   size_t payload_size;
 
-  if(len < sizeof(struct udphdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
+  if (len < sizeof(struct udphdr)) {
+    CLATD_LOG(LOG_ERROR, "udp_packet/(too small)");
     return 0;
   }
 
-  payload = (const uint8_t *) (udp + 1);
+  payload      = (const uint8_t *)(udp + 1);
   payload_size = len - sizeof(struct udphdr);
 
   return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size);
@@ -364,28 +363,28 @@ int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp,
  * len      - size of ip payload
  * returns: the highest position in the output clat_packet that's filled in
  */
-int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
-               uint32_t old_sum, uint32_t new_sum, size_t len) {
+int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum,
+               uint32_t new_sum, size_t len) {
   const uint8_t *payload;
   size_t payload_size, header_size;
 
-  if(len < sizeof(struct tcphdr)) {
-    logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
+  if (len < sizeof(struct tcphdr)) {
+    CLATD_LOG(LOG_ERROR, "tcp_packet/(too small)");
     return 0;
   }
 
-  if(tcp->doff < 5) {
-    logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
+  if (tcp->doff < 5) {
+    CLATD_LOG(LOG_ERROR, "tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
     return 0;
   }
 
-  if((size_t) tcp->doff*4 > len) {
-    logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff);
+  if ((size_t)tcp->doff * 4 > len) {
+    CLATD_LOG(LOG_ERROR, "tcp_packet/tcp header length set too large: %x", tcp->doff);
     return 0;
   }
 
-  header_size = tcp->doff * 4;
-  payload = ((const uint8_t *) tcp) + header_size;
+  header_size  = tcp->doff * 4;
+  payload      = ((const uint8_t *)tcp) + header_size;
   payload_size = len - header_size;
 
   return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size);
@@ -407,9 +406,9 @@ int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *u
 
   memcpy(udp_targ, udp, sizeof(struct udphdr));
 
-  out[pos].iov_len = sizeof(struct udphdr);
-  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload;
-  out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+  out[pos].iov_len               = sizeof(struct udphdr);
+  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+  out[CLAT_POS_PAYLOAD].iov_len  = payload_size;
 
   if (udp_targ->check) {
     udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
@@ -442,23 +441,23 @@ int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *u
  * returns: the highest position in the output clat_packet that's filled in
  */
 int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp,
-                  size_t header_size, uint32_t old_sum, uint32_t new_sum,
-                  const uint8_t *payload, size_t payload_size) {
+                  size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload,
+                  size_t payload_size) {
   struct tcphdr *tcp_targ = out[pos].iov_base;
-  out[pos].iov_len = header_size;
+  out[pos].iov_len        = header_size;
 
   if (header_size > MAX_TCP_HDR) {
     // A TCP header cannot be more than MAX_TCP_HDR bytes long because it's a 4-bit field that
     // counts in 4-byte words. So this can never happen unless there is a bug in the caller.
-    logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating",
-           header_size, MAX_TCP_HDR);
+    CLATD_LOG(LOG_ERROR, "tcp_translate: header too long %d > %d, truncating", header_size,
+           MAX_TCP_HDR);
     header_size = MAX_TCP_HDR;
   }
 
   memcpy(tcp_targ, tcp, header_size);
 
-  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload;
-  out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+  out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload;
+  out[CLAT_POS_PAYLOAD].iov_len  = payload_size;
 
   tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
 
@@ -474,14 +473,13 @@ void send_rawv6(int fd, clat_packet out, int iov_len) {
   // destination address in the packet header only affects what appears on the wire, not where the
   // packet is sent to.
   static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 };
-  static struct msghdr msg = {
-    .msg_name = &sin6,
+  static struct msghdr msg        = {
+    .msg_name    = &sin6,
     .msg_namelen = sizeof(sin6),
   };
 
-  msg.msg_iov = out,
-  msg.msg_iovlen = iov_len,
-  sin6.sin6_addr = ((struct ip6_hdr *) out[CLAT_POS_IPHDR].iov_base)->ip6_dst;
+  msg.msg_iov = out, msg.msg_iovlen = iov_len,
+  sin6.sin6_addr = ((struct ip6_hdr *)out[CLAT_POS_IPHDR].iov_base)->ip6_dst;
   sendmsg(fd, &msg, 0);
 }
 
@@ -506,14 +504,14 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets
 
   // iovec of the packets we'll send. This gets passed down to the translation functions.
   clat_packet out = {
-    { &tun_targ, 0 },                 // Tunnel header.
-    { iphdr, 0 },                     // IP header.
-    { fraghdr, 0 },                   // Fragment header.
-    { transporthdr, 0 },              // Transport layer header.
-    { icmp_iphdr, 0 },                // ICMP error inner IP header.
-    { icmp_fraghdr, 0 },              // ICMP error fragmentation header.
-    { icmp_transporthdr, 0 },         // ICMP error transport layer header.
-    { NULL, 0 },                      // Payload. No buffer, it's a pointer to the original payload.
+    { &tun_targ, 0 },          // Tunnel header.
+    { iphdr, 0 },              // IP header.
+    { fraghdr, 0 },            // Fragment header.
+    { transporthdr, 0 },       // Transport layer header.
+    { icmp_iphdr, 0 },         // ICMP error inner IP header.
+    { icmp_fraghdr, 0 },       // ICMP error fragmentation header.
+    { icmp_transporthdr, 0 },  // ICMP error transport layer header.
+    { NULL, 0 },               // Payload. No buffer, it's a pointer to the original payload.
   };
 
   if (to_ipv6) {
diff --git a/tun.c b/src/tun.c
similarity index 54%
rename from tun.c
rename to src/tun.c
index 49f0ea7..ea5704d 100644 (file)
--- a/tun.c
+++ b/src/tun.c
  *
  * tun.c - tun device functions
  */
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
 #include <arpa/inet.h>
+#include <fcntl.h>
 #include <linux/if.h>
 #include <linux/if_tun.h>
+#include <string.h>
 #include <sys/ioctl.h>
-#include <sys/uio.h>
+#include <unistd.h>
 
-#include "clatd.h"
+#include "common.h"
+#include "log.h"
 
 /* function: tun_open
- * tries to open the tunnel device
+ * tries to open the tunnel device in non-blocking mode
  */
 int tun_open() {
   int fd;
 
-  fd = open("/dev/tun", O_RDWR);
-  if(fd < 0) {
-    fd = open("/dev/net/tun", O_RDWR);
+  fd = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+  if (fd < 0) {
+    fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
   }
 
   return fd;
@@ -43,47 +43,34 @@ int tun_open() {
 /* function: tun_alloc
  * creates a tun interface and names it
  * dev - the name for the new tun device
+ * fd - an open fd to the tun device node
+ * len - the length of the buffer pointed to by dev
  */
-int tun_alloc(char *dev, int fd) {
+int tun_alloc(char *dev, int fd, size_t len) {
   struct ifreq ifr;
   int err;
 
   memset(&ifr, 0, sizeof(ifr));
 
   ifr.ifr_flags = IFF_TUN;
-  if( *dev ) {
+  if (*dev) {
     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
-    ifr.ifr_name[IFNAMSIZ-1] = '\0';
+    ifr.ifr_name[IFNAMSIZ - 1] = '\0';
   }
 
-  if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
+  if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
     close(fd);
     return err;
   }
-  strcpy(dev, ifr.ifr_name);
-  return 0;
-}
 
-/* function: set_nonblocking
- * sets a filedescriptor to non-blocking mode
- * fd - the filedescriptor
- * returns: 0 on success, -1 on failure
- */
-int set_nonblocking(int fd) {
-  int flags = fcntl(fd, F_GETFL);
-  if (flags == -1) {
-    return flags;
-  }
-  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-}
+//< RNTFIX::to make clat4 interface persistently
+       if ((err = ioctl(fd, TUNSETPERSIST, 1)) < 0) {
+               CLATD_LOG(LOG_FATAL, "tun_alloc TUNSETPERSIST failed: %d, %s", errno, strerror(errno));
+               close(fd);
+               return err;
+       }
+//> RNTFIX::to make clat4 interface persistently
 
-/* function: send_tun
- * sends a clat_packet to a tun interface
- * fd      - the tun filedescriptor
- * out     - the packet to send
- * iov_len - the number of entries in the clat_packet
- * returns: number of bytes read on success, -1 on failure
- */
-int send_tun(int fd, clat_packet out, int iov_len) {
-  return writev(fd, out, iov_len);
+  strncpy(dev, ifr.ifr_name, len);
+  return 0;
 }
diff --git a/src_ext/specific_dns.c b/src_ext/specific_dns.c
new file mode 100644 (file)
index 0000000..e3e864d
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * specific_dns
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Youngho Jeon <ykernel.jeon@samsung.com>
+ *
+ * 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.
+ *
+ * specific_dns.h - request to specific dns server to resolve domain name
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <ares.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include "specific_dns.h"
+#include "log.h"
+
+#define NAMESERVER_NUM 2
+#define SEPARATOR ";"
+
+typedef struct _nameserver {
+       char addr[NAMESERVER_NUM][64]; /* IPv6 DNS server address */
+       char if_name[32];
+} nameserver_t;
+
+static nameserver_t address_data;
+
+int set_ifname(const char* if_name)
+{
+       if (!if_name) {
+               CLATD_LOG(LOG_ERROR, "if_nmae is NULL");
+               return -1;
+       }
+
+       memset(address_data.if_name, 0x00, sizeof(address_data.if_name));
+       snprintf(address_data.if_name, sizeof(address_data.if_name), "%s", if_name);
+       CLATD_LOG(LOG_INFO, "%s", address_data.if_name);
+       return 0;
+}
+
+/*
+ * servers - IPv6 dns server addresses separated by ';'
+ */
+int set_specific_nameserver(const char* servers)
+{
+       char addr[128] = {0, };
+       char *last = NULL;
+       char *result = NULL;
+       int idx = 0;
+       if (!servers) {
+               CLATD_LOG(LOG_ERROR, "NULL argument");
+               return -1;
+       }
+
+       memset(address_data.addr, 0x00, sizeof(address_data.addr));
+       snprintf(addr, sizeof(addr), "%s", servers);
+       result = strtok_r(addr, SEPARATOR, &last);
+       while(result != NULL) {
+               if (idx >= NAMESERVER_NUM)
+                       break;
+               snprintf(address_data.addr[idx], sizeof(address_data.addr[idx]), "%s", result);
+               CLATD_LOG(LOG_FATAL, "nameserver %d: %s",idx, address_data.addr[idx]);
+
+               idx++;
+               result = strtok_r(NULL, SEPARATOR, &last);
+       }
+       return 0;
+}
+
+bool has_specific_nameserver()
+{
+       return (strlen(address_data.addr[0]) > 0 && (strncmp(address_data.addr[0], "NULL", 4) != 0))
+                       || (strlen(address_data.addr[1]) > 0 && (strncmp(address_data.addr[1], "NULL", 4) != 0));
+}
+
+static void __free_hostent(struct hostent *hosts)
+{
+       int i;
+
+       if (hosts == NULL) {
+               CLATD_LOG(LOG_ERROR, "hosts is NULL");
+               return;
+       }
+
+       for (i = 0; hosts->h_addr_list[i]; ++i)
+       {
+               free(hosts->h_addr_list[i]);
+       }
+       free(hosts->h_addr_list);
+       if (hosts->h_name)
+               free(hosts->h_name);
+
+       free(hosts);
+}
+
+static void __query_completed_cb(void *arg, int status, int timeouts,
+                       struct hostent *host)
+{
+       struct hostent **result = (struct hostent **)arg;
+       struct hostent *host_res = NULL;
+       CLATD_LOG(LOG_INFO, "status -- timeout %d, %s", timeouts, ares_strerror(status));
+       if (status == ARES_SUCCESS) {
+               if (host) {
+                       int idx;
+                       CLATD_LOG(LOG_INFO, "%s, length %d", host->h_name, host->h_length);
+
+                       host_res = calloc(1, sizeof(struct hostent));
+                       if (host_res == NULL) {
+                               CLATD_LOG(LOG_ERROR, "No Memory");
+                               return;
+                       }
+                       host_res->h_name = strdup(host->h_name);
+                       host_res->h_addrtype = host->h_addrtype;
+                       host_res->h_length = host->h_length;
+                       host_res->h_addr_list = calloc(32, sizeof(char*));
+                       for (idx = 0; host->h_addr_list[idx]; idx++) {
+                               /* just for debugging */
+                               char addr_str[INET6_ADDRSTRLEN] = {0, };
+                               inet_ntop(host->h_addrtype, host->h_addr_list[idx], addr_str, sizeof(addr_str));
+                               CLATD_LOG(LOG_INFO, "%s", addr_str);
+
+                               host_res->h_addr_list[idx] = calloc(host->h_length, sizeof(char));
+                               memcpy(host_res->h_addr_list[idx], host->h_addr_list[idx], host->h_length);
+                       }
+                       *result = host_res;
+               } else {
+                       CLATD_LOG(LOG_ERROR, "No result");
+               }
+       }
+}
+
+static void __state_cb(void *data, int s, int read, int write)
+{
+       CLATD_LOG(LOG_INFO, "Change state fd %d, read %d, write %d", s, read, write);
+}
+
+static void __dns_wait_ares(ares_channel channel, int timeout_sec)
+{
+       int res = 0;
+       time_t timeout = time(NULL) + timeout_sec;
+       time_t cur_t;
+       while(1) {
+               struct timeval *tvp, tv, storetv;
+               fd_set read_fds, write_fds;
+               int nfds;
+
+               cur_t = time(NULL);
+               if (cur_t >= timeout) {
+                       ares_cancel(channel);
+                       break;
+               }
+               storetv.tv_sec = timeout - cur_t;
+               storetv.tv_usec = 0;
+
+               FD_ZERO(&read_fds);
+               FD_ZERO(&write_fds);
+               nfds = ares_fds(channel, &read_fds, &write_fds);
+               CLATD_LOG(LOG_INFO, "ares_fd %d", nfds);
+               if(nfds == 0){
+                       ares_cancel(channel);
+                       break;
+               }
+               tvp = ares_timeout(channel, &storetv, &tv);
+               res = select(nfds, &read_fds, &write_fds, NULL, tvp);
+               CLATD_LOG(LOG_INFO, "%d", res);
+               ares_process(channel, &read_fds, &write_fds);
+       }
+}
+
+static int __gethostbyname(char* dnsserver, const char* hostname, int address_family,
+                       char* local_ip_str, char* if_name, int timeout_sec,
+                       struct hostent **results)
+{
+       ares_channel channel;
+       int status;
+       struct ares_options opts;
+       int optmask = 0;
+       unsigned char local_ip[16] = {0,};
+       int af = AF_INET6;
+
+       if (hostname == NULL) {
+               CLATD_LOG(LOG_ERROR,"invalid prameter: domain_name is null.");
+               return -1;
+       }
+
+       if (results == NULL) {
+               CLATD_LOG(LOG_ERROR,"invalid prameter: results is null.");
+               return -1;
+       }
+
+       status = ares_library_init(ARES_LIB_INIT_ALL);
+       if (status != ARES_SUCCESS) {
+               CLATD_LOG(LOG_ERROR,"ares_library_init: %s", ares_strerror(status));
+               return -1;
+       }
+       opts.sock_state_cb = __state_cb;
+       optmask |= ARES_OPT_SOCK_STATE_CB;
+
+       status = ares_init_options(&channel, &opts, optmask);
+       if(status != ARES_SUCCESS) {
+               CLATD_LOG(LOG_ERROR,"ares_init_options: %s", ares_strerror(status));
+               return -1;
+       }
+
+       if (address_family == AF_INET6) {
+               inet_pton(AF_INET6, local_ip_str, &local_ip);
+               ares_set_local_ip6(channel, (const unsigned char *)local_ip);
+               af = AF_INET6;
+       } else {
+               inet_pton(AF_INET, local_ip_str, &local_ip);
+               ares_set_local_ip4(channel, (unsigned int)local_ip);
+               af = AF_INET;
+       }
+       ares_set_local_dev(channel, if_name);
+       status = ares_set_servers_csv(channel, dnsserver);
+       if(status != ARES_SUCCESS) {
+               CLATD_LOG(LOG_ERROR,"ares_set_servers_csv: %s", ares_strerror(status));
+               return -1;
+       }
+       ares_gethostbyname(channel, hostname, af, __query_completed_cb, (void*)results);
+
+       if (timeout_sec <= 0)
+               timeout_sec = 30;
+
+       __dns_wait_ares(channel, timeout_sec);
+
+       ares_destroy(channel);
+       ares_library_cleanup();
+
+       CLATD_LOG(LOG_INFO,"ares_done");
+       return 0;
+}
+
+static int __get_local_ip_str(char *if_name, char **ip_str)
+{
+       int sockfd = 0;
+       struct ifreq ifr;
+       unsigned char *addr = NULL;
+       char addr_str[INET6_ADDRSTRLEN] = {0, };
+
+       if (!if_name) {
+               CLATD_LOG(LOG_ERROR, "if name is NULL");
+               return -1;
+       }
+
+       if (!ip_str) {
+               CLATD_LOG(LOG_ERROR, "if_str is NULL");
+               return -1;
+       }
+
+       sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sockfd < 0) {
+               CLATD_LOG(LOG_ERROR, "Failed to open socket");
+               return -1;
+       }
+
+       /* interface name to pick */
+       memset(&ifr, 0x00, sizeof(struct ifreq));
+       strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
+       ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+       CLATD_LOG(LOG_INFO, "ifr_name %", ifr.ifr_name);
+       if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {
+               CLATD_LOG(LOG_ERROR, "Failed to get if addr");
+       } else {
+               addr = ((struct sockaddr_in6 *)&ifr.ifr_addr)->sin6_addr.s6_addr;
+               inet_ntop(AF_INET6, addr, addr_str, sizeof(addr_str));
+               CLATD_LOG(LOG_INFO, "addr %s", addr_str);
+               *ip_str = addr_str;
+       }
+       close(sockfd);
+       return 0;
+}
+
+int specific_getaddrinfo(const char *hostname,
+               const struct addrinfo *hint,
+               struct addrinfo **result)
+{
+       int af = 0;
+       int idx;
+       int status;
+       struct hostent *host_result = NULL;
+       struct addrinfo *addrinfo_result = NULL;
+       char addr_str[INET6_ADDRSTRLEN] = {0, };
+
+       if (!hostname) {
+               CLATD_LOG(LOG_ERROR, "hostname is NULL");
+               return -1;
+       }
+
+       if (!result) {
+               CLATD_LOG(LOG_ERROR, "result container is NULL");
+               return -1;
+       }
+
+       CLATD_LOG(LOG_INFO, "initialize...");
+       if (hint) {
+               af = hint->ai_family;
+       } else {
+               af = AF_UNSPEC;
+       }
+
+       CLATD_LOG(LOG_FATAL, "set local dev [%s]", address_data.if_name);
+       __get_local_ip_str(address_data.if_name, &addr_str);
+       for (idx = 0; idx < NAMESERVER_NUM; idx++) {
+               CLATD_LOG(LOG_FATAL, "dnsserver [%s]", address_data.addr[idx]);
+               status = __gethostbyname(address_data.addr[idx],
+                               hostname,
+                               af,
+                               addr_str,
+                               address_data.if_name,
+                               10,
+                               &host_result);
+
+               if (status == 0 && host_result) {
+                       if (host_result->h_addrtype == AF_INET6) {
+                               struct in6_addr plat_addr;
+
+                               addrinfo_result = calloc(1, sizeof(struct addrinfo));
+                               if (addrinfo_result) {
+                                       addrinfo_result->ai_family = host_result->h_addrtype;
+                                       addrinfo_result->ai_addrlen = host_result->h_length;
+                                       addrinfo_result->ai_addr = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_in6));
+                                       if (addrinfo_result->ai_addr) {
+                                               ((struct sockaddr_in6 *)addrinfo_result->ai_addr)->sin6_family = host_result->h_addrtype;
+                                               memcpy((void*)&plat_addr,
+                                                               host_result->h_addr_list[0],
+                                                               host_result->h_length);
+
+                                               ((struct sockaddr_in6 *)addrinfo_result->ai_addr)->sin6_addr = plat_addr;
+                                       } else {
+                                               CLATD_LOG(LOG_ERROR, "failure in memory allocation");
+                                               free(addrinfo_result);
+                                               addrinfo_result = NULL;
+                                       }
+                               } else {
+                                       CLATD_LOG(LOG_ERROR, "failure in memory allocation");
+                               }
+                               __free_hostent(host_result);
+                               host_result = NULL;
+                               break;
+                       }
+                       __free_hostent(host_result);
+                       host_result = NULL;
+               }
+       }
+       *result = addrinfo_result;
+
+       CLATD_LOG(LOG_INFO, "done");
+       return 0;
+}