summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRicardo Cerqueira <cyanogenmod@cerqueira.org>2013-07-24 21:14:00 +0100
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2013-07-24 21:14:00 +0100
commitd523ff99b0e04cbbfbfcb556eed9a56948fe56c4 (patch)
tree6da578352f0ec62fa7ac9e5eff63fdb03a1af516
parent62ea9b6658a4c2116eeb6450c8244269448e79aa (diff)
parent0d7837b9dddce300713bfcf2b84aecf2e360a502 (diff)
downloadandroid_external_android-clat-stable/cm-10.2.tar.gz
android_external_android-clat-stable/cm-10.2.tar.bz2
android_external_android-clat-stable/cm-10.2.zip
Merge tag 'android-4.3_r2.1' into cm-10.2cm-10.2.1cm-10.2.0cm-10.2-M1stable/cm-10.2cm-10.2
Android 4.3 release 2.1
-rw-r--r--Android.mk22
-rw-r--r--BUGS9
-rw-r--r--LICENSE201
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE189
-rw-r--r--checksum.c115
-rw-r--r--checksum.h28
-rw-r--r--clatd.c513
-rw-r--r--clatd.conf15
-rw-r--r--clatd.h33
-rw-r--r--config.c302
-rw-r--r--config.h42
-rw-r--r--debug.h24
-rw-r--r--dns64.c96
-rw-r--r--dns64.h23
-rw-r--r--dump.c240
-rw-r--r--dump.h34
-rw-r--r--getaddr.c137
-rw-r--r--getaddr.h28
-rw-r--r--getroute.c143
-rw-r--r--getroute.h35
-rw-r--r--icmp.c181
-rw-r--r--icmp.h45
-rw-r--r--ipv4.c144
-rw-r--r--ipv4.h25
-rw-r--r--ipv6.c161
-rw-r--r--ipv6.h25
-rw-r--r--logging.c53
-rw-r--r--logging.h27
-rw-r--r--mtu.c46
-rw-r--r--mtu.h24
-rw-r--r--netlink_callbacks.c66
-rw-r--r--netlink_callbacks.h24
-rw-r--r--netlink_msg.c186
-rw-r--r--netlink_msg.h30
-rw-r--r--setif.c129
-rw-r--r--setif.h24
-rw-r--r--setroute.c142
-rw-r--r--setroute.h27
-rw-r--r--translate.c399
-rw-r--r--translate.h65
41 files changed, 4052 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b0d19d9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,22 @@
+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 getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c
+
+LOCAL_C_INCLUDES := external/libnl-headers
+LOCAL_STATIC_LIBRARIES := libnl_2
+LOCAL_SHARED_LIBRARIES := libcutils liblog
+
+LOCAL_MODULE := clatd
+
+include $(BUILD_EXECUTABLE)
+
+
+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)
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..5494d6b
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,9 @@
+Optional:
+ - ipv6 traceroute to handle 464 transitions
+
+known problems/assumptions:
+ - does not handle ICMP types other than echo/echo reply
+ - does not handle protocols other than ICMP, UDP, and TCP
+ - assumes the handset has its own (routed) /64 ipv6 subnet
+ - assumes the /120 or /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/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/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/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..5943b54
--- /dev/null
+++ b/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/checksum.c b/checksum.c
new file mode 100644
index 0000000..a4dc9b8
--- /dev/null
+++ b/checksum.c
@@ -0,0 +1,115 @@
+/*
+ * 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 <linux/icmp.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_finish
+ * close the checksum
+ * temp_sum - sum from ip_checksum_add
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+ while(temp_sum > 0xffff)
+ temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+
+ temp_sum = (~temp_sum) & 0xffff;
+
+ return 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
+ * current - the current checksum or 0 to start a new checksum
+ * ip6 - the ipv6 header
+ * len - the transport length (transport header + payload)
+ */
+uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) {
+ uint32_t checksum_len, checksum_next;
+
+ checksum_len = htonl((uint32_t) len);
+ checksum_next = htonl(ip6->ip6_nxt);
+
+ 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
+ * current - the current checksum or 0 to start a new checksum
+ * ip - the ipv4 header
+ * len - the transport length (transport header + payload)
+ */
+uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) {
+ uint16_t temp_protocol, temp_length;
+
+ temp_protocol = htons(ip->protocol);
+ temp_length = htons(len);
+
+ 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;
+}
diff --git a/checksum.h b/checksum.h
new file mode 100644
index 0000000..473f5f5
--- /dev/null
+++ b/checksum.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.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_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(uint32_t current, const struct ip6_hdr *ip6, uint16_t len);
+uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/clatd.c b/clatd.c
new file mode 100644
index 0000000..063026d
--- /dev/null
+++ b/clatd.c
@@ -0,0 +1,513 @@
+/*
+ * 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/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.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 <sys/capability.h>
+#include <sys/uio.h>
+#include <linux/prctl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_ether.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "ipv4.h"
+#include "ipv6.h"
+#include "translate.h"
+#include "clatd.h"
+#include "config.h"
+#include "logging.h"
+#include "setif.h"
+#include "setroute.h"
+#include "mtu.h"
+#include "getaddr.h"
+#include "dump.h"
+
+#define DEVICENAME6 "clat"
+#define DEVICENAME4 "clat4"
+
+int forwarding_fd = -1;
+volatile sig_atomic_t running = 1;
+
+struct tun_data {
+ char device6[IFNAMSIZ], device4[IFNAMSIZ];
+ int fd6, fd4;
+};
+
+/* function: set_forwarding
+ * enables/disables ipv6 forwarding
+ */
+void set_forwarding(int fd, const char *setting) {
+ /* we have to forward packets from the WAN to the tun interface */
+ if(write(fd, setting, strlen(setting)) < 0) {
+ logmsg(ANDROID_LOG_FATAL,"set_forwarding(%s) failed: %s", setting, strerror(errno));
+ exit(1);
+ }
+}
+
+/* function: stop_loop
+ * signal handler: stop the event loop
+ */
+void stop_loop(int signal) {
+ running = 0;
+}
+
+/* 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: deconfigure_tun_ipv6
+ * removes the ipv6 route
+ * tunnel - tun device data
+ */
+void deconfigure_tun_ipv6(const struct tun_data *tunnel) {
+ int status;
+
+ status = if_route(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_subnet,
+ 128, NULL, 1, 0, ROUTE_DELETE);
+ if(status < 0) {
+ logmsg(ANDROID_LOG_WARN,"deconfigure_tun_ipv6/if_route(6) failed: %s",strerror(-status));
+ }
+}
+
+/* function: configure_tun_ipv6
+ * configures the ipv6 route
+ * note: routes a /128 out of the (assumed routed to us) /64 to the CLAT interface
+ * tunnel - tun device data
+ */
+void configure_tun_ipv6(const struct tun_data *tunnel) {
+ struct in6_addr local_nat64_prefix_6;
+ int status;
+
+ status = if_route(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_subnet,
+ 128, NULL, 1, 0, ROUTE_CREATE);
+ if(status < 0) {
+ logmsg(ANDROID_LOG_FATAL,"configure_tun_ipv6/if_route(6) failed: %s",strerror(-status));
+ exit(1);
+ }
+}
+
+/* function: interface_poll
+ * polls the uplink network interface for address changes
+ * tunnel - tun device data
+ */
+void interface_poll(const struct tun_data *tunnel) {
+ union anyip *interface_ip;
+
+ interface_ip = getinterface_ip(Global_Clatd_Config.default_pdp_interface, AF_INET6);
+ if(!interface_ip) {
+ logmsg(ANDROID_LOG_WARN,"unable to find an ipv6 ip on interface %s",Global_Clatd_Config.default_pdp_interface);
+ return;
+ }
+
+ config_generate_local_ipv6_subnet(&interface_ip->ip6);
+
+ if(!IN6_ARE_ADDR_EQUAL(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) {
+ char from_addr[INET6_ADDRSTRLEN], to_addr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, from_addr, sizeof(from_addr));
+ inet_ntop(AF_INET6, &interface_ip->ip6, to_addr, sizeof(to_addr));
+ logmsg(ANDROID_LOG_WARN, "clat subnet changed from %s to %s", from_addr, to_addr);
+
+ // remove old route
+ deconfigure_tun_ipv6(tunnel);
+
+ // add new route, start translating packets to the new prefix
+ memcpy(&Global_Clatd_Config.ipv6_local_subnet, &interface_ip->ip6, sizeof(struct in6_addr));
+ configure_tun_ipv6(tunnel);
+ }
+
+ free(interface_ip);
+}
+
+/* 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;
+
+ // 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);
+ }
+
+ if((status = if_up(tunnel->device6, Global_Clatd_Config.mtu)) < 0) {
+ logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(6) failed: %s",strerror(-status));
+ exit(1);
+ }
+
+ 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);
+ }
+
+ configure_tun_ipv6(tunnel);
+}
+
+/* function: drop_root
+ * drops root privs but keeps the needed capability
+ */
+void drop_root() {
+ gid_t groups[] = { AID_INET };
+ 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: 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
+ */
+void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel) {
+ int error;
+
+ if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix)) {
+ 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 - 20)) {
+ Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu-20;
+ logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu);
+ }
+
+ error = tun_alloc(tunnel->device6, tunnel->fd6);
+ if(error < 0) {
+ logmsg(ANDROID_LOG_FATAL,"tun_alloc failed: %s",strerror(errno));
+ exit(1);
+ }
+
+ error = tun_alloc(tunnel->device4, tunnel->fd4);
+ if(error < 0) {
+ logmsg(ANDROID_LOG_FATAL,"tun_alloc/4 failed: %s",strerror(errno));
+ exit(1);
+ }
+
+ configure_tun_ip(tunnel);
+}
+
+/* function: packet_handler
+ * takes a tun header and a packet and sends it down the stack
+ * tunnel - tun device data
+ * tun_header - tun header
+ * packet - packet
+ * packetsize - size of packet
+ */
+void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet,
+ size_t packetsize) {
+ int fd;
+ int iov_len = 0;
+
+ // Allocate buffers for all packet headers.
+ struct tun_pi tun_targ;
+ char iphdr[sizeof(struct ip6_hdr)];
+ char transporthdr[MAX_TCP_HDR];
+ char icmp_iphdr[sizeof(struct ip6_hdr)];
+ 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, sizeof(tun_targ) }, // Tunnel header.
+ { iphdr, 0 }, // IP header.
+ { transporthdr, 0 }, // Transport layer header.
+ { icmp_iphdr, 0 }, // ICMP error inner IP header.
+ { icmp_transporthdr, 0 }, // ICMP error transport layer header.
+ { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload.
+ };
+
+ if(tun_header->flags != 0) {
+ logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags);
+ }
+
+ if(ntohs(tun_header->proto) == ETH_P_IP) {
+ fd = tunnel->fd6;
+ fill_tun_header(&tun_targ, ETH_P_IPV6);
+ iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize);
+ } else if(ntohs(tun_header->proto) == ETH_P_IPV6) {
+ fd = tunnel->fd4;
+ fill_tun_header(&tun_targ, ETH_P_IP);
+ iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize);
+ } else {
+ logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto);
+ }
+
+ if (iov_len > 0) {
+ writev(fd, out, iov_len);
+ }
+}
+
+/* function: read_packet
+ * reads a packet from the tunnel fd and passes it down the stack
+ * active_fd - tun file descriptor marked ready for reading
+ * tunnel - tun device data
+ */
+void read_packet(int active_fd, const struct tun_data *tunnel) {
+ ssize_t readlen;
+ char packet[PACKETLEN];
+
+ // in case something ignores the packet length
+ memset(packet, 0, PACKETLEN);
+
+ readlen = read(active_fd,packet,PACKETLEN);
+
+ if(readlen < 0) {
+ 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;
+ } else {
+ struct tun_pi tun_header;
+ ssize_t header_size = sizeof(struct tun_pi);
+
+ if(readlen < header_size) {
+ logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen);
+ return;
+ }
+
+ packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size);
+ }
+}
+
+/* function: event_loop
+ * reads packets from the tun network interface and passes them down the stack
+ * tunnel - tun device data
+ */
+void event_loop(const struct tun_data *tunnel) {
+ time_t last_interface_poll;
+ struct pollfd wait_fd[2];
+
+ // start the poll timer
+ last_interface_poll = time(NULL);
+
+ wait_fd[0].fd = tunnel->fd6;
+ wait_fd[0].events = POLLIN;
+ wait_fd[0].revents = 0;
+ wait_fd[1].fd = tunnel->fd4;
+ wait_fd[1].events = POLLIN;
+ wait_fd[1].revents = 0;
+
+ while(running) {
+ if(poll(wait_fd, 2, NO_TRAFFIC_INTERFACE_POLL_FREQUENCY*1000) == -1) {
+ if(errno != EINTR) {
+ logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno));
+ }
+ } else {
+ int i;
+ for(i = 0; i < 2; i++) {
+ if((wait_fd[i].revents & POLLIN) != 0) {
+ read_packet(wait_fd[i].fd,tunnel);
+ }
+ }
+ }
+
+ time_t now = time(NULL);
+ if(last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) {
+ interface_poll(tunnel);
+ last_interface_poll = now;
+ }
+ }
+}
+
+/* function: print_help
+ * in case the user is running this on the command line
+ */
+void print_help() {
+ printf("android-clat arguments:\n");
+ printf("-i [uplink interface]\n");
+ printf("-p [plat prefix]\n");
+}
+
+/* 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;
+
+ strcpy(tunnel.device6, DEVICENAME6);
+ strcpy(tunnel.device4, DEVICENAME4);
+
+ while((opt = getopt(argc, argv, "i:p:h")) != -1) {
+ switch(opt) {
+ case 'i':
+ uplink_interface = optarg;
+ break;
+ case 'p':
+ plat_prefix = optarg;
+ break;
+ case 'h':
+ default:
+ print_help();
+ exit(1);
+ break;
+ }
+ }
+
+ if(uplink_interface == NULL) {
+ logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
+ printf("I need an interface\n");
+ exit(1);
+ }
+ logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface);
+
+ // open the tunnel device before dropping privs
+ tunnel.fd6 = tun_open();
+ if(tunnel.fd6 < 0) {
+ logmsg(ANDROID_LOG_FATAL, "tun_open failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ tunnel.fd4 = tun_open();
+ if(tunnel.fd4 < 0) {
+ logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ // open the forwarding configuration before dropping privs
+ forwarding_fd = open("/proc/sys/net/ipv6/conf/all/forwarding", O_RDWR);
+ if(forwarding_fd < 0) {
+ logmsg(ANDROID_LOG_FATAL,"open /proc/sys/net/ipv6/conf/all/forwarding failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ // run under a regular user
+ drop_root();
+
+ // When run from netd, the environment variable ANDROID_DNS_MODE is set to
+ // "local", but that only works for the netd process itself.
+ unsetenv("ANDROID_DNS_MODE");
+
+ configure_interface(uplink_interface, plat_prefix, &tunnel);
+
+ set_forwarding(forwarding_fd,"1\n");
+
+ // 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);
+
+ set_forwarding(forwarding_fd,"0\n");
+ logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface);
+
+ return 0;
+}
diff --git a/clatd.conf b/clatd.conf
new file mode 100644
index 0000000..086d39a
--- /dev/null
+++ b/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.0.0.4
+
+# 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 ipv4.google.com
+
+# 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/clatd.h b/clatd.h
new file mode 100644
index 0000000..3459b09
--- /dev/null
+++ b/clatd.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <linux/if_tun.h>
+
+#define MAXMTU 1500
+#define PACKETLEN (MAXMTU+sizeof(struct tun_pi))
+#define CLATD_VERSION "1.1"
+
+// 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
+
+#endif /* __CLATD_H__ */
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..e7ec80e
--- /dev/null
+++ b/config.c
@@ -0,0 +1,302 @@
+/*
+ * 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 "config.h"
+#include "dns64.h"
+#include "logging.h"
+#include "getaddr.h"
+#include "clatd.h"
+#include "setroute.h"
+
+struct clat_config Global_Clatd_Config;
+
+/* function: config_item_str
+ * locates the config item and returns the pointer to a string, or NULL on failure. Caller frees pointer
+ * root - parsed configuration
+ * item_name - name of config item to locate
+ * defaultvar - value to use if config item isn't present
+ */
+char *config_item_str(cnode *root, const char *item_name, const char *defaultvar) {
+ const char *tmp;
+
+ if(!(tmp = config_str(root, item_name, defaultvar))) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
+ return NULL;
+ }
+ return strdup(tmp);
+}
+
+/* function: config_item_int16_t
+ * locates the config item, parses the integer, and returns the pointer ret_val_ptr, or NULL on failure
+ * root - parsed configuration
+ * item_name - name of config item to locate
+ * defaultvar - value to use if config item isn't present
+ * ret_val_ptr - pointer for return value storage
+ */
+int16_t *config_item_int16_t(cnode *root, const char *item_name, const char *defaultvar, int16_t *ret_val_ptr) {
+ const char *tmp;
+ char *endptr;
+ long int conf_int;
+
+ if(!(tmp = config_str(root, item_name, defaultvar))) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
+ return NULL;
+ }
+
+ errno = 0;
+ conf_int = strtol(tmp,&endptr,10);
+ if(errno > 0) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item is not numeric: %s (error=%s)",item_name,tmp,strerror(errno));
+ return NULL;
+ }
+ if(endptr == tmp || *tmp == '\0') {
+ logmsg(ANDROID_LOG_FATAL,"%s config item is not numeric: %s",item_name,tmp);
+ return NULL;
+ }
+ if(*endptr != '\0') {
+ logmsg(ANDROID_LOG_FATAL,"%s config item contains non-numeric characters: %s",item_name,endptr);
+ return NULL;
+ }
+ if(conf_int > INT16_MAX || conf_int < INT16_MIN) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item is too big/small: %d",item_name,conf_int);
+ return NULL;
+ }
+ *ret_val_ptr = conf_int;
+ return ret_val_ptr;
+}
+
+/* function: config_item_ip
+ * locates the config item, parses the ipv4 address, and returns the pointer ret_val_ptr, or NULL on failure
+ * root - parsed configuration
+ * item_name - name of config item to locate
+ * defaultvar - value to use if config item isn't present
+ * ret_val_ptr - pointer for return value storage
+ */
+struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *defaultvar, struct in_addr *ret_val_ptr) {
+ const char *tmp;
+ int status;
+
+ if(!(tmp = config_str(root, item_name, defaultvar))) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
+ return NULL;
+ }
+
+ status = inet_pton(AF_INET, tmp, ret_val_ptr);
+ if(status <= 0) {
+ logmsg(ANDROID_LOG_FATAL,"invalid IPv4 address specified for %s: %s", item_name, tmp);
+ return NULL;
+ }
+
+ return ret_val_ptr;
+}
+
+/* function: config_item_ip6
+ * locates the config item, parses the ipv6 address, and returns the pointer ret_val_ptr, or NULL on failure
+ * root - parsed configuration
+ * item_name - name of config item to locate
+ * defaultvar - value to use if config item isn't present
+ * ret_val_ptr - pointer for return value storage
+ */
+struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char *defaultvar, struct in6_addr *ret_val_ptr) {
+ const char *tmp;
+ int status;
+
+ if(!(tmp = config_str(root, item_name, defaultvar))) {
+ logmsg(ANDROID_LOG_FATAL,"%s config item needed",item_name);
+ return NULL;
+ }
+
+ status = inet_pton(AF_INET6, tmp, ret_val_ptr);
+ if(status <= 0) {
+ logmsg(ANDROID_LOG_FATAL,"invalid IPv6 address specified for %s: %s", item_name, tmp);
+ return NULL;
+ }
+
+ return ret_val_ptr;
+}
+
+/* function: free_config
+ * frees the memory used by the global config variable
+ */
+void free_config() {
+ if(Global_Clatd_Config.plat_from_dns64_hostname) {
+ free(Global_Clatd_Config.plat_from_dns64_hostname);
+ Global_Clatd_Config.plat_from_dns64_hostname = NULL;
+ }
+}
+
+/* function: 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
+ */
+void dns64_detection() {
+ int i, backoff_sleep, status;
+ struct in6_addr tmp_ptr;
+
+ backoff_sleep = 1;
+
+ while(1) {
+ status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,&tmp_ptr);
+ if(status > 0) {
+ memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr));
+ return;
+ }
+ if(status < 0) {
+ logmsg(ANDROID_LOG_FATAL, "dns64_detection/no dns64, giving up\n");
+ exit(1);
+ }
+ logmsg(ANDROID_LOG_WARN, "dns64_detection failed, sleeping for %d seconds", backoff_sleep);
+ sleep(backoff_sleep);
+ if(backoff_sleep >= 120) {
+ backoff_sleep = 120;
+ } else {
+ backoff_sleep *= 2;
+ }
+ }
+}
+
+
+/* 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;
+
+ for(i = 2; i < 4; i++) {
+ interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i];
+ }
+}
+
+/* function: subnet_from_interface
+ * finds the ipv6 subnet configured on the specified interface
+ * root - parsed configuration
+ * interface - network interface name
+ */
+int subnet_from_interface(cnode *root, const char *interface) {
+ union anyip *interface_ip;
+
+ if(!config_item_ip6(root, "ipv6_host_id", "::200:5E10:0:0", &Global_Clatd_Config.ipv6_host_id))
+ return 0;
+
+ interface_ip = getinterface_ip(interface, AF_INET6);
+ if(!interface_ip) {
+ logmsg(ANDROID_LOG_FATAL,"unable to find an ipv6 ip on interface %s",interface);
+ return 0;
+ }
+
+ memcpy(&Global_Clatd_Config.ipv6_local_subnet, &interface_ip->ip6, sizeof(struct in6_addr));
+ free(interface_ip);
+
+ config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet);
+
+ return 1;
+}
+
+/* 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
+ */
+int read_config(const char *file, const char *uplink_interface, const char *plat_prefix) {
+ cnode *root = config_node("", "");
+ void *tmp_ptr = NULL;
+
+ 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;
+ }
+
+ strncpy(Global_Clatd_Config.default_pdp_interface, uplink_interface, sizeof(Global_Clatd_Config.default_pdp_interface));
+
+ if(!subnet_from_interface(root,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(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();
+ }
+ }
+
+
+ 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,"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/config.h b/config.h
new file mode 100644
index 0000000..a83cbec
--- /dev/null
+++ b/config.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <sys/system_properties.h>
+
+#define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1"
+#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com"
+
+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;
+ struct in6_addr plat_subnet;
+ char default_pdp_interface[PROP_VALUE_MAX];
+ char *plat_from_dns64_hostname;
+};
+
+extern struct clat_config Global_Clatd_Config;
+
+int read_config(const char *file, const char *uplink_interface, const char *plat_prefix);
+void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip);
+
+#endif /* __CONFIG_H__ */
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..8e09672
--- /dev/null
+++ b/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/dns64.c b/dns64.c
new file mode 100644
index 0000000..b139fe4
--- /dev/null
+++ b/dns64.c
@@ -0,0 +1,96 @@
+/*
+ * 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"
+
+/* function: plat_prefix
+ * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on temporary failure, -1 on permanent failure
+ * ipv4_name - name to lookup
+ * prefix - the plat /96 prefix
+ */
+int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) {
+ struct addrinfo hints, *result, *p;
+ int status, plat_addr_set, ipv4_records, ipv6_records;
+ struct in6_addr plat_addr, this_plat_addr;
+ struct sockaddr_in6 *this_addr;
+ char plat_addr_str[INET6_ADDRSTRLEN];
+
+ logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS...");
+
+ result = NULL;
+ plat_addr_set = 0;
+ ipv4_records = ipv6_records = 0;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ status = getaddrinfo(ipv4_name, NULL, &hints, &result);
+ if(status != 0) {
+ logmsg(ANDROID_LOG_ERROR,"plat_prefix/dns(%s) status = %d/%s\n", ipv4_name, status, gai_strerror(status));
+ return 0;
+ }
+
+ for(p = result; p; p = p->ai_next) {
+ if(p->ai_family == AF_INET) {
+ ipv4_records++;
+ continue;
+ }
+ if(p->ai_family != AF_INET6) {
+ logmsg(ANDROID_LOG_WARN,"plat_prefix/unexpected address family: %d\n", p->ai_family);
+ continue;
+ }
+ ipv6_records++;
+ this_addr = (struct sockaddr_in6 *)p->ai_addr;
+ this_plat_addr = this_addr->sin6_addr;
+ this_plat_addr.s6_addr32[3] = 0;
+
+ if(!plat_addr_set) {
+ plat_addr = this_plat_addr;
+ plat_addr_set = 1;
+ continue;
+ }
+
+ inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str));
+ if(!IN6_ARE_ADDR_EQUAL(&plat_addr, &this_plat_addr)) {
+ char this_plat_addr_str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &this_plat_addr, this_plat_addr_str, sizeof(this_plat_addr_str));
+ logmsg(ANDROID_LOG_ERROR,"plat_prefix/two different plat addrs = %s,%s",
+ plat_addr_str,this_plat_addr_str);
+ }
+ }
+ if(result != NULL) {
+ freeaddrinfo(result);
+ }
+ if(ipv4_records > 0 && ipv6_records == 0) {
+ logmsg(ANDROID_LOG_WARN,"plat_prefix/no dns64 detected\n");
+ return -1;
+ }
+
+ logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", plat_addr_str);
+ *prefix = plat_addr;
+ return 1;
+}
diff --git a/dns64.h b/dns64.h
new file mode 100644
index 0000000..f295e50
--- /dev/null
+++ b/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, struct in6_addr *prefix);
+
+#endif
diff --git a/dump.c b/dump.c
new file mode 100644
index 0000000..8567655
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,240 @@
+/*
+ * 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 char *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 char *payload, size_t payload_size) {
+ uint32_t temp_checksum;
+ temp_checksum = ipv4_pseudo_header_checksum(0, 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 char *payload, size_t payload_size) {
+ uint32_t temp_checksum;
+ temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size);
+ dump_udp_generic(udp, temp_checksum, payload, payload_size);
+}
+
+/* print tcp header */
+void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t options_size, uint32_t temp_checksum, const char *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 char *payload, size_t payload_size, const char *options, size_t options_size) {
+ uint32_t temp_checksum;
+
+ temp_checksum = ipv4_pseudo_header_checksum(0, 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 char *payload, size_t payload_size, const char *options, size_t options_size) {
+ uint32_t temp_checksum;
+
+ temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*tcp) + options_size + payload_size);
+ dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
+}
+
+/* generic hex dump */
+void logcat_hexdump(const char *info, const char *data, size_t len) {
+ char output[PACKETLEN*3+2];
+ size_t i;
+
+ for(i = 0; i < len && i < PACKETLEN; i++) {
+ snprintf(output + i*3, 4, " %02x", (uint8_t)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/dump.h b/dump.h
new file mode 100644
index 0000000..9cb040c
--- /dev/null
+++ b/dump.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.
+ *
+ * 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 char *payload, size_t payload_size);
+void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *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 char *payload, size_t payload_size);
+void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size, const char *options, size_t options_size);
+
+void logcat_hexdump(const char *info, const char *data, size_t len);
+void dump_iovec(const struct iovec *iov, int iov_len);
+
+#endif /* __DUMP_H__ */
diff --git a/getaddr.c b/getaddr.c
new file mode 100644
index 0000000..fb761f0
--- /dev/null
+++ b/getaddr.c
@@ -0,0 +1,137 @@
+/*
+ * 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/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(struct sockaddr_nl *nla, struct nlmsgerr *err, 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/getaddr.h b/getaddr.h
new file mode 100644
index 0000000..5718e62
--- /dev/null
+++ b/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/getroute.c b/getroute.c
new file mode 100644
index 0000000..5f9475e
--- /dev/null
+++ b/getroute.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * getroute.c - get an ip route
+ */
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <arpa/inet.h>
+
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include "getroute.h"
+#include "netlink_callbacks.h"
+#include "netlink_msg.h"
+
+/* function: get_default_route_cb
+ * finds the default route with the request family and out interface and saves the gateway
+ * msg - netlink message
+ * data - (struct default_route_data) requested filters and response storage
+ */
+static int get_default_route_cb(struct nl_msg *msg, void *data) {
+ struct rtmsg *rt_p;
+ struct rtattr *rta_p;
+ int rta_len;
+ struct default_route_data *default_route = data;
+ union anyip *this_gateway = NULL;
+ ssize_t this_gateway_size;
+ int this_interface_id = -1;
+
+ if(default_route->reply_found_route) { // we already found our route
+ return NL_OK;
+ }
+
+ rt_p = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));
+ if(rt_p->rtm_dst_len != 0) { // not a default route
+ return NL_OK;
+ }
+ if((rt_p->rtm_family != default_route->request_family) || (rt_p->rtm_table != RT_TABLE_MAIN)) { // not a route we care about
+ return NL_OK;
+ }
+
+ rta_p = (struct rtattr *)RTM_RTA(rt_p);
+ rta_len = RTM_PAYLOAD(nlmsg_hdr(msg));
+ for(; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) {
+ switch(rta_p->rta_type) {
+ case RTA_GATEWAY:
+ this_gateway = RTA_DATA(rta_p);
+ this_gateway_size = RTA_PAYLOAD(rta_p);
+ break;
+ case RTA_OIF:
+ this_interface_id = *(int *)RTA_DATA(rta_p);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(this_interface_id == default_route->request_interface_id) {
+ default_route->reply_found_route = 1;
+ if(this_gateway != NULL) {
+ memcpy(&default_route->reply_gateway, this_gateway, this_gateway_size);
+ default_route->reply_has_gateway = 1;
+ } else {
+ default_route->reply_has_gateway = 0;
+ }
+ }
+ return NL_OK;
+}
+
+/* function: error_handler
+ * error callback for get_default_route
+ * nla - where the message came from
+ * err - netlink message
+ * arg - (int *) storage for the error number
+ */
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
+ int *retval = arg;
+ if(err->error < 0) { // error_handler called even on no error (NLMSG_ERROR reply type used)
+ *retval = err->error;
+ }
+ return NL_OK;
+}
+
+/* function: get_default_route
+ * finds the first default route with the given family and interface, returns the gateway (if it exists) in the struct
+ * default_route - requested family and interface, and response storage
+ */
+int get_default_route(struct default_route_data *default_route) {
+ struct rtmsg msg;
+ struct nl_cb *callbacks = NULL;
+ struct nl_msg *nlmsg = NULL;
+ int retval = 0;
+
+ default_route->reply_has_gateway = 0;
+ default_route->reply_found_route = 0;
+
+ memset(&msg,'\0',sizeof(msg));
+ msg.rtm_family = default_route->request_family;
+ msg.rtm_table = RT_TABLE_MAIN;
+ msg.rtm_protocol = RTPROT_KERNEL;
+ msg.rtm_scope = RT_SCOPE_UNIVERSE;
+
+ callbacks = nl_cb_alloc(NL_CB_DEFAULT);
+ if(!callbacks) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ // get_default_route_cb sets the response fields in default_route
+ nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, get_default_route_cb, default_route);
+ nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &retval);
+
+ nlmsg = nlmsg_alloc_rtmsg(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ROOT, &msg);
+ if(!nlmsg) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ send_netlink_msg(nlmsg, callbacks);
+
+cleanup:
+ if(callbacks)
+ nl_cb_put(callbacks);
+ if(nlmsg)
+ nlmsg_free(nlmsg);
+
+ return retval;
+}
diff --git a/getroute.h b/getroute.h
new file mode 100644
index 0000000..e7b8670
--- /dev/null
+++ b/getroute.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * getroute.h - get an ip route
+ */
+#ifndef __GETROUTE_H__
+#define __GETROUTE_H__
+
+// for union anyip
+#include "getaddr.h"
+
+struct default_route_data {
+ int request_interface_id;
+ int request_family;
+
+ union anyip reply_gateway;
+ int reply_has_gateway;
+ int reply_found_route;
+};
+
+int get_default_route(struct default_route_data *default_route);
+
+#endif
diff --git a/icmp.c b/icmp.c
new file mode 100644
index 0000000..af96b83
--- /dev/null
+++ b/icmp.c
@@ -0,0 +1,181 @@
+/*
+ * 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"
+
+/* 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 %d", type);
+ 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/icmp.h b/icmp.h
new file mode 100644
index 0000000..632e92d
--- /dev/null
+++ b/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/ipv4.c b/ipv4.c
new file mode 100644
index 0000000..89e47e4
--- /dev/null
+++ b/ipv4.c
@@ -0,0 +1,144 @@
+/*
+ * 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 <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 "translate.h"
+#include "checksum.h"
+#include "ipv4.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, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ size_t len) {
+ const char *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct icmphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const char *) (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, int pos, const char *packet, size_t len) {
+ const struct iphdr *header = (struct iphdr *) packet;
+ struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base;
+ uint16_t frag_flags;
+ uint8_t nxthdr;
+ const char *next_header;
+ size_t len_left;
+ uint32_t checksum;
+ int iov_len;
+
+ if(len < sizeof(struct iphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header");
+ return 0;
+ }
+
+ frag_flags = ntohs(header->frag_off);
+ if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping");
+ 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.
+ checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left);
+
+ if(nxthdr == IPPROTO_ICMPV6) {
+ iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left);
+ } else if(nxthdr == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left);
+ } else if(nxthdr == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, 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/ipv4.h b/ipv4.h
new file mode 100644
index 0000000..00befd2
--- /dev/null
+++ b/ipv4.h
@@ -0,0 +1,25 @@
+/*
+ * 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.h - takes an ipv4 packet and hands it off to the proper translate function
+ */
+#ifndef __IPV4_H__
+#define __IPV4_H__
+
+#include "translate.h"
+
+int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len);
+
+#endif /* __IPV4_H__ */
diff --git a/ipv6.c b/ipv6.c
new file mode 100644
index 0000000..ef1e62f
--- /dev/null
+++ b/ipv6.c
@@ -0,0 +1,161 @@
+/*
+ * 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 <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 <arpa/inet.h>
+
+#include "translate.h"
+#include "checksum.h"
+#include "ipv6.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, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ size_t len) {
+ const char *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct icmp6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const char *) (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
+ */
+void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
+#if CLAT_DEBUG
+ 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);
+#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, int pos, const char *packet, size_t len) {
+ const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
+ struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base;
+ uint8_t protocol;
+ const char *next_header;
+ size_t len_left;
+ uint32_t checksum;
+ int iov_len;
+ int i;
+
+ 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;
+ if (protocol == IPPROTO_ICMPV6) {
+ // ICMP and ICMPv6 have different protocol numbers.
+ protocol = IPPROTO_ICMP;
+ }
+
+ /* 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);
+
+ // Calculate the pseudo-header checksum.
+ checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left);
+
+ // does not support IPv6 extension headers, this will drop any packet with them
+ if(protocol == IPPROTO_ICMP) {
+ iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, checksum,
+ len_left);
+ } else if(ip6->ip6_nxt == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum,
+ len_left);
+ } else if(ip6->ip6_nxt == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum,
+ 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/ipv6.h b/ipv6.h
new file mode 100644
index 0000000..019de17
--- /dev/null
+++ b/ipv6.h
@@ -0,0 +1,25 @@
+/*
+ * 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.h - takes an ipv6 packet and hands it off to the proper translate function
+ */
+#ifndef __IPV6_H__
+#define __IPV6_H__
+
+#include "translate.h"
+
+int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len);
+
+#endif /* __IPV6_H__ */
diff --git a/logging.c b/logging.c
new file mode 100644
index 0000000..f2f3d50
--- /dev/null
+++ b/logging.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * logging.c - print a log message
+ */
+
+#include <stdarg.h>
+#include <android/log.h>
+
+#include "logging.h"
+#include "debug.h"
+
+/* function: logmsg
+ * prints a log message to android's log buffer
+ * prio - the log message priority
+ * fmt - printf format specifier
+ * ... - printf format arguments
+ */
+void logmsg(int prio, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ __android_log_vprint(prio, "clatd", fmt, ap);
+ va_end(ap);
+}
+
+/* function: logmsg_dbg
+ * prints a log message to android's log buffer if CLAT_DEBUG is set
+ * prio - the log message priority
+ * fmt - printf format specifier
+ * ... - printf format arguments
+ */
+void logmsg_dbg(int prio, const char *fmt, ...) {
+#if CLAT_DEBUG
+ va_list ap;
+
+ va_start(ap, fmt);
+ __android_log_vprint(prio, "clatd", fmt, ap);
+ va_end(ap);
+#endif
+}
diff --git a/logging.h b/logging.h
new file mode 100644
index 0000000..1f4b6b6
--- /dev/null
+++ b/logging.h
@@ -0,0 +1,27 @@
+/*
+ * 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>
+
+void logmsg(int prio, const char *fmt, ...);
+void logmsg_dbg(int prio, const char *fmt, ...);
+
+#endif
diff --git a/mtu.c b/mtu.c
new file mode 100644
index 0000000..975bf0e
--- /dev/null
+++ b/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/mtu.h b/mtu.h
new file mode 100644
index 0000000..c330c24
--- /dev/null
+++ b/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/netlink_callbacks.c b/netlink_callbacks.c
new file mode 100644
index 0000000..5e0f34e
--- /dev/null
+++ b/netlink_callbacks.c
@@ -0,0 +1,66 @@
+/*
+ * 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(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(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/netlink_callbacks.h b/netlink_callbacks.h
new file mode 100644
index 0000000..298ad3e
--- /dev/null
+++ b/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/netlink_msg.c b/netlink_msg.c
new file mode 100644
index 0000000..2ba237d
--- /dev/null
+++ b/netlink_msg.c
@@ -0,0 +1,186 @@
+/*
+ * 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
+ */
+
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netlink-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/netlink_msg.h b/netlink_msg.h
new file mode 100644
index 0000000..13e1f28
--- /dev/null
+++ b/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/setif.c b/setif.c
new file mode 100644
index 0000000..5ee00bc
--- /dev/null
+++ b/setif.c
@@ -0,0 +1,129 @@
+/*
+ * 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 "netlink_msg.h"
+
+/* 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;
+}
diff --git a/setif.h b/setif.h
new file mode 100644
index 0000000..7f83f73
--- /dev/null
+++ b/setif.h
@@ -0,0 +1,24 @@
+/*
+ * 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);
+
+#endif
diff --git a/setroute.c b/setroute.c
new file mode 100644
index 0000000..f264387
--- /dev/null
+++ b/setroute.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * setroute.c - network route configuration
+ */
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <netlink-types.h>
+
+#include "netlink_msg.h"
+#include "setroute.h"
+#include "logging.h"
+#include "getroute.h"
+
+/* function: if_route
+ * create/replace/delete a route
+ * ifname - name of the outbound interface
+ * family - AF_INET or AF_INET6
+ * destination - pointer to a struct in_addr or in6_addr for the destination network
+ * prefixlen - bitlength of the network address (example: 24 for AF_INET's 255.255.255.0)
+ * gateway - pointer to a struct in_addr or in6_addr for the gateway to use or NULL for an interface route
+ * metric - route metric (lower is better)
+ * mtu - route-specific mtu or 0 for the interface mtu
+ * change_type - ROUTE_DELETE, ROUTE_REPLACE, or ROUTE_CREATE
+ */
+int if_route(const char *ifname, int family, const void *destination, int prefixlen, const void *gateway, int metric, int mtu, int change_type) {
+ int retval;
+ struct nl_msg *msg = NULL;
+ struct rtmsg rt;
+ uint16_t type, flags = 0;
+ size_t addr_size;
+ uint32_t ifindex;
+
+ addr_size = inet_family_size(family);
+ if(addr_size == 0) {
+ retval = -EAFNOSUPPORT;
+ goto cleanup;
+ }
+
+ if (!(ifindex = if_nametoindex(ifname))) {
+ retval = -ENODEV;
+ goto cleanup;
+ }
+
+ memset(&rt, 0, sizeof(rt));
+ rt.rtm_family = family;
+ rt.rtm_table = RT_TABLE_MAIN;
+ rt.rtm_dst_len = prefixlen;
+ switch(change_type) {
+ case ROUTE_DELETE:
+ rt.rtm_scope = RT_SCOPE_NOWHERE;
+ type = RTM_DELROUTE;
+ break;
+
+ case ROUTE_REPLACE:
+ flags = NLM_F_REPLACE;
+ case ROUTE_CREATE:
+ type = RTM_NEWROUTE;
+ flags |= NLM_F_CREATE;
+ if(gateway == NULL) {
+ rt.rtm_scope = RT_SCOPE_LINK;
+ } else {
+ rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ }
+ rt.rtm_type = RTN_UNICAST;
+ //RTPROT_STATIC = from administrator's configuration
+ //RTPROT_BOOT = from an automatic process
+ rt.rtm_protocol = RTPROT_BOOT;
+ break;
+
+ default:
+ retval = -EINVAL;
+ goto cleanup;
+ }
+
+ flags |= NLM_F_REQUEST | NLM_F_ACK;
+
+ msg = nlmsg_alloc_rtmsg(type, flags, &rt);
+ if(!msg) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ if(nla_put(msg, RTA_DST, addr_size, destination) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(gateway != NULL)
+ if(nla_put(msg, RTA_GATEWAY, addr_size, gateway) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(nla_put(msg, RTA_OIF, 4, &ifindex) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(nla_put(msg, RTA_PRIORITY, 4, &metric) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(mtu > 0 && change_type != ROUTE_DELETE) {
+ // MTU is inside an RTA_METRICS nested message
+ struct nlattr *metrics = nla_nest_start(msg, RTA_METRICS);
+ if(metrics == NULL) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ if(nla_put(msg, RTAX_MTU, 4, &mtu) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ nla_nest_end(msg, metrics);
+ }
+
+ retval = netlink_sendrecv(msg);
+
+cleanup:
+ if(msg)
+ nlmsg_free(msg);
+
+ return retval;
+}
diff --git a/setroute.h b/setroute.h
new file mode 100644
index 0000000..58f61cf
--- /dev/null
+++ b/setroute.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * setroute.h - network route configuration
+ */
+
+#ifndef __SETROUTE_H__
+#define __SETROUTE_H__
+
+#define ROUTE_DELETE 0
+#define ROUTE_REPLACE 1
+#define ROUTE_CREATE 2
+int if_route(const char *ifname, int family, const void *destination, int cidr, const void *gateway, int metric, int mtu, int change_type);
+
+#endif
diff --git a/translate.c b/translate.c
new file mode 100644
index 0000000..fc70f3d
--- /dev/null
+++ b/translate.c
@@ -0,0 +1,399 @@
+/*
+ * 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 <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 "icmp.h"
+#include "translate.h"
+#include "checksum.h"
+#include "clatd.h"
+#include "config.h"
+#include "logging.h"
+#include "debug.h"
+
+/* 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, int 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, int 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 (ip->saddr == (uint32_t) 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: 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, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ const char *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);
+
+ // 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. This is simpler than the
+ // alternative, which is to always update the pseudo-header checksum in all UDP/TCP/ICMP
+ // translation functions (rather than pre-calculating it when translating the IPv4 header).
+ // 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 = htonl(ntohl(checksum) + 20);
+ } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
+ // Ping packet.
+ icmp6_targ->icmp6_id = icmp->un.echo.id;
+ icmp6_targ->icmp6_seq = icmp->un.echo.sequence;
+ out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+ clat_packet_len = CLAT_POS_PAYLOAD + 1;
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+ icmp6_targ->icmp6_cksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp6_targ->icmp6_cksum = packet_checksum(checksum, out, pos);
+
+ return clat_packet_len;
+}
+
+/* function: icmp6_to_icmp
+ * translate ipv6 icmp to ipv4 icmp
+ * out - output packet
+ * icmp6 - source packet icmp6 header
+ * checksum - pseudo-header checksum (unused)
+ * 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, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ const char *payload, size_t payload_size) {
+ struct icmphdr *icmp_targ = out[pos].iov_base;
+ uint8_t icmp_type;
+ int ttl;
+ 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);
+ } 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 = (char *) payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+ clat_packet_len = CLAT_POS_PAYLOAD + 1;
+ } else {
+ // Unknown type/code. The type/code conversion functions have already logged an error.
+ return 0;
+ }
+
+ icmp_targ->checksum = 0; // Checksum field must be 0 when calculating checksum.
+ icmp_targ->checksum = packet_checksum(0, out, pos);
+
+ return clat_packet_len;
+}
+
+/* function: udp_packet
+ * takes a udp packet and sets it up for translation
+ * out - output packet
+ * udp - pointer to udp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ */
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
+ const char *payload;
+ size_t payload_size;
+
+ if(len < sizeof(struct udphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
+ return 0;
+ }
+
+ payload = (const char *) (udp + 1);
+ payload_size = len - sizeof(struct udphdr);
+
+ return udp_translate(out, pos, udp, checksum, 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, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
+ const char *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 char *) tcp) + header_size;
+ payload_size = len - header_size;
+
+ return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
+}
+
+/* function: udp_translate
+ * common between ipv4/ipv6 - setup checksum and send udp packet
+ * out - output packet
+ * udp - udp header
+ * checksum - pseudo-header checksum
+ * 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, int pos, const struct udphdr *udp, uint32_t checksum,
+ const char *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 = (char *) payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(checksum, out, pos);
+
+ 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
+ *
+ * TODO: mss rewrite
+ * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented)
+ */
+int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
+ uint32_t checksum, const char *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 = (char *)payload;
+ out[CLAT_POS_PAYLOAD].iov_len = payload_size;
+
+ tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ tcp_targ->check = packet_checksum(checksum, out, pos);
+
+ return CLAT_POS_PAYLOAD + 1;
+}
diff --git a/translate.h b/translate.h
new file mode 100644
index 0000000..fded251
--- /dev/null
+++ b/translate.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <linux/if_tun.h>
+
+#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words.
+
+// 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.
+enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR,
+ CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR,
+ CLAT_POS_PAYLOAD, CLAT_POS_MAX };
+typedef struct iovec clat_packet[CLAT_POS_MAX];
+
+// Calculates the checksum over all the packet components starting from pos.
+uint16_t packet_checksum(uint32_t checksum, clat_packet packet, int pos);
+
+// Returns the total length of the packet components after pos.
+uint16_t packet_length(clat_packet packet, int 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 ICMP packets.
+int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ const char *payload, size_t payload_size);
+int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ const char *payload, size_t payload_size);
+
+// Translate TCP and UDP packets.
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len);
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len);
+
+int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
+ uint32_t checksum, const char *payload, size_t payload_size);
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
+ const char *payload, size_t payload_size);
+
+#endif /* __TRANSLATE_H__ */