diff options
author | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-24 21:14:00 +0100 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-24 21:14:00 +0100 |
commit | d523ff99b0e04cbbfbfcb556eed9a56948fe56c4 (patch) | |
tree | 6da578352f0ec62fa7ac9e5eff63fdb03a1af516 | |
parent | 62ea9b6658a4c2116eeb6450c8244269448e79aa (diff) | |
parent | 0d7837b9dddce300713bfcf2b84aecf2e360a502 (diff) | |
download | android_external_android-clat-cm-10.2.tar.gz android_external_android-clat-cm-10.2.tar.bz2 android_external_android-clat-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.mk | 22 | ||||
-rw-r--r-- | BUGS | 9 | ||||
-rw-r--r-- | LICENSE | 201 | ||||
-rw-r--r-- | MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | NOTICE | 189 | ||||
-rw-r--r-- | checksum.c | 115 | ||||
-rw-r--r-- | checksum.h | 28 | ||||
-rw-r--r-- | clatd.c | 513 | ||||
-rw-r--r-- | clatd.conf | 15 | ||||
-rw-r--r-- | clatd.h | 33 | ||||
-rw-r--r-- | config.c | 302 | ||||
-rw-r--r-- | config.h | 42 | ||||
-rw-r--r-- | debug.h | 24 | ||||
-rw-r--r-- | dns64.c | 96 | ||||
-rw-r--r-- | dns64.h | 23 | ||||
-rw-r--r-- | dump.c | 240 | ||||
-rw-r--r-- | dump.h | 34 | ||||
-rw-r--r-- | getaddr.c | 137 | ||||
-rw-r--r-- | getaddr.h | 28 | ||||
-rw-r--r-- | getroute.c | 143 | ||||
-rw-r--r-- | getroute.h | 35 | ||||
-rw-r--r-- | icmp.c | 181 | ||||
-rw-r--r-- | icmp.h | 45 | ||||
-rw-r--r-- | ipv4.c | 144 | ||||
-rw-r--r-- | ipv4.h | 25 | ||||
-rw-r--r-- | ipv6.c | 161 | ||||
-rw-r--r-- | ipv6.h | 25 | ||||
-rw-r--r-- | logging.c | 53 | ||||
-rw-r--r-- | logging.h | 27 | ||||
-rw-r--r-- | mtu.c | 46 | ||||
-rw-r--r-- | mtu.h | 24 | ||||
-rw-r--r-- | netlink_callbacks.c | 66 | ||||
-rw-r--r-- | netlink_callbacks.h | 24 | ||||
-rw-r--r-- | netlink_msg.c | 186 | ||||
-rw-r--r-- | netlink_msg.h | 30 | ||||
-rw-r--r-- | setif.c | 129 | ||||
-rw-r--r-- | setif.h | 24 | ||||
-rw-r--r-- | setroute.c | 142 | ||||
-rw-r--r-- | setroute.h | 27 | ||||
-rw-r--r-- | translate.c | 399 | ||||
-rw-r--r-- | translate.h | 65 |
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) @@ -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) @@ -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 @@ -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__ */ @@ -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:: @@ -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__ */ @@ -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__ */ @@ -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; +} @@ -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 @@ -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 @@ -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 @@ -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; +} @@ -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__ */ @@ -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; +} @@ -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__ */ @@ -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; +} @@ -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 @@ -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; +} @@ -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 @@ -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; +} @@ -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__ */ |