[Feature][ZXW-237]merge P54U03 version
Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No
Change-Id: Id39ef8b992af691eab09c01d4ea26da89e5f4049
diff --git a/ap/app/clatd/Android.mk b/ap/app/clatd/Android.mk
new file mode 100755
index 0000000..cae37fc
--- /dev/null
+++ b/ap/app/clatd/Android.mk
@@ -0,0 +1,52 @@
+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
+# Bug: http://b/33566695
+LOCAL_CFLAGS += -Wno-address-of-packed-member
+LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include system/netd/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
+# Bug: http://b/33566695
+LOCAL_CFLAGS += -Wno-address-of-packed-member
+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 libnetutils
+
+include $(BUILD_NATIVE_TEST)
+
+# Microbenchmark.
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
+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/ap/app/clatd/BUGS b/ap/app/clatd/BUGS
new file mode 100755
index 0000000..70aeb9f
--- /dev/null
+++ b/ap/app/clatd/BUGS
@@ -0,0 +1,5 @@
+known problems/assumptions:
+ - does not handle protocols other than ICMP, UDP, TCP and GRE
+ - assumes the handset has its own (routed) /64 ipv6 subnet
+ - assumes the /128 ipv6 subnet it generates can use the nat64 gateway
+ - assumes the nat64 gateway has the ipv4 address in the last 32 bits of the ipv6 address (that it uses a /96 plat subnet)
diff --git a/ap/app/clatd/LICENSE b/ap/app/clatd/LICENSE
new file mode 100755
index 0000000..261eeb9
--- /dev/null
+++ b/ap/app/clatd/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/ap/app/clatd/MODULE_LICENSE_APACHE2 b/ap/app/clatd/MODULE_LICENSE_APACHE2
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/ap/app/clatd/MODULE_LICENSE_APACHE2
diff --git a/ap/app/clatd/Makefile b/ap/app/clatd/Makefile
new file mode 100755
index 0000000..326c0be
--- /dev/null
+++ b/ap/app/clatd/Makefile
@@ -0,0 +1,73 @@
+#*******************************************************************************
+# include ZXIC application makefile
+#*******************************************************************************
+include $(COMMON_MK)
+
+#*******************************************************************************
+# execute
+#*******************************************************************************
+EXEC = clatd
+
+#*******************************************************************************
+# objects
+#*******************************************************************************
+OBJS = clatd.o \
+ dump.o \
+ checksum.o \
+ translate.o \
+ icmp.o \
+ ipv4.o \
+ ipv6.o \
+ config.o \
+ dns64.o \
+ logging.o \
+ getaddr.o \
+ netlink_callbacks.o \
+ netlink_msg.o \
+ setif.o \
+ mtu.o \
+ tun.o \
+ ring.o \
+ config_utils.o \
+ ifc_utils.o \
+ arc4random.o \
+ load_file.o \
+ getentropy.o
+
+
+#*******************************************************************************
+# include path
+#*******************************************************************************
+EXCFLAGS = -I$(zte_lib_path)/libnl/install/include/libnl3 -I$(zte_lib_path)/libnl/install
+CFLAGS += -DKEYSTREAM_ONLY -g
+CFLAGS += -I. $(EXCFLAGS)
+
+#*******************************************************************************
+# library
+#*******************************************************************************
+EXLDFLAGS = -L$(zte_lib_path)/libnl/install/lib
+LDLIBS += -lpthread
+LDLIBS += -lnl-3 $(EXLDFLAGS)
+
+
+#*******************************************************************************
+# library path
+#*******************************************************************************
+#LDLIBS += -L$(LIB_DIR)/libnvram
+
+#*******************************************************************************
+# targets
+#*******************************************************************************
+all: $(EXEC)
+
+$(EXEC): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS$(LDLIBS_$@))
+
+romfs:
+ cp $(EXEC) $(EXEC).elf
+ $(ROMFSINST) $(EXEC) /bin/$(EXEC)
+ $(ROMFSINST) ./clatd.conf /etc/clatd.conf
+
+clean:
+ -rm -f $(EXEC) *.elf *.gdb *.o
+
diff --git a/ap/app/clatd/NOTICE b/ap/app/clatd/NOTICE
new file mode 100755
index 0000000..5943b54
--- /dev/null
+++ b/ap/app/clatd/NOTICE
@@ -0,0 +1,189 @@
+ Copyright (c) 2010-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/ap/app/clatd/OWNERS b/ap/app/clatd/OWNERS
new file mode 100755
index 0000000..8f0a28b
--- /dev/null
+++ b/ap/app/clatd/OWNERS
@@ -0,0 +1,2 @@
+lorenzo@google.com
+ek@google.com
diff --git a/ap/app/clatd/arc4random.c b/ap/app/clatd/arc4random.c
new file mode 100755
index 0000000..10e5fe9
--- /dev/null
+++ b/ap/app/clatd/arc4random.c
@@ -0,0 +1,193 @@
+/* $OpenBSD: arc4random.c,v 1.54 2015/09/13 08:31:47 guenther Exp $ */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ChaCha based random number generator for OpenBSD.
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+//#define KEYSTREAM_ONLY
+#include "chacha_private.h"
+
+#define minimum(a, b) ((a) < (b) ? (a) : (b))
+#define explicit_bzero(p, s) memset(p, 0, s)
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define inline __inline
+#else /* __GNUC__ || _MSC_VER */
+#define inline
+#endif /* !__GNUC__ && !_MSC_VER */
+
+#define KEYSZ 32
+#define IVSZ 8
+#define BLOCKSZ 64
+#define RSBUFSZ (16*BLOCKSZ)
+
+/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
+static struct _rs {
+ size_t rs_have; /* valid bytes at end of rs_buf */
+ size_t rs_count; /* bytes till reseed */
+} *rs;
+
+/* Maybe be preserved in fork children, if _rs_allocate() decides. */
+static struct _rsx {
+ chacha_ctx rs_chacha; /* chacha context for random keystream */
+ u_char rs_buf[RSBUFSZ]; /* keystream blocks */
+} *rsx;
+
+int getentropy(void* buffer, size_t buffer_size);
+
+static inline int _rs_allocate(struct _rs **, struct _rsx **);
+static inline void _rs_forkdetect(void);
+
+#include "arc4random.h"
+
+static inline void _rs_rekey(u_char *dat, size_t datlen);
+
+static inline void _rs_init(u_char *buf, size_t n)
+{
+ if (n < KEYSZ + IVSZ)
+ return;
+
+ if (rs == NULL) {
+ if (_rs_allocate(&rs, &rsx) == -1)
+ abort();
+ }
+
+ chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
+ chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
+}
+
+static void _rs_stir(void)
+{
+ u_char rnd[KEYSZ + IVSZ];
+
+ if (getentropy(rnd, sizeof rnd) == -1)
+ _getentropy_fail();
+
+ if (!rs)
+ _rs_init(rnd, sizeof(rnd));
+ else
+ _rs_rekey(rnd, sizeof(rnd));
+ explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
+
+ /* invalidate rs_buf */
+ rs->rs_have = 0;
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
+
+ rs->rs_count = 1600000;
+}
+
+static inline void _rs_stir_if_needed(size_t len)
+{
+ _rs_forkdetect();
+ if (!rs || rs->rs_count <= len)
+ _rs_stir();
+ if (rs->rs_count <= len)
+ rs->rs_count = 0;
+ else
+ rs->rs_count -= len;
+}
+
+static inline void _rs_rekey(u_char *dat, size_t datlen)
+{
+#ifndef KEYSTREAM_ONLY
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
+#endif
+ /* fill rs_buf with the keystream */
+ chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
+ rsx->rs_buf, sizeof(rsx->rs_buf));
+ /* mix in optional user provided data */
+ if (dat) {
+ size_t i, m;
+
+ m = minimum(datlen, KEYSZ + IVSZ);
+ for (i = 0; i < m; i++)
+ rsx->rs_buf[i] ^= dat[i];
+ }
+ /* immediately reinit for backtracking resistance */
+ _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
+ memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
+ rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
+}
+
+static inline void _rs_random_buf(void *_buf, size_t n)
+{
+ u_char *buf = (u_char *)_buf;
+ u_char *keystream;
+ size_t m;
+
+ _rs_stir_if_needed(n);
+ while (n > 0) {
+ if (rs->rs_have > 0) {
+ m = minimum(n, rs->rs_have);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
+ - rs->rs_have;
+ memcpy(buf, keystream, m);
+ memset(keystream, 0, m);
+ buf += m;
+ n -= m;
+ rs->rs_have -= m;
+ }
+ if (rs->rs_have == 0)
+ _rs_rekey(NULL, 0);
+ }
+}
+
+static inline void _rs_random_u32(uint32_t *val)
+{
+ u_char *keystream;
+
+ _rs_stir_if_needed(sizeof(*val));
+ if (rs->rs_have < sizeof(*val))
+ _rs_rekey(NULL, 0);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
+ memcpy(val, keystream, sizeof(*val));
+ memset(keystream, 0, sizeof(*val));
+ rs->rs_have -= sizeof(*val);
+}
+
+uint32_t arc4random(void)
+{
+ uint32_t val;
+
+ _ARC4_LOCK();
+ _rs_random_u32(&val);
+ _ARC4_UNLOCK();
+ return val;
+}
+
+void arc4random_buf(void *buf, size_t n)
+{
+ _ARC4_LOCK();
+ _rs_random_buf(buf, n);
+ _ARC4_UNLOCK();
+}
+
diff --git a/ap/app/clatd/arc4random.h b/ap/app/clatd/arc4random.h
new file mode 100755
index 0000000..2435c0d
--- /dev/null
+++ b/ap/app/clatd/arc4random.h
@@ -0,0 +1,105 @@
+/* $OpenBSD: arc4random_linux.h,v 1.7 2014/07/20 20:51:13 bcook Exp $ */
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Stub functions for portability.
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <stdio.h>
+
+//#include <async_safe/log.h>
+
+//#include "bionic_prctl.h"
+#include <sys/prctl.h>
+
+// This is only supported by Android kernels, so it's not in the uapi headers.
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+
+
+// Android gets these from "thread_private.h".
+//#include "thread_private.h"
+static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx)
+#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx)
+
+#ifdef __GLIBC__
+extern void *__dso_handle;
+extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
+#define _ARC4_ATFORK(f) __register_atfork(NULL, NULL, (f), __dso_handle)
+#else
+#define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f))
+#endif
+
+// Formats a message to the log (priority 'fatal'), then aborts.
+// Implemented as a macro so that async_safe_fatal isn't on the stack when we crash:
+// we appear to go straight from the caller to abort, saving an uninteresting stack
+// frame.
+#define async_safe_fatal(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ abort(); \
+ } while (0) \
+
+static inline void _getentropy_fail(void) {
+ async_safe_fatal("getentropy failed: %s", strerror(errno));
+}
+
+volatile sig_atomic_t _rs_forked;
+
+static inline void
+_rs_forkdetect(void)
+{
+ static pid_t _rs_pid = 0;
+ pid_t pid = getpid();
+
+ if (_rs_pid == 0 || _rs_pid != pid || _rs_forked) {
+ _rs_pid = pid;
+ _rs_forked = 0;
+ if (rs)
+ memset(rs, 0, sizeof(*rs));
+ }
+}
+
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
+{
+ // OpenBSD's arc4random_linux.h allocates two separate mappings, but for
+ // themselves they just allocate both structs into one mapping like this.
+ struct {
+ struct _rs rs;
+ struct _rsx rsx;
+ } *p;
+
+ if ((p = mmap(NULL, sizeof(*p), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
+ return (-1);
+
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, sizeof(*p), "arc4random data");
+
+ *rsp = &p->rs;
+ *rsxp = &p->rsx;
+
+ return (0);
+}
diff --git a/ap/app/clatd/chacha_private.h b/ap/app/clatd/chacha_private.h
new file mode 100755
index 0000000..7c3680f
--- /dev/null
+++ b/ap/app/clatd/chacha_private.h
@@ -0,0 +1,222 @@
+/*
+chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+*/
+
+/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct
+{
+ u32 input[16]; /* could be compressed */
+} chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define U8TO32_LITTLE(p) \
+ (((u32)((p)[0]) ) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v) ); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[16] = "expand 32-byte k";
+static const char tau[16] = "expand 16-byte k";
+
+static void
+chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
+{
+ const char *constants;
+
+ x->input[4] = U8TO32_LITTLE(k + 0);
+ x->input[5] = U8TO32_LITTLE(k + 4);
+ x->input[6] = U8TO32_LITTLE(k + 8);
+ x->input[7] = U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = U8TO32_LITTLE(k + 0);
+ x->input[9] = U8TO32_LITTLE(k + 4);
+ x->input[10] = U8TO32_LITTLE(k + 8);
+ x->input[11] = U8TO32_LITTLE(k + 12);
+ x->input[0] = U8TO32_LITTLE(constants + 0);
+ x->input[1] = U8TO32_LITTLE(constants + 4);
+ x->input[2] = U8TO32_LITTLE(constants + 8);
+ x->input[3] = U8TO32_LITTLE(constants + 12);
+}
+
+static void
+chacha_ivsetup(chacha_ctx *x,const u8 *iv)
+{
+ x->input[12] = 0;
+ x->input[13] = 0;
+ x->input[14] = U8TO32_LITTLE(iv + 0);
+ x->input[15] = U8TO32_LITTLE(iv + 4);
+}
+
+static void
+chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
+{
+ u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if (!bytes) return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 = PLUS(x0,j0);
+ x1 = PLUS(x1,j1);
+ x2 = PLUS(x2,j2);
+ x3 = PLUS(x3,j3);
+ x4 = PLUS(x4,j4);
+ x5 = PLUS(x5,j5);
+ x6 = PLUS(x6,j6);
+ x7 = PLUS(x7,j7);
+ x8 = PLUS(x8,j8);
+ x9 = PLUS(x9,j9);
+ x10 = PLUS(x10,j10);
+ x11 = PLUS(x11,j11);
+ x12 = PLUS(x12,j12);
+ x13 = PLUS(x13,j13);
+ x14 = PLUS(x14,j14);
+ x15 = PLUS(x15,j15);
+
+#ifndef KEYSTREAM_ONLY
+ x0 = XOR(x0,U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1,U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2,U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3,U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4,U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5,U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6,U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7,U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8,U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9,U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10,U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11,U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12,U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13,U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14,U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15,U8TO32_LITTLE(m + 60));
+#endif
+
+ j12 = PLUSONE(j12);
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0,x0);
+ U32TO8_LITTLE(c + 4,x1);
+ U32TO8_LITTLE(c + 8,x2);
+ U32TO8_LITTLE(c + 12,x3);
+ U32TO8_LITTLE(c + 16,x4);
+ U32TO8_LITTLE(c + 20,x5);
+ U32TO8_LITTLE(c + 24,x6);
+ U32TO8_LITTLE(c + 28,x7);
+ U32TO8_LITTLE(c + 32,x8);
+ U32TO8_LITTLE(c + 36,x9);
+ U32TO8_LITTLE(c + 40,x10);
+ U32TO8_LITTLE(c + 44,x11);
+ U32TO8_LITTLE(c + 48,x12);
+ U32TO8_LITTLE(c + 52,x13);
+ U32TO8_LITTLE(c + 56,x14);
+ U32TO8_LITTLE(c + 60,x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+#ifndef KEYSTREAM_ONLY
+ m += 64;
+#endif
+ }
+}
diff --git a/ap/app/clatd/checksum.c b/ap/app/clatd/checksum.c
new file mode 100755
index 0000000..23a7c02
--- /dev/null
+++ b/ap/app/clatd/checksum.c
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ *
+ * checksum.c - ipv4/ipv6 checksum calculation
+ */
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "checksum.h"
+
+/* function: ip_checksum_add
+ * adds data to a checksum
+ * current - the current checksum (or 0 to start a new checksum)
+ * 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;
+}
+
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
+ * 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);
+
+ return temp_sum;
+}
+
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ * 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);
+}
+
+/* function: ip_checksum
+ * combined ip_checksum_add and ip_checksum_finish
+ * data - data to checksum
+ * len - length of data
+ */
+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);
+}
+
+/* function: ipv6_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp/icmp headers
+ * ip6 - the ipv6 header
+ * 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;
+}
+
+/* function: ipv4_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp headers
+ * 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;
+
+ 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));
+
+ return current;
+}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ * checksum - the header checksum in the original packet in network byte order
+ * old_hdr_sum - the pseudo-header checksum of the original packet
+ * 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
+ }
+}
diff --git a/ap/app/clatd/checksum.h b/ap/app/clatd/checksum.h
new file mode 100755
index 0000000..d0af88e
--- /dev/null
+++ b/ap/app/clatd/checksum.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <stdint.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+uint32_t ip_checksum_add(uint32_t current, const void *data, int len);
+uint16_t ip_checksum_finish(uint32_t temp_sum);
+uint16_t ip_checksum(const void *data, int len);
+
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/ap/app/clatd/clatd b/ap/app/clatd/clatd
new file mode 100755
index 0000000..0106af5
--- /dev/null
+++ b/ap/app/clatd/clatd
Binary files differ
diff --git a/ap/app/clatd/clatd.c b/ap/app/clatd/clatd.c
new file mode 100755
index 0000000..a1552d2
--- /dev/null
+++ b/ap/app/clatd/clatd.c
@@ -0,0 +1,545 @@
+/*
+ * 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 <linux/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 <grp.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"
+#include <asm/unistd.h>
+#include "my_header.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(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);
+ }
+ unsigned char info[40] = {0};
+ unsigned char *pinfo = info;
+ memcpy(pinfo, &Global_Clatd_Config.plat_subnet, sizeof(struct in6_addr));
+ pinfo = pinfo + sizeof(struct in6_addr);
+ memcpy(pinfo, &Global_Clatd_Config.ipv6_local_subnet, sizeof(struct in6_addr));
+ pinfo = pinfo + sizeof(struct in6_addr);
+ memcpy(pinfo, &Global_Clatd_Config.ipv4_local_subnet, sizeof(struct in_addr));
+ syscall(__NR_set_xlat, (char *)info, Global_Clatd_Config.default_pdp_interface);
+ 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("/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, ARRAY_SIZE(wait_fd),
+ NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) {
+ if (errno != EINTR) {
+ logmsg(ANDROID_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);
+ logmsg(ANDROID_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)) {
+ 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("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. Removing the
+ // following line causes XLAT failure in permissive mode.
+ //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/ap/app/clatd/clatd.conf b/ap/app/clatd/clatd.conf
new file mode 100755
index 0000000..1e1bf84
--- /dev/null
+++ b/ap/app/clatd/clatd.conf
@@ -0,0 +1,15 @@
+# host ID to use as the source of CLAT traffic
+# this is a /128 taken out of the /64 routed to the phone
+ipv6_host_id ::464
+
+# ipv4 subnet for the local traffic to use. This is a /32 host address
+ipv4_local_subnet 192.190.0.1
+
+# get the plat_subnet from dns lookups (requires DNS64)
+plat_from_dns64 yes
+# hostname to use to lookup plat subnet. must contain only A records
+plat_from_dns64_hostname ipv4only.arpa
+
+# plat subnet to send ipv4 traffic to. This is a /96 subnet.
+# This setting only makes sense with: plat_from_dns64 no
+#plat_subnet 2001:db8:1:2:3:4::
diff --git a/ap/app/clatd/clatd.elf b/ap/app/clatd/clatd.elf
new file mode 100755
index 0000000..0106af5
--- /dev/null
+++ b/ap/app/clatd/clatd.elf
Binary files differ
diff --git a/ap/app/clatd/clatd.h b/ap/app/clatd/clatd.h
new file mode 100755
index 0000000..036f3e8
--- /dev/null
+++ b/ap/app/clatd/clatd.h
@@ -0,0 +1,56 @@
+/*
+ * 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 system definitions
+ */
+#ifndef __CLATD_H__
+#define __CLATD_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_packet_index;
+typedef struct iovec clat_packet[CLAT_POS_MAX];
+
+struct clat_icmp_frag
+{
+ time_t time;
+ clat_packet data;
+ int iov_len;
+ unsigned short ip_id;
+ unsigned short ip_len;
+ int flag;//0-unused 1-first 2-last
+};
+
+#endif /* __CLATD_H__ */
diff --git a/ap/app/clatd/clatd_microbenchmark.c b/ap/app/clatd/clatd_microbenchmark.c
new file mode 100755
index 0000000..fed3100
--- /dev/null
+++ b/ap/app/clatd/clatd_microbenchmark.c
@@ -0,0 +1,214 @@
+/*
+ * 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;
+}
diff --git a/ap/app/clatd/clatd_test.cpp b/ap/app/clatd/clatd_test.cpp
new file mode 100755
index 0000000..b901c30
--- /dev/null
+++ b/ap/app/clatd/clatd_test.cpp
@@ -0,0 +1,936 @@
+/*
+ * 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_test.cpp - unit tests for clatd
+ */
+
+#include <iostream>
+
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <netinet/in6.h>
+#include <sys/uio.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "checksum.h"
+#include "translate.h"
+#include "config.h"
+#include "clatd.h"
+}
+
+// For convenience.
+#define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0])
+
+// Default translation parameters.
+static const char kIPv4LocalAddr[] = "192.0.0.4";
+static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464";
+static const char kIPv6PlatSubnet[] = "64:ff9b::";
+
+// 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 */ \
+ 0x00, 0x00, 0x40, 0x00, /* ID=0x0000, flags=IP_DF, offset=0 */ \
+ 55, (p), (c1), (c2), /* TTL=55, protocol=p, checksum=c1,c2 */ \
+ 192, 0, 0, 4, /* Src=192.0.0.4 */ \
+ 8, 8, 8, 8, /* Dst=8.8.8.8 */
+#define IPV4_UDP_HEADER IPV4_HEADER(IPPROTO_UDP, 0x73, 0xb0)
+#define IPV4_ICMP_HEADER IPV4_HEADER(IPPROTO_ICMP, 0x73, 0xc0)
+
+#define IPV6_HEADER(p) \
+ 0x60, 0x00, 0, 0, /* Version=6, tclass=0x00, flowlabel=0 */ \
+ 0, 21, (p), 55, /* plen=11, nxthdr=p, hlim=55 */ \
+ 0x20, 0x01, 0x0d, 0xb8, /* Src=2001:db8:0:b11::464 */ \
+ 0x00, 0x00, 0x0b, 0x11, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x04, 0x64, \
+ 0x00, 0x64, 0xff, 0x9b, /* Dst=64:ff9b::8.8.8.8 */ \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x08, 0x08, 0x08, 0x08,
+#define IPV6_UDP_HEADER IPV6_HEADER(IPPROTO_UDP)
+#define IPV6_ICMPV6_HEADER IPV6_HEADER(IPPROTO_ICMPV6)
+
+#define UDP_LEN 21
+#define UDP_HEADER \
+ 0xc8, 0x8b, 0, 53, /* Port 51339->53 */ \
+ 0x00, UDP_LEN, 0, 0, /* Length 21, checksum empty for now */
+
+#define PAYLOAD 'H', 'e', 'l', 'l', 'o', ' ', 0x4e, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x00
+
+#define IPV4_PING \
+ 0x08, 0x00, 0x88, 0xd0, /* Type 8, code 0, checksum 0x88d0 */ \
+ 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */
+
+#define IPV6_PING \
+ 0x80, 0x00, 0xc3, 0x42, /* Type 128, code 0, checksum 0xc342 */ \
+ 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */
+
+// Macros to return pseudo-headers from packets.
+#define IPV4_PSEUDOHEADER(ip, tlen) \
+ ip[12], ip[13], ip[14], ip[15], /* Source address */ \
+ ip[16], ip[17], ip[18], ip[19], /* Destination address */ \
+ 0, ip[9], /* 0, protocol */ \
+ ((tlen) >> 16) & 0xff, (tlen) & 0xff, /* Transport length */
+
+#define IPV6_PSEUDOHEADER(ip6, protocol, tlen) \
+ ip6[8], ip6[9], ip6[10], ip6[11], /* Source address */ \
+ ip6[12], ip6[13], ip6[14], ip6[15], \
+ ip6[16], ip6[17], ip6[18], ip6[19], \
+ ip6[20], ip6[21], ip6[22], ip6[23], \
+ ip6[24], ip6[25], ip6[26], ip6[27], /* Destination address */ \
+ ip6[28], ip6[29], ip6[30], ip6[31], \
+ ip6[32], ip6[33], ip6[34], ip6[35], \
+ ip6[36], ip6[37], ip6[38], ip6[39], \
+ ((tlen) >> 24) & 0xff, /* Transport length */ \
+ ((tlen) >> 16) & 0xff, \
+ ((tlen) >> 8) & 0xff, \
+ (tlen) & 0xff, \
+ 0, 0, 0, (protocol),
+
+// A fragmented DNS request.
+static const uint8_t kIPv4Frag1[] = {
+ 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x00, 0x40, 0x11,
+ 0x8c, 0x6d, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00
+};
+static const uint8_t kIPv4Frag2[] = {
+ 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x02, 0x40, 0x11,
+ 0x8c, 0x6b, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65
+};
+static const uint8_t kIPv4Frag3[] = {
+ 0x45, 0x00, 0x00, 0x1d, 0xfe, 0x47, 0x00, 0x04, 0x40, 0x11,
+ 0xac, 0x70, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 };
+static const size_t kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2),
+ sizeof(kIPv4Frag3) };
+
+static const uint8_t kIPv6Frag1[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x47, 0x14, 0x5d,
+ 0x00, 0x35, 0x00, 0x29, 0xeb, 0x91, 0x50, 0x47, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x00
+};
+
+static const uint8_t kIPv6Frag2[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0xfe, 0x47, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65
+};
+
+static const uint8_t kIPv6Frag3[] = {
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2c, 0x40, 0x20, 0x01,
+ 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
+ 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0xfe, 0x47, 0x03, 0x63,
+ 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 };
+static const size_t kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2),
+ sizeof(kIPv6Frag3) };
+
+static const uint8_t kReassembledIPv4[] = {
+ 0x45, 0x00, 0x00, 0x3d, 0xfe, 0x47, 0x00, 0x00, 0x40, 0x11,
+ 0xac, 0x54, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08,
+ 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47,
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+ 0x01
+};
+
+// Expected checksums.
+static const uint32_t kUdpPartialChecksum = 0xd5c8;
+static const uint32_t kPayloadPartialChecksum = 0x31e9c;
+static const uint16_t kUdpV4Checksum = 0xd0c7;
+static const uint16_t kUdpV6Checksum = 0xa74a;
+
+uint8_t ip_version(const uint8_t *packet) {
+ uint8_t version = packet[0] >> 4;
+ return version;
+}
+
+int is_ipv4_fragment(struct iphdr *ip) {
+ // A packet is a fragment if its fragment offset is nonzero or if the MF flag is set.
+ return ntohs(ip->frag_off) & (IP_OFFMASK | IP_MF);
+}
+
+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);
+ return len >= sizeof(*ip6) + sizeof(*frag) &&
+ (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG));
+}
+
+int ipv4_fragment_offset(struct iphdr *ip) {
+ return ntohs(ip->frag_off) & IP_OFFMASK;
+}
+
+int ipv6_fragment_offset(struct ip6_frag *frag) {
+ return ntohs((frag->ip6f_offlg & IP6F_OFF_MASK) >> 3);
+}
+
+void check_packet(const uint8_t *packet, size_t len, const char *msg) {
+ void *payload;
+ size_t payload_length = 0;
+ uint32_t pseudo_checksum = 0;
+ uint8_t protocol = 0;
+ int version = ip_version(packet);
+ switch (version) {
+ case 4: {
+ 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;
+ if (!is_ipv4_fragment(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";
+ break;
+ }
+ case 6: {
+ 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);
+ ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag))
+ << msg << ": IPv6 fragment: short fragment header\n";
+ protocol = frag->ip6f_nxt;
+ 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;
+ payload_length = len - sizeof(*ip6);
+ }
+ ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6)
+ << msg << ": Unsupported IPv6 next header " << protocol;
+ if (payload_length) {
+ pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol);
+ }
+ break;
+ }
+ default:
+ FAIL() << msg << ": Unsupported IP version " << version << "\n";
+ return;
+ }
+
+ // If we understand the payload, verify the checksum.
+ if (payload_length) {
+ uint16_t checksum;
+ switch(protocol) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_ICMPV6:
+ checksum = ip_checksum_finish(ip_checksum_add(pseudo_checksum, payload, payload_length));
+ break;
+ case IPPROTO_ICMP:
+ checksum = ip_checksum(payload, payload_length);
+ break;
+ default:
+ checksum = 0; // Don't check.
+ break;
+ }
+ EXPECT_EQ(0, checksum) << msg << ": Incorrect transport checksum\n";
+ }
+
+ if (protocol == IPPROTO_UDP) {
+ 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) {
+ EXPECT_EQ(payload_length, ntohs(udp->len)) << msg << ": Incorrect UDP length\n";
+ }
+ }
+}
+
+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;
+ uint8_t protocol = 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 headersize, payload_offset;
+
+ ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n";
+ check_packet(packet, len, "Fragment sanity check");
+
+ switch (version) {
+ case 4: {
+ 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";
+
+ headersize = sizeof(*ip_orig);
+ payload_offset = headersize;
+ if (pos == 0) {
+ 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);
+ 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";
+
+ headersize = sizeof(*ip6_orig);
+ payload_offset = sizeof(*ip6_orig) + sizeof(*frag);
+ if (pos == 0) {
+ ip6 = (struct ip6_hdr *) reassembled;
+ protocol = frag->ip6f_nxt;
+ }
+ break;
+ }
+ default:
+ FAIL() << msg << ": Invalid IP version << " << version;
+ }
+
+ // If this is the first fragment, copy the header.
+ if (pos == 0) {
+ ASSERT_LT(headersize, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n";
+ memcpy(reassembled, packet, headersize);
+ total_length = headersize;
+ pos += headersize;
+ }
+
+ // Copy the payload.
+ int payload_length = len - payload_offset;
+ total_length += payload_length;
+ ASSERT_LT(total_length, *reassembled_len) << msg << ": Reassembly buffer too small\n";
+ memcpy(reassembled + pos, packet + payload_offset, payload_length);
+ 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));
+ ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n";
+ }
+ if (ip6) {
+ 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";
+ }
+
+ *reassembled_len = total_length;
+}
+
+void check_data_matches(const void *expected, const void *actual, size_t len, const char *msg) {
+ if (memcmp(expected, actual, len)) {
+ // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4.
+ int hexdump_len = len * 3 + (len / 20 + 1) * 5;
+ char expected_hexdump[hexdump_len], actual_hexdump[hexdump_len];
+ unsigned pos = 0;
+ for (unsigned i = 0; i < len; i++) {
+ if (i % 20 == 0) {
+ snprintf(expected_hexdump + pos, hexdump_len - pos, "\n ");
+ snprintf(actual_hexdump + pos, hexdump_len - pos, "\n ");
+ pos += 4;
+ }
+ 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"
+ << "\n Expected:" << (char *) expected_hexdump
+ << "\n Actual:" << (char *) actual_hexdump << "\n";
+ }
+}
+
+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));
+ 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);
+ 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)));
+}
+
+// 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);
+}
+
+void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen,
+ const char *msg) {
+ int fds[2];
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) {
+ abort();
+ }
+
+ char foo[512];
+ snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg);
+ check_packet(original, original_len, foo);
+
+ int read_fd, write_fd;
+ uint16_t expected_proto;
+ int version = ip_version(original);
+ switch (version) {
+ case 4:
+ expected_proto = htons(ETH_P_IPV6);
+ 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];
+ break;
+ default:
+ FAIL() << msg << ": Unsupported IP version " << version << "\n";
+ break;
+ }
+
+ translate_packet(write_fd, (version == 4), original, original_len);
+
+ snprintf(foo, sizeof(foo), "%s: Invalid translated packet", msg);
+ if (version == 6) {
+ // Translating to IPv4. Expect a tun header.
+ struct tun_pi new_tun_header;
+ struct iovec iov[] = {
+ { &new_tun_header, sizeof(new_tun_header) },
+ { 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";
+ EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n";
+ *outlen = len - sizeof(new_tun_header);
+ check_packet(out, *outlen, msg);
+ } else {
+ FAIL() << msg << ": Packet was not translated: len=" << len;
+ *outlen = 0;
+ }
+ } else {
+ // Translating to IPv6. Expect raw packet.
+ *outlen = read(read_fd, out, *outlen);
+ check_packet(out, *outlen, 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);
+ EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n";
+ check_data_matches(expected, translated, translated_len, msg);
+}
+
+void check_fragment_translation(const uint8_t *original[], const size_t original_lengths[],
+ const uint8_t *expected[], const size_t expected_lengths[],
+ int numfragments, const char *msg) {
+ for (int i = 0; i < numfragments; i++) {
+ // 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);
+ }
+
+ // Sanity check that reassembling the original and translated fragments produces valid packets.
+ uint8_t reassembled[MAXMTU];
+ size_t reassembled_len = sizeof(reassembled);
+ reassemble_packet(original, original_lengths, numfragments, reassembled, &reassembled_len, msg);
+ check_packet(reassembled, reassembled_len, msg);
+
+ uint8_t translated[MAXMTU];
+ size_t translated_len = sizeof(translated);
+ do_translate_packet(reassembled, reassembled_len, translated, &translated_len, msg);
+ check_packet(translated, translated_len, msg);
+}
+
+int get_transport_checksum(const uint8_t *packet) {
+ struct iphdr *ip;
+ struct ip6_hdr *ip6;
+ uint8_t protocol;
+ const void *payload;
+
+ int version = ip_version(packet);
+ switch (version) {
+ case 4:
+ ip = (struct iphdr *) packet;
+ if (is_ipv4_fragment(ip)) {
+ return -1;
+ }
+ protocol = ip->protocol;
+ payload = ip + 1;
+ break;
+ case 6:
+ ip6 = (struct ip6_hdr *) packet;
+ protocol = ip6->ip6_nxt;
+ payload = ip6 + 1;
+ break;
+ default:
+ return -1;
+ }
+
+ switch (protocol) {
+ case IPPROTO_UDP:
+ return ((struct udphdr *) payload)->check;
+
+ case IPPROTO_TCP:
+ return ((struct tcphdr *) payload)->check;
+
+ case IPPROTO_FRAGMENT:
+ default:
+ return -1;
+ }
+}
+
+struct clat_config Global_Clatd_Config;
+
+class ClatdTest : public ::testing::Test {
+ protected:
+ 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;
+ Global_Clatd_Config.use_dynamic_iid = 1;
+ }
+};
+
+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];
+ inet_ntop(AF_INET6, expected, expected_str, sizeof(expected_str));
+ inet_ntop(AF_INET6, actual, actual_str, sizeof(actual_str));
+ FAIL()
+ << "Unexpected IPv6 address:: "
+ << "\n Expected: " << expected_str
+ << "\n Actual: " << actual_str
+ << "\n";
+ }
+}
+
+TEST_F(ClatdTest, TestIPv6PrefixEqual) {
+ EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet,
+ &Global_Clatd_Config.plat_subnet));
+ EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet,
+ &Global_Clatd_Config.ipv6_local_subnet));
+
+ struct in6_addr subnet2 = Global_Clatd_Config.ipv6_local_subnet;
+ EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2));
+ EXPECT_TRUE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet));
+
+ subnet2.s6_addr[6] = 0xff;
+ EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2));
+ EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet));
+}
+
+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;
+ for (int shift = 0; shift < 8; shift++) {
+ onebits += (*byte >> shift) & 1;
+ }
+ }
+ return onebits;
+}
+
+TEST_F(ClatdTest, TestCountOnebits) {
+ uint64_t i;
+ i = 1;
+ ASSERT_EQ(1, count_onebits(&i, sizeof(i)));
+ i <<= 61;
+ ASSERT_EQ(1, count_onebits(&i, sizeof(i)));
+ 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)));
+}
+
+TEST_F(ClatdTest, TestGenIIDConfigured) {
+ struct in6_addr myaddr, expected;
+ Global_Clatd_Config.use_dynamic_iid = 0;
+ ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id));
+ ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected));
+ ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr));
+ config_generate_local_ipv6_subnet(&myaddr);
+ expect_ipv6_addr_equal(&expected, &myaddr);
+
+ Global_Clatd_Config.use_dynamic_iid = 1;
+ config_generate_local_ipv6_subnet(&myaddr);
+ EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&expected, &myaddr));
+}
+
+TEST_F(ClatdTest, TestGenIIDRandom) {
+ struct in6_addr interface_ipv6;
+ ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &interface_ipv6));
+ Global_Clatd_Config.ipv6_host_id = in6addr_any;
+
+ // Generate a boatload of random IIDs.
+ int onebits = 0;
+ uint64_t prev_iid = 0;
+ for (int i = 0; i < 100000; i++) {
+ struct in6_addr myaddr = interface_ipv6;
+
+ config_generate_local_ipv6_subnet(&myaddr);
+
+ // Check the generated IP address is in the same prefix as the interface IPv6 address.
+ 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]);
+ ASSERT_TRUE(iid != prev_iid)
+ << "Two consecutive random IIDs are the same: "
+ << std::showbase << std::hex
+ << iid << "\n";
+ prev_iid = iid;
+
+ // 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 in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet;
+
+ uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr)));
+ uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) +
+ ip_checksum_add(0, &myaddr, sizeof(myaddr)));
+
+ if (c1 != c2) {
+ char myaddr_str[INET6_ADDRSTRLEN], plat_str[INET6_ADDRSTRLEN], ipv4_str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &myaddr, myaddr_str, sizeof(myaddr_str));
+ inet_ntop(AF_INET6, plat_subnet, plat_str, sizeof(plat_str));
+ inet_ntop(AF_INET, ipv4addr, ipv4_str, sizeof(ipv4_str));
+ FAIL()
+ << "Bad IID: " << myaddr_str
+ << " not checksum-neutral with " << ipv4_str << " and " << plat_str
+ << std::showbase << std::hex
+ << "\n IPv4 checksum: " << c1
+ << "\n IPv6 checksum: " << c2
+ << "\n";
+ }
+
+ // Check that IIDs are roughly random and use all the bits by counting the
+ // total number of bits set to 1 in a random sample of 100000 generated IIDs.
+ onebits += count_onebits(&iid, sizeof(iid));
+ }
+ EXPECT_LE(3190000, onebits);
+ EXPECT_GE(3210000, onebits);
+}
+
+extern "C" addr_free_func config_is_ipv4_address_free;
+int never_free(in_addr_t /* addr */) { return 0; }
+int always_free(in_addr_t /* addr */) { return 1; }
+int only2_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 2; }
+int over6_free(in_addr_t addr) { return (ntohl(addr) & 0xff) >= 6; }
+int only10_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 10; }
+
+TEST_F(ClatdTest, SelectIPv4Address) {
+ struct in_addr addr;
+
+ inet_pton(AF_INET, kIPv4LocalAddr, &addr);
+
+ addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free;
+
+ // If no addresses are free, return INADDR_NONE.
+ config_is_ipv4_address_free = never_free;
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29));
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 16));
+
+ // If the configured address is free, pick that. But a prefix that's too big is invalid.
+ config_is_ipv4_address_free = always_free;
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 29));
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 20));
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 15));
+
+ // A prefix length of 32 works, but anything above it is invalid.
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 32));
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 33));
+
+ // If another address is free, pick it.
+ config_is_ipv4_address_free = over6_free;
+ EXPECT_EQ(inet_addr("192.0.0.6"), config_select_ipv4_address(&addr, 29));
+
+ // Check that we wrap around to addresses that are lower than the first address.
+ config_is_ipv4_address_free = only2_free;
+ EXPECT_EQ(inet_addr("192.0.0.2"), config_select_ipv4_address(&addr, 29));
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 30));
+
+ // If a free address exists outside the prefix, we don't pick it.
+ config_is_ipv4_address_free = only10_free;
+ EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29));
+ EXPECT_EQ(inet_addr("192.0.0.10"), config_select_ipv4_address(&addr, 24));
+
+ // 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");
+ 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, DataSanitycheck) {
+ // Sanity checks the data.
+ uint8_t v4_header[] = { IPV4_UDP_HEADER };
+ ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n";
+
+ uint8_t v6_header[] = { IPV6_UDP_HEADER };
+ ASSERT_EQ(sizeof(struct ip6_hdr), sizeof(v6_header)) << "Test IPv6 header: incorrect length\n";
+
+ uint8_t udp_header[] = { UDP_HEADER };
+ ASSERT_EQ(sizeof(struct udphdr), sizeof(udp_header)) << "Test UDP header: incorrect length\n";
+
+ // 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));
+ 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));
+ 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");
+
+ uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD };
+ check_packet(ipv4_ping, sizeof(ipv4_ping), "IPv4 ping sanity check");
+
+ uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD };
+ check_packet(ipv6_ping, sizeof(ipv6_ping), "IPv6 ping sanity check");
+
+ // 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");
+ 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";
+ 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";
+ 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_pseudo_header[] = { IPV4_PSEUDOHEADER(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";
+
+ 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);
+ EXPECT_EQ(ip_checksum_finish(pseudo_checksum),
+ ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header)))
+ << "ipv6_pseudo_header_checksum incorrect\n";
+}
+
+TEST_F(ClatdTest, TransportChecksum) {
+ uint8_t udphdr[] = { UDP_HEADER };
+ uint8_t payload[] = { PAYLOAD };
+ EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr)))
+ << "UDP partial checksum\n";
+ EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload)))
+ << "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);
+
+ EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n";
+ EXPECT_EQ(0x2644bU, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n";
+ EXPECT_EQ(
+ kUdpV4Checksum,
+ ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum))
+ << "Unexpected UDP/IPv4 checksum\n";
+ EXPECT_EQ(
+ kUdpV6Checksum,
+ ip_checksum_finish(ipv6_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum))
+ << "Unexpected UDP/IPv6 checksum\n";
+
+ EXPECT_EQ(kUdpV6Checksum,
+ ip_checksum_adjust(kUdpV4Checksum, ipv4_pseudo_sum, ipv6_pseudo_sum))
+ << "Adjust IPv4/UDP checksum to IPv6\n";
+ EXPECT_EQ(kUdpV4Checksum,
+ ip_checksum_adjust(kUdpV6Checksum, ipv6_pseudo_sum, ipv4_pseudo_sum))
+ << "Adjust IPv6/UDP checksum to IPv4\n";
+}
+
+TEST_F(ClatdTest, AdjustChecksum) {
+ struct checksum_data {
+ uint16_t checksum;
+ uint32_t old_hdr_sum;
+ uint32_t new_hdr_sum;
+ uint16_t result;
+ } DATA[] = {
+ { 0x1423, 0xb8ec, 0x2d757, 0xf5b5 },
+ { 0xf5b5, 0x2d757, 0xb8ec, 0x1423 },
+ { 0xdd2f, 0x5555, 0x3285, 0x0000 },
+ { 0x1215, 0x5560, 0x15560 + 20, 0x1200 },
+ { 0xd0c7, 0x3ad0, 0x2644b, 0xa74a },
+ };
+ unsigned i = 0;
+
+ for (i = 0; i < ARRAYSIZE(DATA); i++) {
+ struct checksum_data *data = DATA + i;
+ uint16_t result = ip_checksum_adjust(data->checksum, data->old_hdr_sum, data->new_hdr_sum);
+ EXPECT_EQ(result, data->result)
+ << "Incorrect checksum" << std::showbase << std::hex
+ << "\n Expected: " << data->result
+ << "\n Actual: " << result
+ << "\n checksum=" << data->checksum
+ << " old_sum=" << data->old_hdr_sum << " new_sum=" << data->new_hdr_sum << "\n";
+ }
+}
+
+TEST_F(ClatdTest, Translate) {
+ 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);
+ fix_udp_checksum(udp_ipv6);
+ check_translated_packet(udp_ipv4, sizeof(udp_ipv4), udp_ipv6, sizeof(udp_ipv6),
+ "UDP/IPv4 -> UDP/IPv6 translation");
+ check_translated_packet(udp_ipv6, sizeof(udp_ipv6), udp_ipv4, sizeof(udp_ipv4),
+ "UDP/IPv6 -> UDP/IPv4 translation");
+
+ uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD };
+ uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD };
+ check_translated_packet(ipv4_ping, sizeof(ipv4_ping), ipv6_ping, sizeof(ipv6_ping),
+ "ICMP->ICMPv6 translation");
+ check_translated_packet(ipv6_ping, sizeof(ipv6_ping), ipv4_ping, sizeof(ipv4_ping),
+ "ICMPv6->ICMP translation");
+}
+
+TEST_F(ClatdTest, Fragmentation) {
+ check_fragment_translation(kIPv4Fragments, kIPv4FragLengths,
+ kIPv6Fragments, kIPv6FragLengths,
+ ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation");
+
+ check_fragment_translation(kIPv6Fragments, kIPv6FragLengths,
+ kIPv4Fragments, kIPv4FragLengths,
+ ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation");
+}
+
+void check_translate_checksum_neutral(const uint8_t *original, size_t original_len,
+ 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);
+ 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 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";
+}
+
+TEST_F(ClatdTest, TranslateChecksumNeutral) {
+ // Generate a random clat IPv6 address and check that translation is checksum-neutral.
+ Global_Clatd_Config.ipv6_host_id = in6addr_any;
+ 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]);
+
+ // Check that translating UDP packets is checksum-neutral. First, IPv4.
+ uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD };
+ fix_udp_checksum(udp_ipv4);
+ check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20,
+ "UDP/IPv4 -> UDP/IPv6 checksum neutral");
+
+ // 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;
+ 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");
+}
diff --git a/ap/app/clatd/config.c b/ap/app/clatd/config.c
new file mode 100755
index 0000000..552e9ab
--- /dev/null
+++ b/ap/app/clatd/config.c
@@ -0,0 +1,402 @@
+/*
+ * 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 "ifc.h"
+#include "config_utils.h"
+
+#include "config.h"
+#include "dns64.h"
+#include "logging.h"
+#include "getaddr.h"
+#include "clatd.h"
+#include "checksum.h"
+
+void arc4random_buf(void *buf, size_t n);
+
+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 >= 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.
+ arc4random_buf(&myaddr->s6_addr[8], 8);
+ //arc4random_addrandom(&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/ap/app/clatd/config.h b/ap/app/clatd/config.h
new file mode 100755
index 0000000..e31a81d
--- /dev/null
+++ b/ap/app/clatd/config.h
@@ -0,0 +1,50 @@
+/*
+ * 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.h - configuration settings
+ */
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#include <netinet/in.h>
+#include <linux/if.h>
+
+#define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4"
+#define DEFAULT_IPV4_LOCAL_PREFIXLEN "29"
+#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4only.arpa"
+
+struct clat_config {
+ int16_t mtu, ipv4mtu;
+ struct in6_addr ipv6_local_subnet;
+ struct in6_addr ipv6_host_id;
+ struct in_addr ipv4_local_subnet;
+ int16_t ipv4_local_prefixlen;
+ struct in6_addr plat_subnet;
+ char *default_pdp_interface;
+ char *plat_from_dns64_hostname;
+ int use_dynamic_iid;
+};
+
+extern struct clat_config Global_Clatd_Config;
+
+int read_config(const char *file, const char *uplink_interface, const char *plat_prefix,
+ 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);
+
+typedef int (*addr_free_func)(in_addr_t addr);
+
+#endif /* __CONFIG_H__ */
diff --git a/ap/app/clatd/config_utils.c b/ap/app/clatd/config_utils.c
new file mode 100755
index 0000000..284b32a
--- /dev/null
+++ b/ap/app/clatd/config_utils.c
@@ -0,0 +1,325 @@
+/*
+ * 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 <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <config_utils.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+ cnode* 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 = load_file(fn, 0);
+ config_load(root, data);
+ // TODO: deliberate leak :-/
+}
+
+void config_free(cnode *root)
+{
+ cnode *cur = root->first_child;
+
+ while (cur) {
+ cnode *prev = cur;
+ config_free(cur);
+ cur = cur->next;
+ free(prev);
+ }
+}
diff --git a/ap/app/clatd/config_utils.h b/ap/app/clatd/config_utils.h
new file mode 100755
index 0000000..68431eb
--- /dev/null
+++ b/ap/app/clatd/config_utils.h
@@ -0,0 +1,66 @@
+/*
+ * 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);
+
+/* free a config node tree */
+void config_free(cnode *root);
+
+void *load_file(const char *fn, unsigned *_sz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ap/app/clatd/debug.h b/ap/app/clatd/debug.h
new file mode 100755
index 0000000..8e09672
--- /dev/null
+++ b/ap/app/clatd/debug.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ *
+ * debug.h - debug settings
+ */
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+// set to 1 to enable debug logging and packet dumping.
+#define CLAT_DEBUG 0
+
+#endif /* __DEBUG_H__ */
diff --git a/ap/app/clatd/dns64.c b/ap/app/clatd/dns64.c
new file mode 100755
index 0000000..3a287cf
--- /dev/null
+++ b/ap/app/clatd/dns64.c
@@ -0,0 +1,82 @@
+/*
+ * 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 "NetdClient.h"
+//#include "resolv_netid.h"
+#include "my_header.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...");
+
+ // 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);
+ status = getaddrinfo(ipv4_name, NULL, &hints, &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;
+}
diff --git a/ap/app/clatd/dns64.h b/ap/app/clatd/dns64.h
new file mode 100755
index 0000000..f5eaea8
--- /dev/null
+++ b/ap/app/clatd/dns64.h
@@ -0,0 +1,23 @@
+/*
+ * 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.h - find the nat64 prefix with a dns64 lookup
+ */
+#ifndef __DNS64_H__
+#define __DNS64_H__
+
+int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix);
+
+#endif
diff --git a/ap/app/clatd/dump.c b/ap/app/clatd/dump.c
new file mode 100755
index 0000000..b8e1efe
--- /dev/null
+++ b/ap/app/clatd/dump.c
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ *
+ * dump.c - print various headers for debugging
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/ip.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 "debug.h"
+#include "checksum.h"
+#include "clatd.h"
+#include "logging.h"
+
+#if CLAT_DEBUG
+
+/* print ip header */
+void dump_ip(struct iphdr *header) {
+ u_int16_t frag_flags;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ 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("frag: ");
+ if(frag_flags & IP_RF) {
+ printf("(RF) ");
+ }
+ if(frag_flags & IP_DF) {
+ printf("DF ");
+ }
+ 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));
+ inet_ntop(AF_INET, &header->saddr, addrstr, sizeof(addrstr));
+ printf("saddr = %s\n",addrstr);
+ inet_ntop(AF_INET, &header->daddr, addrstr, sizeof(addrstr));
+ printf("daddr = %s\n",addrstr);
+}
+
+/* print ip6 header */
+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);
+
+ inet_ntop(AF_INET6, &header->ip6_src, addrstr, sizeof(addrstr));
+ printf("source = %s\n",addrstr);
+
+ inet_ntop(AF_INET6, &header->ip6_dst, addrstr, sizeof(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("echo reply");
+ } 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));
+ }
+}
+
+/* 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("(echo request)");
+ } else if(icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
+ printf("(echo reply)");
+ }
+ printf("\n");
+ printf("code = %x\n",icmp6->icmp6_code);
+
+ 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);
+ }
+}
+
+/* print udp header */
+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);
+
+ 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);
+}
+
+/* print ipv4/udp header */
+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) {
+ 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) {
+ uint16_t my_checksum;
+
+ temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr));
+ 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);
+
+ 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);
+#ifdef __BIONIC__
+ printf("CWR = %x\n",tcp->cwr);
+ printf("ECE = %x\n",tcp->ece);
+#else
+ 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);
+
+ if(options) {
+ size_t i;
+
+ printf("options: ");
+ 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) {
+ uint32_t temp_checksum;
+
+ temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size);
+ dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
+}
+
+/* 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) {
+ uint32_t temp_checksum;
+
+ 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];
+ size_t i;
+
+ output[0] = '\0';
+ for(i = 0; i < len && i < PACKETLEN; i++) {
+ snprintf(output + i*3, 4, " %02x", data[i]);
+ }
+ output[len*3+3] = '\0';
+
+ logmsg(ANDROID_LOG_WARN,"info %s len %d data%s", info, len, output);
+}
+
+void dump_iovec(const struct iovec *iov, int iov_len) {
+ int i;
+ char *str;
+ for (i = 0; i < iov_len; i++) {
+ asprintf(&str, "iov[%d]: ", i);
+ logcat_hexdump(str, iov[i].iov_base, iov[i].iov_len);
+ free(str);
+ }
+}
+#endif // CLAT_DEBUG
diff --git a/ap/app/clatd/dump.h b/ap/app/clatd/dump.h
new file mode 100755
index 0000000..bb41b3b
--- /dev/null
+++ b/ap/app/clatd/dump.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ * dump.h - debug functions
+ */
+#ifndef __DUMP_H__
+#define __DUMP_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_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 logcat_hexdump(const char *info, const uint8_t *data, size_t len);
+void dump_iovec(const struct iovec *iov, int iov_len);
+
+#endif /* __DUMP_H__ */
diff --git a/ap/app/clatd/getaddr.c b/ap/app/clatd/getaddr.c
new file mode 100755
index 0000000..e0478cf
--- /dev/null
+++ b/ap/app/clatd/getaddr.c
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ *
+ * getaddr.c - get a locally configured address
+ */
+#include <netinet/in.h>
+#include <strings.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <linux/if_addr.h>
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include "getaddr.h"
+#include "netlink_msg.h"
+#include "logging.h"
+
+// shared state between getinterface_ip and getaddr_cb
+struct target {
+ int family;
+ unsigned int ifindex;
+ union anyip ip;
+ int foundip;
+};
+
+/* function: getaddr_cb
+ * callback for getinterface_ip
+ * 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;
+ struct rtattr *rta_p;
+ int rta_len;
+ struct target *targ_p = (struct target *)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_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) {
+ case IFA_ADDRESS:
+ 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) {
+ memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
+ targ_p->foundip = 1;
+ return NL_OK;
+ }
+ break;
+ }
+ }
+
+ return NL_OK;
+}
+
+/* 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
+ */
+static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla,
+ __attribute__((unused)) struct nlmsgerr *err,
+ __attribute__((unused)) void *arg) {
+ return NL_OK;
+}
+
+/* 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
+ */
+union anyip *getinterface_ip(const char *interface, int family) {
+ struct ifaddrmsg ifa;
+ struct nl_cb *callbacks = NULL;
+ struct target targ;
+ union anyip *retval = NULL;
+
+ targ.family = family;
+ targ.foundip = 0;
+ targ.ifindex = if_nametoindex(interface);
+ 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) {
+ goto cleanup;
+ }
+ nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, getaddr_cb, &targ);
+ nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &targ);
+
+ // sends message and waits for a response
+ send_ifaddrmsg(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &ifa, callbacks);
+
+ if(targ.foundip) {
+ retval = malloc(sizeof(union anyip));
+ if(!retval) {
+ logmsg(ANDROID_LOG_FATAL,"getinterface_ip/out of memory");
+ goto cleanup;
+ }
+ memcpy(retval, &targ.ip, sizeof(union anyip));
+ }
+
+cleanup:
+ if(callbacks)
+ nl_cb_put(callbacks);
+
+ return retval;
+}
diff --git a/ap/app/clatd/getaddr.h b/ap/app/clatd/getaddr.h
new file mode 100755
index 0000000..5718e62
--- /dev/null
+++ b/ap/app/clatd/getaddr.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ *
+ * getaddr.h - get a locally configured address
+ */
+#ifndef __GETADDR_H__
+#define __GETADDR_H__
+
+union anyip {
+ struct in6_addr ip6;
+ struct in_addr ip4;
+};
+
+union anyip *getinterface_ip(const char *interface, int family);
+
+#endif
diff --git a/ap/app/clatd/getentropy.c b/ap/app/clatd/getentropy.c
new file mode 100755
index 0000000..04bf81a
--- /dev/null
+++ b/ap/app/clatd/getentropy.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/random.h>
+#include <unistd.h>
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 00400000
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 02000000
+#endif
+
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+
+static int getentropy_urandom(void* buffer, size_t buffer_size, int saved_errno) {
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0));
+ if (fd == -1) return -1;
+
+ size_t collected = 0;
+ while (collected < buffer_size) {
+ ssize_t count = TEMP_FAILURE_RETRY(read(fd, (char*)buffer + collected, buffer_size - collected));
+ if (count == -1) {
+ close(fd);
+ return -1;
+ }
+ collected += count;
+ }
+
+ close(fd);
+ errno = saved_errno;
+ return 0;
+}
+
+int getentropy(void* buffer, size_t buffer_size) {
+ if (buffer_size > 256) {
+ errno = EIO;
+ return -1;
+ }
+
+ int saved_errno = errno;
+
+ size_t collected = 0;
+ while (collected < buffer_size) {
+#if 0 //No ""getrandom on Linux-3.4.5
+ long count = TEMP_FAILURE_RETRY(getrandom((char*)buffer + collected,
+ buffer_size - collected, GRND_NONBLOCK));
+#else
+ long count = -1;
+#endif
+ if (count == -1) {
+ // EAGAIN: there isn't enough entropy right now.
+ // ENOSYS/EINVAL: getrandom(2) or GRND_NONBLOCK isn't supported.
+ // EFAULT: `buffer` is invalid.
+ // Try /dev/urandom regardless because it can't hurt,
+ // and we don't need to optimize the EFAULT case.
+ // See http://b/33059407 and http://b/67015565.
+ return getentropy_urandom(buffer, buffer_size, saved_errno);
+ }
+ collected += count;
+ }
+
+ errno = saved_errno;
+ return 0;
+}
diff --git a/ap/app/clatd/icmp.c b/ap/app/clatd/icmp.c
new file mode 100755
index 0000000..936958c
--- /dev/null
+++ b/ap/app/clatd/icmp.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
+ */
+
+#include <netinet/in.h>
+//#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <linux/icmp.h>
+
+#include "logging.h"
+#include "icmp.h"
+
+#include "my_header.h"
+
+/* function: icmp_guess_ttl
+ * Guesses the number of hops a received packet has traversed based on its TTL.
+ * ttl - the ttl of the received packet.
+ */
+uint8_t icmp_guess_ttl(uint8_t ttl) {
+ if (ttl > 128) {
+ return 255 - ttl;
+ } else if (ttl > 64) {
+ return 128 - ttl;
+ } else if (ttl > 32) {
+ return 64 - ttl;
+ } else {
+ return 32 - ttl;
+ }
+}
+
+/* function: is_icmp_error
+ * 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;
+}
+
+/* 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;
+}
+
+/* function: icmp_to_icmp6_type
+ * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
+ * type - the ICMPv6 type
+ */
+uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP_ECHO:
+ return ICMP6_ECHO_REQUEST;
+
+ case ICMP_ECHOREPLY:
+ return ICMP6_ECHO_REPLY;
+
+ case ICMP_TIME_EXCEEDED:
+ return ICMP6_TIME_EXCEEDED;
+
+ case ICMP_DEST_UNREACH:
+ // These two types need special translation which we don't support yet.
+ if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) {
+ return ICMP6_DST_UNREACH;
+ }
+ }
+
+ // 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);
+ return ICMP6_PARAM_PROB;
+}
+
+/* function: icmp_to_icmp6_code
+ * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
+ * type - the ICMP type
+ * code - the ICMP code
+ */
+uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP_ECHO:
+ case ICMP_ECHOREPLY:
+ return 0;
+
+ case ICMP_TIME_EXCEEDED:
+ return code;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_UNREACH_NET:
+ case ICMP_UNREACH_HOST:
+ return ICMP6_DST_UNREACH_NOROUTE;
+
+ case ICMP_UNREACH_PORT:
+ return ICMP6_DST_UNREACH_NOPORT;
+
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_HOST_PROHIB:
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ return ICMP6_DST_UNREACH_ADMIN;
+
+ // 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);
+ return 0;
+}
+
+/* function: icmp6_to_icmp_type
+ * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
+ * type - the ICMP type
+ */
+uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP6_ECHO_REQUEST:
+ return ICMP_ECHO;
+
+ case ICMP6_ECHO_REPLY:
+ return ICMP_ECHOREPLY;
+
+ case ICMP6_DST_UNREACH:
+ return ICMP_DEST_UNREACH;
+
+ case ICMP6_TIME_EXCEEDED:
+ return ICMP_TIME_EXCEEDED;
+ }
+
+ // 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);
+ return ICMP_PARAMETERPROB;
+}
+
+/* function: icmp6_to_icmp_code
+ * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
+ * type - the ICMPv6 type
+ * code - the ICMPv6 code
+ */
+uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
+ switch (type) {
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY:
+ case ICMP6_TIME_EXCEEDED:
+ return code;
+
+ case ICMP6_DST_UNREACH:
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ return ICMP_UNREACH_HOST;
+
+ case ICMP6_DST_UNREACH_ADMIN:
+ return ICMP_UNREACH_HOST_PROHIB;
+
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ return ICMP_UNREACH_HOST;
+
+ case ICMP6_DST_UNREACH_ADDR:
+ return ICMP_HOST_UNREACH;
+
+ case ICMP6_DST_UNREACH_NOPORT:
+ return ICMP_UNREACH_PORT;
+
+ // 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);
+ return 0;
+}
diff --git a/ap/app/clatd/icmp.h b/ap/app/clatd/icmp.h
new file mode 100755
index 0000000..632e92d
--- /dev/null
+++ b/ap/app/clatd/icmp.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
+ */
+
+#ifndef __ICMP_H__
+#define __ICMP_H__
+
+#include <stdint.h>
+
+// Guesses the number of hops a received packet has traversed based on its TTL.
+uint8_t icmp_guess_ttl(uint8_t ttl);
+
+// Determines whether an ICMP type is an error message.
+int is_icmp_error(uint8_t type);
+
+// Determines whether an ICMPv6 type is an error message.
+int is_icmp6_error(uint8_t type);
+
+// Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
+uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code);
+
+// Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
+uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code);
+
+// Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
+uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code);
+
+// Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
+uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code);
+
+#endif /* __ICMP_H__ */
diff --git a/ap/app/clatd/ifc.h b/ap/app/clatd/ifc.h
new file mode 100755
index 0000000..3b27234
--- /dev/null
+++ b/ap/app/clatd/ifc.h
@@ -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/ap/app/clatd/ifc_utils.c b/ap/app/clatd/ifc_utils.c
new file mode 100755
index 0000000..02304e5
--- /dev/null
+++ b/ap/app/clatd/ifc_utils.c
@@ -0,0 +1,759 @@
+/*
+ * 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 <stdarg.h>
+#include <sys/ioctl.h>
+
+#ifdef ANDROID
+#define LOG_TAG "NetUtils"
+#include <cutils/properties.h>
+#include <log/log.h>
+#else
+#define ALOGD printf
+#define ALOGW printf
+#endif
+
+#include "ifc.h"
+
+/* Deprecated. In Android O and above, there's no limit on property name length. */
+#define PROP_NAME_MAX 32
+
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+
+int property_set(const char *key, const char *value) {
+ return 0;
+}
+
+void printerr(char *fmt, ...)
+{
+ va_list ap;
+ char tmp_buf[256] = {0};
+ va_start(ap, fmt);
+ vsnprintf(tmp_buf, sizeof(tmp_buf), fmt, ap);
+ va_end(ap);
+
+ printf("%s\n", tmp_buf);
+}
+
+#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;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_MUTEX_INITIALIZER;
+void printerr(char *fmt, ...);
+
+#define DBG 0
+#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) {
+ printerr("socket() failed: %s\n", strerror(errno));
+ }
+ }
+
+ ret = ifc_ctl_sock < 0 ? -1 : 0;
+ if (DBG) printerr("ifc_init_returning %d", ret);
+ 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) {
+ printerr("socket() failed: %s\n", strerror(errno));
+ }
+ }
+ return ifc_ctl_sock6 < 0 ? -1 : 0;
+}
+
+void ifc_close(void)
+{
+ if (DBG) printerr("ifc_close");
+ 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 (DBG) printerr("ifc_up(%s) = %d", name, ret);
+ return ret;
+}
+
+int ifc_down(const char *name)
+{
+ int ret = ifc_set_flags(name, 0, IFF_UP);
+ if (DBG) printerr("ifc_down(%s) = %d", name, ret);
+ 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 (DBG) printerr("ifc_set_addr(%s, xx) = %d", name, ret);
+ 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) {
+ printf("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 (DBG) printerr("ifc_set_mask(%s, xx) = %d", name, ret);
+ 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 (DBG) printerr("ifc_create_default_route(%s, %d) = %d", name, gw, ret);
+ 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) {
+ ALOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
+ }
+ 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) {
+
+ char dns_prop_name[PROPERTY_KEY_MAX];
+
+ ifc_init();
+
+ if (ifc_up(ifname)) {
+ printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_addr(ifname, address)) {
+ printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_prefixLength(ifname, prefixLength)) {
+ printerr("failed to set prefixLength %d: %s\n", prefixLength, strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_create_default_route(ifname, gateway)) {
+ printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+
+ ifc_close();
+
+ 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) : "");
+
+ return 0;
+}
diff --git a/ap/app/clatd/ipv4.c b/ap/app/clatd/ipv4.c
new file mode 100755
index 0000000..406cc42
--- /dev/null
+++ b/ap/app/clatd/ipv4.c
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ *
+ * ipv4.c - takes ipv4 packets, finds their headers, and then calls translation functions on them
+ */
+#include <string.h>
+
+#include "translate.h"
+#include "checksum.h"
+#include "logging.h"
+#include "debug.h"
+#include "dump.h"
+
+/* function: icmp_packet
+ * translates an icmp packet
+ * out - output packet
+ * icmp - pointer to icmp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, size_t len) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct icmphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const uint8_t *) (icmp + 1);
+ payload_size = len - sizeof(struct icmphdr);
+
+ return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size);
+}
+
+/* function: ipv4_packet
+ * translates an ipv4 packet
+ * out - output packet
+ * packet - packet data
+ * len - size of packet
+ * 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, struct clat_icmp_frag *icmp_frag) {
+ 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;
+ const uint8_t *next_header;
+ size_t len_left;
+ 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");
+ return 0;
+ }
+
+ if(header->ihl < 5) {
+ logmsg_dbg(ANDROID_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);
+ return 0;
+ }
+
+ if(header->version != 4) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version);
+ return 0;
+ }
+
+ /* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be
+ * ignored and the packet translated normally; there is no attempt to
+ * translate the options.
+ */
+
+ next_header = packet + header->ihl*4;
+ len_left = len - header->ihl * 4;
+
+ nxthdr = header->protocol;
+ if (nxthdr == IPPROTO_ICMP) {
+ // ICMP and ICMPv6 have different protocol numbers.
+ nxthdr = IPPROTO_ICMPV6;
+ }
+
+ /* Fill in the IPv6 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip6_header(ip6_targ, 0, nxthdr, header);
+ out[pos].iov_len = sizeof(struct ip6_hdr);
+
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+ old_sum = ipv4_pseudo_header_checksum(header, len_left);
+ 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);
+ out[pos + 1].iov_len = frag_hdr_len;
+
+ if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) {
+ uint16_t frag_len = ((ntohs(header->frag_off))&IP_OFFMASK)<< 3;
+ if(icmp_frag && frag_len > 0 && ((ntohs(header->frag_off)) & IP_MF) == 0 && (nxthdr == IPPROTO_ICMPV6)){
+ icmp_frag->ip_len = len_left + frag_len;
+ icmp_frag->ip_id = header->id;
+ icmp_frag->flag = 2;
+ }
+ // 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) {
+ if(icmp_frag && ((ntohs(header->frag_off)) & (IP_MF|IP_OFFMASK)) == IP_MF){
+ icmp_frag->ip_len = len_left;
+ icmp_frag->ip_id = header->id;
+ icmp_frag->flag = 1;
+ }
+ 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);
+ } else if (nxthdr == IPPROTO_UDP) {
+ 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);
+ logcat_hexdump("ipv4/protocol", packet, len);
+#endif
+ return 0;
+ }
+
+ // Set the length.
+ ip6_targ->ip6_plen = htons(packet_length(out, pos));
+ return iov_len;
+}
diff --git a/ap/app/clatd/ipv6.c b/ap/app/clatd/ipv6.c
new file mode 100755
index 0000000..69063c4
--- /dev/null
+++ b/ap/app/clatd/ipv6.c
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ *
+ * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them
+ */
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include "translate.h"
+#include "checksum.h"
+#include "logging.h"
+#include "dump.h"
+#include "config.h"
+#include "debug.h"
+
+/* function: icmp6_packet
+ * takes an icmp6 packet and sets it up for translation
+ * out - output packet
+ * icmp6 - pointer to icmp6 header in packet
+ * checksum - pseudo-header checksum (unused)
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ uint32_t checksum, size_t len) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct icmp6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const uint8_t *) (icmp6 + 1);
+ payload_size = len - sizeof(struct icmp6_hdr);
+
+ return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size);
+}
+
+/* function: log_bad_address
+ * logs a bad address to android's log buffer if debugging is turned on
+ * fmt - printf-style format, use %s to place the address
+ * badaddr - the bad address in question
+ */
+#if CLAT_DEBUG
+void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
+ char srcstr[INET6_ADDRSTRLEN];
+ char dststr[INET6_ADDRSTRLEN];
+
+ inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
+ inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
+ logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
+}
+#else
+#define log_bad_address(fmt, src, dst)
+#endif
+
+/* function: ipv6_packet
+ * takes an ipv6 packet and hands it off to the layer 4 protocol function
+ * out - output packet
+ * packet - packet data
+ * len - size of packet
+ * 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, struct clat_icmp_frag *icmp_frag) {
+ 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;
+ size_t len_left;
+ 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);
+ return 0;
+ }
+
+ 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
+ }
+
+ // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless
+ // it's an ICMP packet (which can come from anywhere). We do not send IPv6 packets from the plat
+ // subnet to the local subnet, but these can appear as inner packets in ICMP errors, so we need
+ // to translate them. We accept third-party ICMPv6 errors, even though their source addresses
+ // cannot be translated, so that things like unreachables and traceroute will work. fill_ip_header
+ // takes care of faking a source address for them.
+ if (!(is_in_plat_subnet(&ip6->ip6_src) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) &&
+ !(is_in_plat_subnet(&ip6->ip6_dst) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet)) &&
+ ip6->ip6_nxt != IPPROTO_ICMPV6) {
+ log_bad_address("ipv6_packet/wrong source address: %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
+ return 0;
+ }
+
+ next_header = packet + sizeof(struct ip6_hdr);
+ len_left = len - sizeof(struct ip6_hdr);
+
+ protocol = ip6->ip6_nxt;
+
+ /* Fill in the IPv4 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip_header(ip_targ, 0, protocol, ip6);
+ out[pos].iov_len = sizeof(struct iphdr);
+
+ // 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;
+ if (len_left < sizeof(*frag_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
+ return 0;
+ }
+
+ next_header += sizeof(*frag_hdr);
+ len_left -= sizeof(*frag_hdr);
+
+ protocol = parse_frag_header(frag_hdr, ip_targ);
+ }
+ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol);
+ // ICMP and ICMPv6 have different protocol numbers.
+ if (protocol == IPPROTO_ICMPV6) {
+ protocol = IPPROTO_ICMP;
+ ip_targ->protocol = IPPROTO_ICMP;
+ }
+
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+
+ new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left);
+
+ // Does not support IPv6 extension headers except Fragment.
+ if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) {
+ uint16_t frag_len = ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK);
+ if(icmp_frag && frag_len > 0 && (protocol == IPPROTO_ICMP)){
+ icmp_frag->ip_len = len_left + frag_len;
+ icmp_frag->ip_id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+ icmp_frag->flag = 2;
+ }
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else if (protocol == IPPROTO_ICMP) {
+ if(icmp_frag && frag_hdr){
+ icmp_frag->ip_len = len_left;
+ icmp_frag->ip_id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+ icmp_frag->flag = 1;
+ }
+ iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, old_sum, 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);
+ } else if (protocol == IPPROTO_UDP) {
+ 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);
+ logcat_hexdump("ipv6/nxthdr", packet, len);
+#endif
+ return 0;
+ }
+
+ // 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));
+ return iov_len;
+}
diff --git a/ap/app/clatd/load_file.c b/ap/app/clatd/load_file.c
new file mode 100755
index 0000000..be84dec
--- /dev/null
+++ b/ap/app/clatd/load_file.c
@@ -0,0 +1,53 @@
+/* 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 <cutils/misc.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.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 0;
+}
diff --git a/ap/app/clatd/logging.c b/ap/app/clatd/logging.c
new file mode 100755
index 0000000..53b2559
--- /dev/null
+++ b/ap/app/clatd/logging.c
@@ -0,0 +1,60 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.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;
+ char tmp_buf[256] = {0};
+ va_start(ap, fmt);
+ //__android_log_vprint(prio, "clatd", fmt, ap);
+ vsnprintf(tmp_buf, sizeof(tmp_buf), fmt, ap);
+ printf("%s\n", tmp_buf);
+ 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;
+ char tmp_buf[256] = {0};
+ va_start(ap, fmt);
+ //__android_log_vprint(prio, "clatd", fmt, ap);
+ vsnprintf(tmp_buf, sizeof(tmp_buf), fmt, ap);
+ printf("%s\n", tmp_buf);
+ va_end(ap);
+}
+#else
+void logmsg_dbg(__attribute__((unused)) int prio, __attribute__((unused)) const char *fmt, ...) {}
+#endif
diff --git a/ap/app/clatd/logging.h b/ap/app/clatd/logging.h
new file mode 100755
index 0000000..0867e31
--- /dev/null
+++ b/ap/app/clatd/logging.h
@@ -0,0 +1,51 @@
+/*
+ * 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.h - print a log message
+ */
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+// for the priorities
+//#include <android/log.h>
+
+/**
+ * Android log priority values, in increasing order of priority.
+ */
+typedef enum android_LogPriority {
+ /** For internal use only. */
+ ANDROID_LOG_UNKNOWN = 0,
+ /** The default priority, for internal use only. */
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ /** Verbose logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_VERBOSE,
+ /** Debug logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_DEBUG,
+ /** Informational logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_INFO,
+ /** Warning logging. For use with recoverable failures. */
+ ANDROID_LOG_WARN,
+ /** Error logging. For use with unrecoverable failures. */
+ ANDROID_LOG_ERROR,
+ /** Fatal logging. For use when aborting. */
+ ANDROID_LOG_FATAL,
+ /** For internal use only. */
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+void logmsg(int prio, const char *fmt, ...);
+void logmsg_dbg(int prio, const char *fmt, ...);
+
+#endif
diff --git a/ap/app/clatd/mtu.c b/ap/app/clatd/mtu.c
new file mode 100755
index 0000000..975bf0e
--- /dev/null
+++ b/ap/app/clatd/mtu.c
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ * mtu.c - get interface mtu
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include "mtu.h"
+
+/* function: getifmtu
+ * returns the interface mtu or -1 on failure
+ * ifname - interface name
+ */
+int getifmtu(const char *ifname) {
+ int fd;
+ struct ifreq if_mtu;
+
+ fd = socket(AF_INET, SOCK_STREAM, 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) {
+ return -1;
+ }
+ return if_mtu.ifr_mtu;
+}
diff --git a/ap/app/clatd/mtu.h b/ap/app/clatd/mtu.h
new file mode 100755
index 0000000..c330c24
--- /dev/null
+++ b/ap/app/clatd/mtu.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ *
+ * mtu.c - get interface mtu
+ */
+
+#ifndef __MTU_H__
+#define __MTU_H__
+
+int getifmtu(const char *ifname);
+
+#endif
diff --git a/ap/app/clatd/my_header.h b/ap/app/clatd/my_header.h
new file mode 100755
index 0000000..8835b23
--- /dev/null
+++ b/ap/app/clatd/my_header.h
@@ -0,0 +1,48 @@
+#ifndef _MY_HEADER_H_
+#define _MY_HEADER_H_
+
+/*
+ * Passing NETID_UNSET as the netId causes system/netd/server/DnsProxyListener.cpp to
+ * fill in the appropriate default netId for the query.
+ */
+#define NETID_UNSET 0u
+
+/*
+ * MARK_UNSET represents the default (i.e. unset) value for a socket mark.
+ */
+#define MARK_UNSET 0u
+
+
+/*
+ * Definition of type and code field values.
+ * http://www.iana.org/assignments/icmp-parameters
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_NET_PROHIB 9 /* for crypto devs */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_FILTER_PROHIB 13 /* prohibited access */
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* precedence cutoff */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_ECHO 8 /* echo service */
+#define NETID_USE_LOCAL_NAMESERVERS 0x80000000
+
+#if 0
+struct ucred {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+};
+#endif
+
+#define AID_VPN 1016 /* vpn system */
+#define AID_CLAT 1029 /* clat part of nat464 */
+#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
+
+#endif
+
diff --git a/ap/app/clatd/netlink_callbacks.c b/ap/app/clatd/netlink_callbacks.c
new file mode 100755
index 0000000..a79aa76
--- /dev/null
+++ b/ap/app/clatd/netlink_callbacks.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * netlink_callbacks.c - generic callbacks for netlink responses
+ */
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+/* function: ack_handler
+ * generic netlink callback for ack messages
+ * msg - netlink message
+ * data - pointer to an int, stores the success code
+ */
+static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) {
+ int *retval = data;
+ *retval = 0;
+ return NL_OK;
+}
+
+/* function: error_handler
+ * generic netlink callback for error messages
+ * nla - error source
+ * 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) {
+ int *retval = arg;
+ if(err->error < 0) {
+ *retval = err->error;
+ } else {
+ *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
+ */
+struct nl_cb *alloc_ack_callbacks(int *retval) {
+ struct nl_cb *callbacks;
+
+ callbacks = nl_cb_alloc(NL_CB_DEFAULT);
+ if(!callbacks) {
+ return NULL;
+ }
+ nl_cb_set(callbacks, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, retval);
+ nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, retval);
+ return callbacks;
+}
diff --git a/ap/app/clatd/netlink_callbacks.h b/ap/app/clatd/netlink_callbacks.h
new file mode 100755
index 0000000..298ad3e
--- /dev/null
+++ b/ap/app/clatd/netlink_callbacks.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * netlink_callbacks.h - callbacks for netlink responses
+ */
+
+#ifndef __NETLINK_CALLBACKS_H__
+#define __NETLINK_CALLBACKS_H__
+
+struct nl_cb *alloc_ack_callbacks(int *retval);
+
+#endif
diff --git a/ap/app/clatd/netlink_msg.c b/ap/app/clatd/netlink_msg.c
new file mode 100755
index 0000000..26a8208
--- /dev/null
+++ b/ap/app/clatd/netlink_msg.c
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ *
+ * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink
+ */
+#define _GNU_SOURCE
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <errno.h>
+
+//#include <netlink-private/object-api.h>
+//#include <netlink-private/types.h>
+#include "my_header.h"
+#include "netlink-private/object-api.h"
+#include "netlink-private/types.h"
+
+#include <netlink/socket.h>
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+
+#include "netlink_msg.h"
+#include "netlink_callbacks.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) {
+ return sizeof(struct in_addr);
+ } else if(family == AF_INET6) {
+ return sizeof(struct in6_addr);
+ } else {
+ return 0;
+ }
+}
+
+/* function: nlmsg_alloc_generic
+ * allocates a netlink message with the given struct inside of it. returns NULL on failure
+ * type - netlink message type
+ * flags - netlink message flags
+ * 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 *msg;
+
+ msg = nlmsg_alloc();
+ if(!msg) {
+ return NULL;
+ }
+
+ if ((sizeof(struct nl_msg) + payload_len) > msg->nm_size) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len);
+ msg->nm_nlh->nlmsg_flags = flags;
+ msg->nm_nlh->nlmsg_type = type;
+
+ memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len);
+
+ return msg;
+}
+
+/* function: nlmsg_alloc_ifaddr
+ * allocates a netlink message with a struct ifaddrmsg inside of it. returns NULL on failure
+ * type - netlink message type
+ * flags - netlink message flags
+ * ifa - ifaddrmsg to copy into the new netlink message
+ */
+struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa) {
+ return nlmsg_alloc_generic(type, flags, ifa, sizeof(*ifa));
+}
+
+/* function: nlmsg_alloc_ifinfo
+ * allocates a netlink message with a struct ifinfomsg inside of it. returns NULL on failure
+ * type - netlink message type
+ * flags - netlink message flags
+ * ifi - ifinfomsg to copy into the new netlink message
+ */
+struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi) {
+ return nlmsg_alloc_generic(type, flags, ifi, sizeof(*ifi));
+}
+
+/* function: nlmsg_alloc_rtmsg
+ * allocates a netlink message with a struct rtmsg inside of it. returns NULL on failure
+ * type - netlink message type
+ * flags - netlink message flags
+ * rt - rtmsg to copy into the new netlink message
+ */
+struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt) {
+ return nlmsg_alloc_generic(type, flags, rt, sizeof(*rt));
+}
+
+/* function: netlink_set_kernel_only
+ * sets a socket to receive messages only from the kernel
+ * sock - socket to connect
+ */
+int netlink_set_kernel_only(struct nl_sock *nl_sk) {
+ struct sockaddr_nl addr = { AF_NETLINK, 0, 0, 0 };
+
+ if (!nl_sk) {
+ return -EFAULT;
+ }
+
+ int sockfd = nl_socket_get_fd(nl_sk);
+ return connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
+}
+
+/* function: send_netlink_msg
+ * sends a netlink message, reads a response, and hands the response(s) to the callbacks
+ * msg - netlink message to send
+ * callbacks - callbacks to use on responses
+ */
+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_connect(nl_sk, NETLINK_ROUTE) != 0)
+ goto cleanup;
+
+ if(nl_send_auto_complete(nl_sk, msg) < 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);
+}
+
+/* function: send_ifaddrmsg
+ * sends a netlink/ifaddrmsg message and hands the responses to the callbacks
+ * type - netlink message type
+ * flags - netlink message flags
+ * ifa - ifaddrmsg to send
+ * callbacks - callbacks to use with the responses
+ */
+void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) {
+ struct nl_msg *msg = NULL;
+
+ msg = nlmsg_alloc_ifaddr(type, flags, ifa);
+ if(!msg)
+ return;
+
+ send_netlink_msg(msg, callbacks);
+
+ nlmsg_free(msg);
+}
+
+/* function: netlink_sendrecv
+ * send a nl_msg and return an int status - only supports OK/ERROR responses
+ * msg - msg to send
+ */
+int netlink_sendrecv(struct nl_msg *msg) {
+ struct nl_cb *callbacks = NULL;
+ int retval = -EIO;
+
+ callbacks = alloc_ack_callbacks(&retval);
+ if(!callbacks) {
+ return -ENOMEM;
+ }
+
+ send_netlink_msg(msg, callbacks);
+
+ nl_cb_put(callbacks);
+
+ return retval;
+}
diff --git a/ap/app/clatd/netlink_msg.h b/ap/app/clatd/netlink_msg.h
new file mode 100755
index 0000000..13e1f28
--- /dev/null
+++ b/ap/app/clatd/netlink_msg.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ * netlink_msg.h - send an ifaddrmsg/ifinfomsg via netlink
+ */
+#ifndef __NETLINK_IFMSG_H__
+#define __NETLINK_IFMSG_H__
+
+size_t inet_family_size(int family);
+struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa);
+struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi);
+struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt);
+void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks);
+void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks);
+int netlink_sendrecv(struct nl_msg *msg);
+int netlink_set_kernel_only(struct nl_sock *nl_sk);
+
+#endif
diff --git a/ap/app/clatd/ring.c b/ap/app/clatd/ring.c
new file mode 100755
index 0000000..5e99fd5
--- /dev/null
+++ b/ap/app/clatd/ring.c
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ * 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 <linux/if.h>
+#include <linux/if_packet.h>
+
+#include "logging.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));
+ if (packetsock < 0) {
+ logmsg(ANDROID_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));
+ 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));
+ }
+
+ struct packet_ring *ring = &tunnel->ring;
+ 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.
+ };
+
+ if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) {
+ logmsg(ANDROID_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,
+ packetsock, 0);
+ if (ring->base == MAP_FAILED) {
+ logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno));
+ return -1;
+ }
+
+ ring->block = 0;
+ ring->slot = 0;
+ ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE;
+ 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);
+
+ return packetsock;
+}
+
+/* function: ring_advance
+ * advances to the next position in the packet ring
+ * ring - packet ring buffer
+ */
+static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) {
+ uint8_t *next = (uint8_t *) ring->next;
+
+ ring->slot++;
+ next += TP_FRAME_SIZE;
+
+ if (ring->slot == ring->numslots) {
+ ring->slot = 0;
+ ring->block++;
+
+ if (ring->block < ring->numblocks) {
+ next += TP_FRAME_GAP;
+ } else {
+ ring->block = 0;
+ next = (uint8_t *) ring->base;
+ }
+ }
+
+ ring->next = (struct tpacket2_hdr *) next;
+ return ring->next;
+}
+
+/* function: ring_read
+ * reads a packet from the ring buffer 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 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;
+ translate_packet(write_fd, to_ipv6, packet, tp->tp_len);
+ tp->tp_status = TP_STATUS_KERNEL;
+ tp = ring_advance(ring);
+ }
+}
diff --git a/ap/app/clatd/ring.h b/ap/app/clatd/ring.h
new file mode 100755
index 0000000..03ae8c1
--- /dev/null
+++ b/ap/app/clatd/ring.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * ring.c - packet ring buffer functions
+ */
+#ifndef __RING_H__
+#define __RING_H__
+
+#include <linux/if.h>
+#include <linux/if_packet.h>
+
+#include "clatd.h"
+
+struct tun_data;
+
+// Frame size. Must be a multiple of TPACKET_ALIGNMENT (=16)
+// Why the 16? http://lxr.free-electrons.com/source/net/packet/af_packet.c?v=3.4#L1764
+#define TP_FRAME_SIZE (TPACKET_ALIGN(MAXMTU) + TPACKET_ALIGN(TPACKET2_HDRLEN) + 16)
+
+// Block size. Must be a multiple of the page size, and a power of two for efficient memory use.
+#define TP_BLOCK_SIZE 8192
+
+// In order to save memory, our frames are not an exact divider of the block size. Therefore, the
+// mmaped region will have gaps corresponding to the empty space at the end of each block.
+#define TP_FRAMES (TP_BLOCK_SIZE / TP_FRAME_SIZE)
+#define TP_FRAME_GAP (TP_BLOCK_SIZE % TP_FRAME_SIZE)
+
+// TODO: Make this configurable. This requires some refactoring because the packet socket is
+// opened before we drop privileges, but the configuration file is read after. A value of 16
+// results in 656 frames (1048576 bytes).
+#define TP_NUM_BLOCKS 16
+
+struct packet_ring {
+ uint8_t *base;
+ struct tpacket2_hdr *next;
+ int slot, numslots;
+ int block, numblocks;
+};
+
+int ring_create(struct tun_data *tunnel);
+void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6);
+
+#endif
diff --git a/ap/app/clatd/setif.c b/ap/app/clatd/setif.c
new file mode 100755
index 0000000..07f5bac
--- /dev/null
+++ b/ap/app/clatd/setif.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * setif.c - network interface configuration
+ */
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include "logging.h"
+#include "netlink_msg.h"
+
+#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
+ * ifname - name of interface to change
+ * family - address family (AF_INET, AF_INET6)
+ * address - pointer to a struct in_addr or in6_addr
+ * 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 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_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) {
+ 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) {
+ // AF_INET gets IFA_LOCAL + IFA_BROADCAST
+ if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ } else {
+ retval = -EAFNOSUPPORT;
+ goto cleanup;
+ }
+
+ retval = netlink_sendrecv(msg);
+
+cleanup:
+ if(msg)
+ nlmsg_free(msg);
+
+ return retval;
+}
+
+/* function: if_up
+ * sets interface link state to up and sets mtu, returns 0 on success and <0 on failure
+ * ifname - interface name to change
+ * mtu - new mtu
+ */
+int if_up(const char *ifname, int mtu) {
+ 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;
+ }
+
+ if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ retval = netlink_sendrecv(msg);
+
+cleanup:
+ if(msg)
+ nlmsg_free(msg);
+
+ return retval;
+}
+
+static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) {
+ struct ipv6_mreq mreq = { *addr, ifindex };
+ char *optname;
+ int ret;
+
+ switch (what) {
+ DEBUG_OPTNAME(IPV6_JOIN_ANYCAST)
+ DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST)
+ default:
+ optname = "???";
+ break;
+ }
+
+ ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq));
+ if (ret) {
+ logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno));
+ }
+
+ return ret;
+}
+
+/* function: add_anycast_address
+ * adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure
+ * sock - the socket to add the address to
+ * addr - the IP address to add
+ * ifname - name of interface to add the address to
+ */
+int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) {
+ int ifindex;
+
+ ifindex = if_nametoindex(ifname);
+ if (!ifindex) {
+ logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname);
+ return -ENODEV;
+ }
+
+ return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex);
+}
+
+/* function: del_anycast_address
+ * removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure
+ * sock - the socket to remove from, must have had the address added via add_anycast_address
+ * addr - the IP address to remove
+ */
+int del_anycast_address(int sock, struct in6_addr *addr) {
+ return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0);
+}
diff --git a/ap/app/clatd/setif.h b/ap/app/clatd/setif.h
new file mode 100755
index 0000000..d31eed5
--- /dev/null
+++ b/ap/app/clatd/setif.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ *
+ * setif.h - network interface configuration
+ */
+#ifndef __SETIF_H__
+#define __SETIF_H__
+
+int add_address(const char *ifname, int family, const void *address, int cidr, const void *broadcast);
+int if_up(const char *ifname, int mtu);
+
+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);
+
+#endif
diff --git a/ap/app/clatd/translate.c b/ap/app/clatd/translate.c
new file mode 100755
index 0000000..1541cea
--- /dev/null
+++ b/ap/app/clatd/translate.c
@@ -0,0 +1,637 @@
+/*
+ * 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.
+ *
+ * translate.c - CLAT functions / partial implementation of rfc6145
+ */
+#include <string.h>
+
+#include "icmp.h"
+#include "translate.h"
+#include "checksum.h"
+#include "clatd.h"
+#include "config.h"
+#include "logging.h"
+#include "debug.h"
+#include "tun.h"
+
+#define CLAT_ICMP_FRAG_MEM_MAX 10
+#define CLAT_ICMP_FRAG_TIMEOUT 300
+
+struct clat_icmp_frag g_clat_icmp_frag[CLAT_ICMP_FRAG_MEM_MAX];
+
+static void clat_packet_copy(clat_packet in, clat_packet out)
+{
+ int i;
+ for(i = 0; i < CLAT_POS_MAX; i++){
+ if(in[i].iov_len > 0 && in[i].iov_base ){
+ out[i].iov_base = malloc(in[i].iov_len);
+ if(out[i].iov_base){
+ memcpy(out[i].iov_base, in[i].iov_base, in[i].iov_len);
+ out[i].iov_len = in[i].iov_len;
+ }else
+ logmsg_dbg(ANDROID_LOG_ERROR,"clat_packet_copy malloc fail");
+ }
+ }
+}
+
+static void clat_packet_free(clat_packet in)
+{
+ int i;
+ for(i = 0; i < CLAT_POS_MAX; i++){
+ if(in[i].iov_base)
+ free(in[i].iov_base);
+ }
+}
+
+static void clat_icmp_frag_add(struct clat_icmp_frag *in){
+ int i = 0;
+ for(i = 0; i < CLAT_ICMP_FRAG_MEM_MAX; i++){
+ if(g_clat_icmp_frag[i].flag != 0 &&
+ (in->time - g_clat_icmp_frag[i].time) > CLAT_ICMP_FRAG_TIMEOUT){
+ logmsg_dbg(ANDROID_LOG_ERROR,"clat_icmp_frag_add timeout %d",i);
+ clat_packet_free(g_clat_icmp_frag[i].data);
+ memset(&g_clat_icmp_frag[i], 0, sizeof(struct clat_icmp_frag));
+ }
+ if(g_clat_icmp_frag[i].flag == 0){
+ memcpy(&g_clat_icmp_frag[i], in, sizeof(struct clat_icmp_frag));
+ return 0;
+ }
+ }
+ logmsg_dbg(ANDROID_LOG_ERROR,"clat_icmp_frag_add noidle fail");
+}
+
+static int clat_icmp_frag_find(struct clat_icmp_frag *in){
+ int i = 0;
+ for(i = 0; i < CLAT_ICMP_FRAG_MEM_MAX; i++){
+ if(g_clat_icmp_frag[i].flag != 0 &&
+ g_clat_icmp_frag[i].ip_id == in->ip_id){
+ memcpy(in, &g_clat_icmp_frag[i], sizeof(struct clat_icmp_frag));
+ memset(&g_clat_icmp_frag[i], 0, sizeof(struct clat_icmp_frag));
+ return 0;
+ }
+ }
+ logmsg_dbg(ANDROID_LOG_ERROR,"clat_icmp_frag_find fail");
+ return -1;
+}
+
+/* function: packet_checksum
+ * calculates the checksum over all the packet components starting from pos
+ * checksum - checksum of packet components before pos
+ * packet - packet to calculate the checksum of
+ * pos - position to start counting from
+ * returns - the completed 16-bit checksum, ready to write into a checksum header field
+ */
+uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos) {
+ int i;
+ for (i = pos; i < CLAT_POS_MAX; i++) {
+ if (packet[i].iov_len > 0) {
+ checksum = ip_checksum_add(checksum, packet[i].iov_base, packet[i].iov_len);
+ }
+ }
+ return ip_checksum_finish(checksum);
+}
+
+/* function: packet_length
+ * returns the total length of all the packet components after pos
+ * packet - packet to calculate the length of
+ * pos - position to start counting after
+ * returns: the total length of the packet components after pos
+ */
+uint16_t packet_length(clat_packet packet, clat_packet_index pos) {
+ size_t len = 0;
+ int i;
+ for (i = pos + 1; i < CLAT_POS_MAX; i++) {
+ len += packet[i].iov_len;
+ }
+ return len;
+}
+
+/* function: is_in_plat_subnet
+ * returns true iff the given IPv6 address is in the plat subnet.
+ * addr - IPv6 address
+ */
+int is_in_plat_subnet(const struct in6_addr *addr6) {
+ // Assumes a /96 plat subnet.
+ return (addr6 != NULL) && (memcmp(addr6, &Global_Clatd_Config.plat_subnet, 12) == 0);
+}
+
+/* function: ipv6_addr_to_ipv4_addr
+ * return the corresponding ipv4 address for the given ipv6 address
+ * addr6 - ipv6 address
+ * returns: the IPv4 address
+ */
+uint32_t ipv6_addr_to_ipv4_addr(const struct in6_addr *addr6) {
+ if (is_in_plat_subnet(addr6)) {
+ // Assumes a /96 plat subnet.
+ return addr6->s6_addr32[3];
+ } else if (IN6_ARE_ADDR_EQUAL(addr6, &Global_Clatd_Config.ipv6_local_subnet)) {
+ // Special-case our own address.
+ return Global_Clatd_Config.ipv4_local_subnet.s_addr;
+ } else {
+ // Third party packet. Let the caller deal with it.
+ return INADDR_NONE;
+ }
+}
+
+/* function: ipv4_addr_to_ipv6_addr
+ * return the corresponding ipv6 address for the given ipv4 address
+ * addr4 - ipv4 address
+ */
+struct in6_addr ipv4_addr_to_ipv6_addr(uint32_t addr4) {
+ struct in6_addr addr6;
+ // Both addresses are in network byte order (addr4 comes from a network packet, and the config
+ // file entry is read using inet_ntop).
+ if (addr4 == Global_Clatd_Config.ipv4_local_subnet.s_addr) {
+ return Global_Clatd_Config.ipv6_local_subnet;
+ } else {
+ // Assumes a /96 plat subnet.
+ addr6 = Global_Clatd_Config.plat_subnet;
+ addr6.s6_addr32[3] = addr4;
+ return addr6;
+ }
+}
+
+/* function: fill_tun_header
+ * fill in the header for the tun fd
+ * tun_header - tunnel header, already allocated
+ * proto - ethernet protocol id: ETH_P_IP(ipv4) or ETH_P_IPV6(ipv6)
+ */
+void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) {
+ tun_header->flags = 0;
+ tun_header->proto = htons(proto);
+}
+
+/* function: fill_ip_header
+ * generate an ipv4 header from an ipv6 header
+ * ip_targ - (ipv4) target packet header, source: original ipv4 addr, dest: local subnet addr
+ * payload_len - length of other data inside packet
+ * protocol - protocol number (tcp, udp, etc)
+ * old_header - (ipv6) source packet header, source: nat64 prefix, dest: local subnet prefix
+ */
+void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header) {
+ 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->frag_off = htons(IP_DF);
+ ip->ttl = old_header->ip6_hlim;
+ ip->protocol = protocol;
+ 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);
+
+ // 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) {
+ ttl_guess = icmp_guess_ttl(old_header->ip6_hlim);
+ ip->saddr = htonl((0xff << 24) + ttl_guess);
+ }
+}
+
+/* function: fill_ip6_header
+ * generate an ipv6 header from an ipv4 header
+ * ip6 - (ipv6) target packet header, source: local subnet prefix, dest: nat64 prefix
+ * payload_len - length of other data inside packet
+ * protocol - protocol number (tcp, udp, etc)
+ * old_header - (ipv4) source packet header, source: local subnet addr, dest: internet's ipv4 addr
+ */
+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_plen = htons(payload_len);
+ ip6->ip6_nxt = protocol;
+ ip6->ip6_hlim = old_header->ttl;
+
+ ip6->ip6_src = ipv4_addr_to_ipv6_addr(old_header->saddr);
+ ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr);
+}
+
+/* function: maybe_fill_frag_header
+ * fills a fragmentation header
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - target (ipv6) fragmentation header
+ * ip6_targ - target (ipv6) header
+ * old_header - (ipv4) source packet header
+ * returns: the length of the fragmentation header if present, or zero if not present
+ */
+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;
+ 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_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);
+ if (frag_flags & IP_MF) {
+ frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG;
+ }
+ frag_hdr->ip6f_ident = htonl(ntohs(old_header->id));
+ ip6_targ->ip6_nxt = IPPROTO_FRAGMENT;
+
+ return sizeof(*frag_hdr);
+}
+
+/* function: parse_frag_header
+ * return the length of the fragmentation header if present, or zero if not present
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - (ipv6) fragmentation header
+ * ip_targ - target (ipv4) header
+ * returns: the next header value
+ */
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) {
+ uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3);
+ if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) {
+ frag_off |= IP_MF;
+ }
+ ip_targ->frag_off = htons(frag_off);
+ ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+ ip_targ->protocol = frag_hdr->ip6f_nxt;
+ return frag_hdr->ip6f_nxt;
+}
+
+extern uint32_t ip_checksum_add(uint32_t current, const void *data, int len);
+extern uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+/* function: icmp_to_icmp6
+ * translate ipv4 icmp to ipv6 icmp
+ * out - output packet
+ * icmp - source packet icmp header
+ * checksum - pseudo-header checksum
+ * payload - icmp payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size) {
+ struct icmp6_hdr *icmp6_targ = out[pos].iov_base;
+ uint8_t icmp6_type;
+ int clat_packet_len;
+
+ memset(icmp6_targ, 0, sizeof(struct icmp6_hdr));
+
+ 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) {
+ // 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, NULL);
+
+ // The pseudo-header checksum was calculated on the transport length of the original IPv4
+ // packet that we were asked to translate. This transport length is 20 bytes smaller than it
+ // needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
+ // an IPv6 header, which is 20 bytes longer. Fix it up here.
+ // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
+ // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
+ checksum = checksum + htons(20);
+ icmp6_targ->icmp6_cksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp6_targ->icmp6_cksum = packet_checksum(checksum, out, pos);
+ } 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;
+ if(icmp6_type == ICMP6_ECHO_REQUEST){
+ icmp6_targ->icmp6_cksum = ip_checksum_adjust(icmp->checksum, ICMP_ECHO, ip_checksum_add(checksum, &icmp6_type, 1));
+ } else {
+ icmp6_targ->icmp6_cksum = ip_checksum_adjust(icmp->checksum, ICMP_ECHOREPLY, ip_checksum_add(checksum, &icmp6_type, 1));
+ }
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+
+ return clat_packet_len;
+}
+
+/* function: icmp6_to_icmp
+ * translate ipv6 icmp to ipv4 icmp
+ * out - output packet
+ * icmp6 - source packet icmp6 header
+ * payload - icmp6 payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size) {
+ struct icmphdr *icmp_targ = out[pos].iov_base;
+ uint8_t icmp_type;
+ int clat_packet_len;
+
+ memset(icmp_targ, 0, sizeof(struct icmphdr));
+
+ 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) &&
+ 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, NULL);
+ icmp_targ->checksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp_targ->checksum = packet_checksum(0, out, pos);
+ } 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;
+ if(icmp_type == ICMP_ECHO){
+ uint8_t icmp6_type = ICMP6_ECHO_REQUEST;
+ icmp_targ->checksum = ip_checksum_adjust(icmp6->icmp6_cksum, ip_checksum_add(checksum, &icmp6_type, 1), ICMP_ECHO);
+ } else {
+ uint8_t icmp6_type = ICMP6_ECHO_REPLY;
+ icmp_targ->checksum = ip_checksum_adjust(icmp6->icmp6_cksum, ip_checksum_add(checksum, &icmp6_type, 1), ICMP_ECHOREPLY);
+ }
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+
+ return clat_packet_len;
+}
+
+/* function: generic_packet
+ * takes a generic IP packet and sets it up for translation
+ * out - output packet
+ * pos - position in the output packet of the transport header
+ * payload - pointer to IP payload
+ * len - size of ip payload
+ * 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;
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+/* function: udp_packet
+ * takes a udp packet and sets it up for translation
+ * out - output packet
+ * udp - pointer to udp header in packet
+ * old_sum - pseudo-header checksum of old header
+ * 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) {
+ const uint8_t *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct udphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
+ return 0;
+ }
+
+ 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);
+}
+
+/* function: tcp_packet
+ * takes a tcp packet and sets it up for translation
+ * out - output packet
+ * tcp - pointer to tcp header in packet
+ * checksum - pseudo-header checksum
+ * 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) {
+ const uint8_t *payload;
+ size_t payload_size, header_size;
+
+ if(len < sizeof(struct tcphdr)) {
+ logmsg_dbg(ANDROID_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);
+ 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);
+ return 0;
+ }
+
+ 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);
+}
+
+/* function: udp_translate
+ * common between ipv4/ipv6 - setup checksum and send udp packet
+ * out - output packet
+ * udp - udp header
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
+ * payload - tcp payload
+ * payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+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) {
+ struct udphdr *udp_targ = out[pos].iov_base;
+
+ 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;
+
+ if (udp_targ->check) {
+ udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
+ } else {
+ // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that
+ // the transmitter generated no checksum (for debugging or for higher level protocols that
+ // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So
+ // for safety we recompute it.
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(new_sum, out, pos);
+ }
+
+ // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent
+ // in one's complement arithmetic)."
+ if (!udp_targ->check) {
+ udp_targ->check = 0xffff;
+ }
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+/* function: tcp_translate
+ * common between ipv4/ipv6 - setup checksum and send tcp packet
+ * out - output packet
+ * tcp - tcp header
+ * header_size - size of tcp header including options
+ * checksum - partial checksum covering ipv4/ipv6 header
+ * payload - tcp payload
+ * payload_size - size of payload
+ * 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) {
+ struct tcphdr *tcp_targ = out[pos].iov_base;
+ 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);
+ 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;
+
+ tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
+// Weak symbol so we can override it in the unit test.
+void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak));
+
+void send_rawv6(int fd, clat_packet out, int iov_len) {
+ // A send on a raw socket requires a destination address to be specified even if the socket's
+ // protocol is IPPROTO_RAW. This is the address that will be used in routing lookups; the
+ // 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,
+ .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;
+ sendmsg(fd, &msg, 0);
+}
+
+/* function: translate_packet
+ * takes a packet, translates it, and writes it to fd
+ * fd - fd to write translated packet to
+ * to_ipv6 - true if translating to ipv6, false if translating to ipv4
+ * packet - packet
+ * packetsize - size of packet
+ */
+void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize) {
+ int iov_len = 0;
+
+ // Allocate buffers for all packet headers.
+ struct clat_icmp_frag icmp_frag = {0};
+ struct tun_pi tun_targ;
+ char iphdr[sizeof(struct ip6_hdr)];
+ char fraghdr[sizeof(struct ip6_frag)];
+ char transporthdr[MAX_TCP_HDR];
+ char icmp_iphdr[sizeof(struct ip6_hdr)];
+ char icmp_fraghdr[sizeof(struct ip6_frag)];
+ char icmp_transporthdr[MAX_TCP_HDR];
+
+ // 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.
+ };
+
+ if (to_ipv6) {
+ iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize, &icmp_frag);
+ if (iov_len > 0) {
+ if(icmp_frag.flag == 1){
+ clat_packet_copy(out, icmp_frag.data);
+ icmp_frag.iov_len = iov_len;
+ icmp_frag.time = time(NULL);
+ clat_icmp_frag_add(&icmp_frag);
+ return;
+ } else if(icmp_frag.flag == 2){
+ unsigned short total_len = icmp_frag.ip_len;
+ struct icmp6_hdr *icmp6_targ;
+ clat_icmp_frag_find(&icmp_frag);
+ icmp6_targ = icmp_frag.data[CLAT_POS_TRANSPORTHDR].iov_base;
+ icmp6_targ->icmp6_cksum = htons(ip_checksum_adjust(htons(icmp6_targ->icmp6_cksum), icmp_frag.ip_len, total_len));
+ send_rawv6(fd, icmp_frag.data, icmp_frag.iov_len);
+ clat_packet_free(icmp_frag.data);
+ }
+ send_rawv6(fd, out, iov_len);
+ }
+ } else {
+ iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize, &icmp_frag);
+ if (iov_len > 0) {
+ fill_tun_header(&tun_targ, ETH_P_IP);
+ out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ);
+ if(icmp_frag.flag == 1){
+ clat_packet_copy(out, icmp_frag.data);
+ icmp_frag.iov_len = iov_len;
+ icmp_frag.time = time(NULL);
+ clat_icmp_frag_add(&icmp_frag);
+ return;
+ } else if(icmp_frag.flag == 2){
+ unsigned short total_len = icmp_frag.ip_len;
+ struct icmphdr *icmp_targ;
+ clat_icmp_frag_find(&icmp_frag);
+ icmp_targ = icmp_frag.data[CLAT_POS_TRANSPORTHDR].iov_base;
+ icmp_targ->checksum = htons(ip_checksum_adjust(htons(icmp_targ->checksum), total_len , icmp_frag.ip_len));
+ send_tun(fd, icmp_frag.data, icmp_frag.iov_len);
+ clat_packet_free(icmp_frag.data);
+ }
+ send_tun(fd, out, iov_len);
+ }
+ }
+}
diff --git a/ap/app/clatd/translate.h b/ap/app/clatd/translate.h
new file mode 100755
index 0000000..e7ee19a
--- /dev/null
+++ b/ap/app/clatd/translate.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ * translate.h - translate from one version of ip to another
+ */
+#ifndef __TRANSLATE_H__
+#define __TRANSLATE_H__
+
+#include <netinet/in.h>
+#include <netinet/ip.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 "clatd.h"
+
+#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);
+
+// Returns the total length of the packet components after pos.
+uint16_t packet_length(clat_packet packet, clat_packet_index pos);
+
+// Returns true iff the given IPv6 address is in the plat subnet.
+int is_in_plat_subnet(const struct in6_addr *addr6);
+
+// Functions to create tun, IPv4, and IPv6 headers.
+void fill_tun_header(struct tun_pi *tun_header, uint16_t proto);
+void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header);
+void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
+ const struct iphdr *old_header);
+
+// Translate and send packets.
+void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize);
+
+// Translate IPv4 and IPv6 packets.
+int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len, struct clat_icmp_frag *icmp_frag);
+int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len, struct clat_icmp_frag *icmp_frag);
+
+// Deal with fragmented packets.
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header);
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ);
+
+// Deal with fragmented packets.
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header);
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ);
+
+// Translate ICMP packets.
+int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size);
+int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6,
+ uint32_t checksum, const uint8_t *payload, size_t payload_size);
+
+// Translate generic IP packets.
+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_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);
+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);
+
+#endif /* __TRANSLATE_H__ */
diff --git a/ap/app/clatd/tun.c b/ap/app/clatd/tun.c
new file mode 100755
index 0000000..7e62e44
--- /dev/null
+++ b/ap/app/clatd/tun.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ * tun.c - tun device functions
+ */
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+//#include <linux/if.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include "clatd.h"
+
+/* function: tun_open
+ * tries to open the tunnel device
+ */
+int tun_open() {
+ int fd;
+
+ fd = open("/dev/tun", O_RDWR);
+ if(fd < 0) {
+ fd = open("/dev/net/tun", O_RDWR);
+ }
+
+ return fd;
+}
+
+/* function: tun_alloc
+ * creates a tun interface and names it
+ * dev - the name for the new tun device
+ */
+int tun_alloc(char *dev, int fd) {
+ struct ifreq ifr;
+ int err;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_TUN;
+ if( *dev ) {
+ strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = '\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);
+}
+
+/* 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);
+}
diff --git a/ap/app/clatd/tun.h b/ap/app/clatd/tun.h
new file mode 100755
index 0000000..bcdd10e
--- /dev/null
+++ b/ap/app/clatd/tun.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ * tun.h - tun device functions
+ */
+#ifndef __TUN_H__
+#define __TUN_H__
+
+#include <linux/if.h>
+
+#include "clatd.h"
+#include "ring.h"
+
+struct tun_data {
+ char device4[IFNAMSIZ];
+ int read_fd6, write_fd6, fd4;
+ struct packet_ring ring;
+};
+
+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);
+
+#endif