From a45056e35c1af2a0f0a6eed258fd5fdf4846a79f Mon Sep 17 00:00:00 2001 From: Daniel Drown Date: Fri, 23 Mar 2012 10:42:54 -0500 Subject: android clat service This software provides the nat 4->6 translation needed for the "clat" part of the 464xlat standard. It is needed for better IPv4 application support while on an IPv6-only mobile network connection using 464xlat's nat64 (such as T-Mobile's IPv6 trial). A general diagram of how 464xlat works: http://dan.drown.org/android/clat/Clat-Plat.png Depends-on: I2392f8127dcd90d16b0f20ff31bcc5aa096db464 Change-Id: If2bc6916fc66fd4bca7cc241c83cfae839b82e15 Signed-off-by: Daniel Drown --- Android.mk | 14 ++ BUGS | 9 + LICENSE | 201 +++++++++++++++++++ MODULE_LICENSE_APACHE2 | 0 NOTICE | 189 ++++++++++++++++++ checksum.c | 113 +++++++++++ checksum.h | 28 +++ clatd.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++++ clatd.conf | 15 ++ clatd.h | 32 +++ config.c | 302 ++++++++++++++++++++++++++++ config.h | 42 ++++ debug.h | 24 +++ dns64.c | 91 +++++++++ dns64.h | 23 +++ dump.c | 225 +++++++++++++++++++++ dump.h | 33 ++++ getaddr.c | 137 +++++++++++++ getaddr.h | 28 +++ getroute.c | 143 ++++++++++++++ getroute.h | 35 ++++ ipv4.c | 187 ++++++++++++++++++ ipv4.h | 23 +++ ipv6.c | 194 ++++++++++++++++++ ipv6.h | 23 +++ logging.c | 53 +++++ logging.h | 27 +++ mtu.c | 46 +++++ mtu.h | 24 +++ netlink_callbacks.c | 66 +++++++ netlink_callbacks.h | 24 +++ netlink_msg.c | 168 ++++++++++++++++ netlink_msg.h | 29 +++ setif.c | 129 ++++++++++++ setif.h | 24 +++ setroute.c | 186 ++++++++++++++++++ setroute.h | 28 +++ translate.c | 404 ++++++++++++++++++++++++++++++++++++++ translate.h | 30 +++ 39 files changed, 3871 insertions(+) create mode 100644 Android.mk create mode 100644 BUGS create mode 100644 LICENSE create mode 100644 MODULE_LICENSE_APACHE2 create mode 100644 NOTICE create mode 100644 checksum.c create mode 100644 checksum.h create mode 100644 clatd.c create mode 100644 clatd.conf create mode 100644 clatd.h create mode 100644 config.c create mode 100644 config.h create mode 100644 debug.h create mode 100644 dns64.c create mode 100644 dns64.h create mode 100644 dump.c create mode 100644 dump.h create mode 100644 getaddr.c create mode 100644 getaddr.h create mode 100644 getroute.c create mode 100644 getroute.h create mode 100644 ipv4.c create mode 100644 ipv4.h create mode 100644 ipv6.c create mode 100644 ipv6.h create mode 100644 logging.c create mode 100644 logging.h create mode 100644 mtu.c create mode 100644 mtu.h create mode 100644 netlink_callbacks.c create mode 100644 netlink_callbacks.h create mode 100644 netlink_msg.c create mode 100644 netlink_msg.h create mode 100644 setif.c create mode 100644 setif.h create mode 100644 setroute.c create mode 100644 setroute.h create mode 100644 translate.c create mode 100644 translate.h diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..b1238ce --- /dev/null +++ b/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.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 + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE := clatd + +include $(BUILD_EXECUTABLE) diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..5494d6b --- /dev/null +++ b/BUGS @@ -0,0 +1,9 @@ +Optional: + - ipv6 traceroute to handle 464 transitions + +known problems/assumptions: + - does not handle ICMP types other than echo/echo reply + - does not handle protocols other than ICMP, UDP, and TCP + - assumes the handset has its own (routed) /64 ipv6 subnet + - assumes the /120 or /128 ipv6 subnet it generates can use the nat64 gateway + - assumes the nat64 gateway has the ipv4 address in the last 32 bits of the ipv6 address (that it uses a /96 plat subnet) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5943b54 --- /dev/null +++ b/NOTICE @@ -0,0 +1,189 @@ + Copyright (c) 2010-2012, Daniel Drown + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/checksum.c b/checksum.c new file mode 100644 index 0000000..14b7113 --- /dev/null +++ b/checksum.c @@ -0,0 +1,113 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "checksum.h" + +/* function: ip_checksum_add + * adds data to a checksum + * current_sum - 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_sum, const void *data, int len) { + uint32_t checksum = current_sum; + 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_sum - the current checksum or 0 to start a new checksum + * ip6 - the ipv6 header + */ +uint32_t ipv6_pseudo_header_checksum(uint32_t current_sum, const struct ip6_hdr *ip6) { + uint32_t checksum_len, checksum_next; + + checksum_len = htonl(ntohs(ip6->ip6_plen)); + checksum_next = htonl(ip6->ip6_nxt); + + current_sum = ip_checksum_add(current_sum,&(ip6->ip6_src),sizeof(struct in6_addr)); + current_sum = ip_checksum_add(current_sum,&(ip6->ip6_dst),sizeof(struct in6_addr)); + current_sum = ip_checksum_add(current_sum,&checksum_len,sizeof(checksum_len)); + current_sum = ip_checksum_add(current_sum,&checksum_next,sizeof(checksum_next)); + + return current_sum; +} + +/* function: ipv4_pseudo_header_checksum + * calculate the pseudo header checksum for use in tcp/udp headers + * current_sum - the current checksum or 0 to start a new checksum + * ip - the ipv4 header + */ +uint32_t ipv4_pseudo_header_checksum(uint32_t current_sum, const struct iphdr *ip) { + uint16_t temp_protocol, temp_length; + + temp_protocol = htons(ip->protocol); + temp_length = htons(ntohs(ip->tot_len) - ip->ihl*4); + + current_sum = ip_checksum_add(current_sum, &(ip->saddr), sizeof(uint32_t)); + current_sum = ip_checksum_add(current_sum, &(ip->daddr), sizeof(uint32_t)); + current_sum = ip_checksum_add(current_sum, &temp_protocol, sizeof(uint16_t)); + current_sum = ip_checksum_add(current_sum, &temp_length, sizeof(uint16_t)); + + return current_sum; +} diff --git a/checksum.h b/checksum.h new file mode 100644 index 0000000..c8346a0 --- /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_sum, 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_sum, const struct ip6_hdr *ip6); +uint32_t ipv4_pseudo_header_checksum(uint32_t current_sum, const struct iphdr *ip); + +#endif /* __CHECKSUM_H__ */ diff --git a/clatd.c b/clatd.c new file mode 100644 index 0000000..17a3548 --- /dev/null +++ b/clatd.c @@ -0,0 +1,522 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "ipv4.h" +#include "ipv6.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: set_accept_ra + * accepts IPv6 RAs on all interfaces, even when forwarding is on + */ +void set_accept_ra() { + int fd, i; + const char *interface_names[] = {"wlan0","default",NULL}; + const char ipv6_sysctl[] = "/proc/sys/net/ipv6/conf/"; + const char accept_ra[] = "/accept_ra"; + + for(i = 0; interface_names[i]; i++) { + ssize_t sysctl_path_len = strlen(ipv6_sysctl)+strlen(interface_names[i])+strlen(accept_ra)+1; + char *sysctl_path = malloc(sysctl_path_len); + if(!sysctl_path) { + logmsg(ANDROID_LOG_FATAL,"set_accept_ra: malloc failed"); + exit(1); + } + snprintf(sysctl_path, sysctl_path_len, "%s%s%s", ipv6_sysctl, interface_names[i], accept_ra); + + fd = open(sysctl_path, O_RDWR); + free(sysctl_path); + if(fd < 0) { + continue; + } + if(write(fd, "2\n", 2) < 0) { + logmsg(ANDROID_LOG_WARN,"write to (%s)accept_ra failed: %s",interface_names[i],strerror(errno)); + } + close(fd); + } +} + +/* function: got_sigterm + * signal handler: mark it time to clean up + */ +void got_sigterm(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) { + struct in_addr default_4; + int status; + + default_4.s_addr = INADDR_ANY; + + 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); + } + 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); + } + + configure_tun_ipv6(tunnel); + + /* setup default ipv4 route */ + status = if_route(tunnel->device4, AF_INET, &default_4, 0, NULL, 1, 0, ROUTE_REPLACE); + if(status < 0) { + logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_route failed: %s",strerror(-status)); + exit(1); + } +} + +/* function: drop_root + * drops root privs but keeps the needed capability + */ +void drop_root() { + gid_t groups[] = { AID_INET }; + 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) { + tun_header->proto = ntohs(tun_header->proto); + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags); + } + + if(tun_header->proto == ETH_P_IP) { + ip_packet(tunnel->fd6,packet,packetsize); + } else if(tun_header->proto == ETH_P_IPV6) { + ipv6_packet(tunnel->fd4,packet,packetsize); + } else { + logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); + } +} + +/* 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; + } + + memcpy(&tun_header, packet, header_size); + + packet_handler(tunnel, &tun_header, 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); + } + + // 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); + } + + // forwarding slows down IPv6 config while transitioning to wifi + // forwarding also removes default routes learned from a RA + set_accept_ra(); + + // run under a regular user + drop_root(); + + if(signal(SIGTERM, got_sigterm) == SIG_ERR) { + logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); + exit(1); + } + + configure_interface(uplink_interface, plat_prefix, &tunnel); + + // turning on forwarding in linux has a side effect of calling rt6_purge_dflt_routers + // workaround: turn the RA-learned default route into a static route + set_default_ipv6_route(uplink_interface); + set_forwarding(forwarding_fd,"1\n"); + + event_loop(&tunnel); // event_loop returns if someone sets the tun interface down manually + + set_forwarding(forwarding_fd,"0\n"); + + return 0; +} diff --git a/clatd.conf b/clatd.conf new file mode 100644 index 0000000..e6336d7 --- /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 ::200:5E10:0:0 + +# ipv4 subnet for the local traffic to use. This is a /32 host address +ipv4_local_subnet 192.168.255.1 + +# get the plat_subnet from dns lookups (requires DNS64) +plat_from_dns64 yes +# hostname to use to lookup plat subnet. must contain only A records +plat_from_dns64_hostname ipv4.google.com + +# plat subnet to send ipv4 traffic to. This is a /96 subnet. +# This setting only makes sense with: plat_from_dns64 no +#plat_subnet 2001:db8:1:2:3:4:: diff --git a/clatd.h b/clatd.h new file mode 100644 index 0000000..00a4094 --- /dev/null +++ b/clatd.h @@ -0,0 +1,32 @@ +/* + * 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 + +#define MAXMTU 1500 +#define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) + +// 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 +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include + +#define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" +#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" + +struct clat_config { + int16_t mtu, ipv4mtu; + struct in6_addr ipv6_local_subnet; + struct in6_addr ipv6_host_id; + struct in_addr ipv4_local_subnet; + struct in6_addr plat_subnet; + char default_pdp_interface[PROP_VALUE_MAX]; + char *plat_from_dns64_hostname; +}; + +extern struct clat_config Global_Clatd_Config; + +int read_config(const char *file, const char *uplink_interface, const char *plat_prefix); +void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); + +#endif /* __CONFIG_H__ */ diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..ba4971b --- /dev/null +++ b/debug.h @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * debug.h - debug settings + */ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +// set to 1 to enable debug logging +#define CLAT_DEBUG 0 + +#endif /* __DEBUG_H__ */ diff --git a/dns64.c b/dns64.c new file mode 100644 index 0000000..ab662c7 --- /dev/null +++ b/dns64.c @@ -0,0 +1,91 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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; + } + + if(!IN6_ARE_ADDR_EQUAL(&plat_addr, &this_plat_addr)) { + char plat_addr_str[INET6_ADDRSTRLEN], this_plat_addr_str[INET6_ADDRSTRLEN]; + logmsg(ANDROID_LOG_ERROR,"plat_prefix/two different plat addrs = %s,%s", + inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)), + inet_ntop(AF_INET6, &this_plat_addr, this_plat_addr_str, sizeof(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; + } + *prefix = plat_addr; + return 1; +} diff --git a/dns64.h b/dns64.h new file mode 100644 index 0000000..f295e50 --- /dev/null +++ b/dns64.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * dns64.h - find the nat64 prefix with a dns64 lookup + */ +#ifndef __DNS64_H__ +#define __DNS64_H__ + +int plat_prefix(const char *ipv4_name, struct in6_addr *prefix); + +#endif diff --git a/dump.c b/dump.c new file mode 100644 index 0000000..3e4a6cc --- /dev/null +++ b/dump.c @@ -0,0 +1,225 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checksum.h" +#include "clatd.h" +#include "logging.h" + +/* 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); + 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); + 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 +#include +#include +#include + +#include +#include +#include + +#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 +#include + +#include +#include +#include +#include + +#include +#include + +#include "getroute.h" +#include "netlink_callbacks.h" +#include "netlink_msg.h" + +/* function: get_default_route_cb + * finds the default route with the request family and out interface and saves the gateway + * msg - netlink message + * data - (struct default_route_data) requested filters and response storage + */ +static int get_default_route_cb(struct nl_msg *msg, void *data) { + struct rtmsg *rt_p; + struct rtattr *rta_p; + int rta_len; + struct default_route_data *default_route = data; + union anyip *this_gateway = NULL; + ssize_t this_gateway_size; + int this_interface_id = -1; + + if(default_route->reply_found_route) { // we already found our route + return NL_OK; + } + + rt_p = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg)); + if(rt_p->rtm_dst_len != 0) { // not a default route + return NL_OK; + } + if((rt_p->rtm_family != default_route->request_family) || (rt_p->rtm_table != RT_TABLE_MAIN)) { // not a route we care about + return NL_OK; + } + + rta_p = (struct rtattr *)RTM_RTA(rt_p); + rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); + for(; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) { + switch(rta_p->rta_type) { + case RTA_GATEWAY: + this_gateway = RTA_DATA(rta_p); + this_gateway_size = RTA_PAYLOAD(rta_p); + break; + case RTA_OIF: + this_interface_id = *(int *)RTA_DATA(rta_p); + break; + default: + break; + } + } + + if(this_interface_id == default_route->request_interface_id) { + default_route->reply_found_route = 1; + if(this_gateway != NULL) { + memcpy(&default_route->reply_gateway, this_gateway, this_gateway_size); + default_route->reply_has_gateway = 1; + } else { + default_route->reply_has_gateway = 0; + } + } + return NL_OK; +} + +/* function: error_handler + * error callback for get_default_route + * nla - where the message came from + * err - netlink message + * arg - (int *) storage for the error number + */ +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + int *retval = arg; + if(err->error < 0) { // error_handler called even on no error (NLMSG_ERROR reply type used) + *retval = err->error; + } + return NL_OK; +} + +/* function: get_default_route + * finds the first default route with the given family and interface, returns the gateway (if it exists) in the struct + * default_route - requested family and interface, and response storage + */ +int get_default_route(struct default_route_data *default_route) { + struct rtmsg msg; + struct nl_cb *callbacks = NULL; + struct nl_msg *nlmsg = NULL; + int retval = 0; + + default_route->reply_has_gateway = 0; + default_route->reply_found_route = 0; + + memset(&msg,'\0',sizeof(msg)); + msg.rtm_family = default_route->request_family; + msg.rtm_table = RT_TABLE_MAIN; + msg.rtm_protocol = RTPROT_KERNEL; + msg.rtm_scope = RT_SCOPE_UNIVERSE; + + callbacks = nl_cb_alloc(NL_CB_DEFAULT); + if(!callbacks) { + retval = -ENOMEM; + goto cleanup; + } + // get_default_route_cb sets the response fields in default_route + nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, get_default_route_cb, default_route); + nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &retval); + + nlmsg = nlmsg_alloc_rtmsg(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ROOT, &msg); + if(!nlmsg) { + retval = -ENOMEM; + goto cleanup; + } + send_netlink_msg(nlmsg, callbacks); + +cleanup: + if(callbacks) + nl_cb_put(callbacks); + if(nlmsg) + nlmsg_free(nlmsg); + + return retval; +} diff --git a/getroute.h b/getroute.h new file mode 100644 index 0000000..e7b8670 --- /dev/null +++ b/getroute.h @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * getroute.h - get an ip route + */ +#ifndef __GETROUTE_H__ +#define __GETROUTE_H__ + +// for union anyip +#include "getaddr.h" + +struct default_route_data { + int request_interface_id; + int request_family; + + union anyip reply_gateway; + int reply_has_gateway; + int reply_found_route; +}; + +int get_default_route(struct default_route_data *default_route); + +#endif diff --git a/ipv4.c b/ipv4.c new file mode 100644 index 0000000..784f10c --- /dev/null +++ b/ipv4.c @@ -0,0 +1,187 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "translate.h" +#include "checksum.h" +#include "ipv4.h" +#include "logging.h" +#include "debug.h" + +/* function: icmp_packet + * takes an icmp packet and sets it up for translation + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip - ip header + */ +void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { + struct icmphdr icmp; + const char *payload; + size_t payload_size; + + if(len < sizeof(icmp)) { + logmsg_dbg(ANDROID_LOG_ERROR,"icmp_packet/(too small)"); + return; + } + + memcpy(&icmp, packet, sizeof(icmp)); + payload = packet + sizeof(icmp); + payload_size = len - sizeof(icmp); + + icmp_to_icmp6(fd,ip,&icmp,payload,payload_size); +} + +/* function: tcp_packet + * takes a tcp packet and sets it up for translation + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip - ip header + */ +void tcp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { + struct tcphdr tcp; + const char *payload; + const char *options; + size_t payload_size, options_size; + + if(len < sizeof(tcp)) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); + return; + } + + memcpy(&tcp, packet, sizeof(tcp)); + + if(tcp.doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x",tcp.doff); + return; + } + + if((size_t)tcp.doff*4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x",tcp.doff); + return; + } + + if(tcp.doff > 5) { + options = packet + sizeof(tcp); + options_size = tcp.doff*4 - sizeof(tcp); + } else { + options = NULL; + options_size = 0; + } + + payload = packet + tcp.doff*4; + payload_size = len - tcp.doff*4; + + tcp_to_tcp6(fd,ip,&tcp,payload,payload_size,options,options_size); +} + +/* function: udp_packet + * takes a udp packet and sets it up for translation + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip - ip header + */ +void udp_packet(int fd, const char *packet, size_t len, const struct iphdr *ip) { + struct udphdr udp; + const char *payload; + size_t payload_size; + + if(len < sizeof(udp)) { + logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)"); + return; + } + + memcpy(&udp, packet, sizeof(udp)); + payload = packet + sizeof(udp); + payload_size = len - sizeof(udp); + + udp_to_udp6(fd,ip,&udp,payload,payload_size); +} + +/* function: ip_packet + * takes an ip packet and hands it off to the layer 4 protocol function + * fd - tun interface fd + * packet - packet data + * len - size of packet + */ +void ip_packet(int fd, const char *packet, size_t len) { + struct iphdr header; + uint16_t frag_flags; + const char *next_header; + size_t len_left; + + if(len < sizeof(header)) { + logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/too short for an ip header"); + return; + } + + memcpy(&header, packet, sizeof(header)); + + 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; + } + + if(header.ihl < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set to less than 5: %x",header.ihl); + return; + } + + 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; + } + + if(header.version != 4) { + logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header version not 4: %x",header.version); + return; + } + + /* 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; + + if(header.protocol == IPPROTO_ICMP) { + icmp_packet(fd,next_header,len_left,&header); + } else if(header.protocol == IPPROTO_TCP) { + tcp_packet(fd,next_header,len_left,&header); + } else if(header.protocol == IPPROTO_UDP) { + udp_packet(fd,next_header,len_left,&header); + } else { +#if CLAT_DEBUG + logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/unknown protocol: %x",header.protocol); + logcat_hexdump("ipv4/protocol", packet, len); +#endif + } +} diff --git a/ipv4.h b/ipv4.h new file mode 100644 index 0000000..eef3f0c --- /dev/null +++ b/ipv4.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function + */ +#ifndef __IPV4_H__ +#define __IPV4_H__ + +void ip_packet(int fd, const char *packet, size_t len); + +#endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c new file mode 100644 index 0000000..8fc36a4 --- /dev/null +++ b/ipv6.c @@ -0,0 +1,194 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip6 - ip6 header + */ +void icmp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { + struct icmp6_hdr icmp6; + const char *payload; + size_t payload_size; + + if(len < sizeof(icmp6)) { + logmsg_dbg(ANDROID_LOG_ERROR,"icmp6_packet/(too small)"); + return; + } + + memcpy(&icmp6, packet, sizeof(icmp6)); + payload = packet + sizeof(icmp6); + payload_size = len - sizeof(icmp6); + + icmp6_to_icmp(fd, ip6, &icmp6, payload, payload_size); +} + +/* function: tcp6_packet + * takes a tcp packet and sets it up for translation + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip6 - ip6 header + */ +void tcp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { + struct tcphdr tcp; + const char *payload; + const char *options; + size_t payload_size, options_size; + + if(len < sizeof(tcp)) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/(too small)"); + return; + } + + memcpy(&tcp, packet, sizeof(tcp)); + + if(tcp.doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set to less than 5: %x",tcp.doff); + return; + } + + if((size_t)tcp.doff*4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set too large: %x",tcp.doff); + return; + } + + if(tcp.doff > 5) { + options = packet + sizeof(tcp); + options_size = tcp.doff*4 - sizeof(tcp); + } else { + options = NULL; + options_size = 0; + } + + payload = packet + tcp.doff*4; + payload_size = len - tcp.doff*4; + + tcp6_to_tcp(fd,ip6,&tcp,payload,payload_size,options,options_size); +} + +/* function: udp6_packet + * takes a udp packet and sets it up for translation + * fd - tun interface fd + * packet - ip payload + * len - size of ip payload + * ip6 - ip6 header + */ +void udp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { + struct udphdr udp; + const char *payload; + size_t payload_size; + + if(len < sizeof(udp)) { + logmsg_dbg(ANDROID_LOG_ERROR,"udp6_packet/(too small)"); + return; + } + + memcpy(&udp, packet, sizeof(udp)); + payload = packet + sizeof(udp); + payload_size = len - sizeof(udp); + + udp6_to_udp(fd,ip6,&udp,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 *badaddr) { +#if CLAT_DEBUG + char badaddr_str[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, badaddr, badaddr_str, sizeof(badaddr_str)); + logmsg_dbg(ANDROID_LOG_ERROR,fmt,badaddr_str); +#endif +} + +/* function: ipv6_packet + * takes an ipv6 packet and hands it off to the layer 4 protocol function + * fd - tun interface fd + * packet - packet data + * len - size of packet + */ +void ipv6_packet(int fd, const char *packet, size_t len) { + struct ip6_hdr header; + const char *next_header; + size_t len_left; + int i; + + if(len < sizeof(header)) { + logmsg_dbg(ANDROID_LOG_ERROR,"ipv6_packet/too short for an ip6 header"); + return; + } + + memcpy(&header, packet, sizeof(header)); + + next_header = packet + sizeof(header); + len_left = len - sizeof(header); + + if(IN6_IS_ADDR_MULTICAST(&header.ip6_dst)) { + log_bad_address("ipv6_packet/multicast %s", &header.ip6_dst); + return; // silently ignore + } + + for(i = 0; i < 3; i++) { + if(header.ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) { + log_bad_address("ipv6_packet/wrong source address: %s", &header.ip6_src); + return; + } + } + if(!IN6_ARE_ADDR_EQUAL(&header.ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) { + log_bad_address("ipv6_packet/wrong destination address: %s", &header.ip6_dst); + return; + } + + // does not support IPv6 extension headers, this will drop any packet with them + if(header.ip6_nxt == IPPROTO_ICMPV6) { + icmp6_packet(fd,next_header,len_left,&header); + } else if(header.ip6_nxt == IPPROTO_TCP) { + tcp6_packet(fd,next_header,len_left,&header); + } else if(header.ip6_nxt == IPPROTO_UDP) { + udp6_packet(fd,next_header,len_left,&header); + } else { +#if CLAT_DEBUG + logmsg(ANDROID_LOG_ERROR,"ipv6_packet/unknown next header type: %x",header.ip6_nxt); + logcat_hexdump("ipv6/nxthdr", packet, len); +#endif + } +} diff --git a/ipv6.h b/ipv6.h new file mode 100644 index 0000000..b898ad9 --- /dev/null +++ b/ipv6.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function + */ +#ifndef __IPV6_H__ +#define __IPV6_H__ + +void ipv6_packet(int fd, 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 +#include + +#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 + +void logmsg(int prio, const char *fmt, ...); +void logmsg_dbg(int prio, const char *fmt, ...); + +#endif diff --git a/mtu.c b/mtu.c new file mode 100644 index 0000000..975bf0e --- /dev/null +++ b/mtu.c @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * mtu.c - get interface mtu + */ + +#include +#include +#include +#include +#include +#include + +#include "mtu.h" + +/* function: getifmtu + * returns the interface mtu or -1 on failure + * ifname - interface name + */ +int getifmtu(const char *ifname) { + int fd; + struct ifreq if_mtu; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd < 0) { + return -1; + } + strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ); + if_mtu.ifr_name[IFNAMSIZ - 1] = '\0'; + if(ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { + return -1; + } + return if_mtu.ifr_mtu; +} diff --git a/mtu.h b/mtu.h new file mode 100644 index 0000000..c330c24 --- /dev/null +++ b/mtu.h @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * mtu.c - get interface mtu + */ + +#ifndef __MTU_H__ +#define __MTU_H__ + +int getifmtu(const char *ifname); + +#endif diff --git a/netlink_callbacks.c b/netlink_callbacks.c new file mode 100644 index 0000000..5e0f34e --- /dev/null +++ b/netlink_callbacks.c @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Daniel Drown + * + * 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 +#include + +#include +#include +#include + +/* 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 + * + * 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..7363028 --- /dev/null +++ b/netlink_msg.c @@ -0,0 +1,168 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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: 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; + + 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..bc85b2d --- /dev/null +++ b/netlink_msg.h @@ -0,0 +1,29 @@ +/* + * 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); + +#endif diff --git a/setif.c b/setif.c new file mode 100644 index 0000000..5ee00bc --- /dev/null +++ b/setif.c @@ -0,0 +1,129 @@ +/* + * Copyright 2012 Daniel Drown + * + * 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 +#include +#include + +#include +#include +#include + +#include "netlink_msg.h" + +/* function: add_address + * adds an IP address to/from an interface, returns 0 on success and <0 on failure + * ifname - name of interface to change + * family - address family (AF_INET, AF_INET6) + * address - pointer to a struct in_addr or in6_addr + * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) + * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) + */ +int add_address(const char *ifname, int family, const void *address, int prefixlen, const void *broadcast) { + int retval; + size_t addr_size; + struct ifaddrmsg ifa; + struct nl_msg *msg = NULL; + + addr_size = inet_family_size(family); + if(addr_size == 0) { + retval = -EAFNOSUPPORT; + goto cleanup; + } + + memset(&ifa, 0, sizeof(ifa)); + if (!(ifa.ifa_index = if_nametoindex(ifname))) { + retval = -ENODEV; + goto cleanup; + } + ifa.ifa_family = family; + ifa.ifa_prefixlen = prefixlen; + ifa.ifa_scope = RT_SCOPE_UNIVERSE; + + msg = nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); + if(!msg) { + retval = -ENOMEM; + goto cleanup; + } + + if(nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { + retval = -ENOMEM; + goto cleanup; + } + if(family == AF_INET6) { + // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS + if(nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { + retval = -ENOMEM; + goto cleanup; + } + } else if(family == AF_INET) { + // AF_INET gets IFA_LOCAL + IFA_BROADCAST + if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { + retval = -ENOMEM; + goto cleanup; + } + } else { + retval = -EAFNOSUPPORT; + goto cleanup; + } + + retval = netlink_sendrecv(msg); + +cleanup: + if(msg) + nlmsg_free(msg); + + return retval; +} + +/* function: if_up + * sets interface link state to up and sets mtu, returns 0 on success and <0 on failure + * ifname - interface name to change + * mtu - new mtu + */ +int if_up(const char *ifname, int mtu) { + int retval = -1; + struct ifinfomsg ifi; + struct nl_msg *msg = NULL; + + memset(&ifi, 0, sizeof(ifi)); + if (!(ifi.ifi_index = if_nametoindex(ifname))) { + retval = -ENODEV; + goto cleanup; + } + ifi.ifi_change = IFF_UP; + ifi.ifi_flags = IFF_UP; + + msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); + if(!msg) { + retval = -ENOMEM; + goto cleanup; + } + + if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { + retval = -ENOMEM; + goto cleanup; + } + + retval = netlink_sendrecv(msg); + +cleanup: + if(msg) + nlmsg_free(msg); + + return retval; +} diff --git a/setif.h b/setif.h new file mode 100644 index 0000000..7f83f73 --- /dev/null +++ b/setif.h @@ -0,0 +1,24 @@ +/* + * Copyright 2012 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * setif.h - network interface configuration + */ +#ifndef __SETIF_H__ +#define __SETIF_H__ + +int add_address(const char *ifname, int family, const void *address, int cidr, const void *broadcast); +int if_up(const char *ifname, int mtu); + +#endif diff --git a/setroute.c b/setroute.c new file mode 100644 index 0000000..3edfe39 --- /dev/null +++ b/setroute.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. + * + * setroute.c - network route configuration + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; +} + +/* function: set_default_ipv6_route + * copies the default route on an interface (turns an RA route into a static + * route), which is needed to keep the route when forwarding is turned on + * device - interface to be the default route + */ +void set_default_ipv6_route(const char *device) { + struct in6_addr default_6 = IN6ADDR_ANY_INIT; + struct default_route_data default_route; + int status; + void *gateway = NULL; + + memset(&default_route, '\0', sizeof(default_route)); + default_route.request_family = AF_INET6; + default_route.request_interface_id = if_nametoindex(device); + if(default_route.request_interface_id == 0) { + logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route failed: no interface %s found", device); + exit(1); + } + + status = get_default_route(&default_route); + if(status < 0) { + logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: returned %d", status); + exit(1); + } + + if(!default_route.reply_found_route) { + logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: no default route found for %s", device); + exit(1); + } + + if(default_route.reply_has_gateway) { + gateway = &default_route.reply_gateway.ip6; + } + + if((status = if_route(device, AF_INET6, &default_6, 0, gateway, 1, 0, ROUTE_REPLACE)) < 0) { + if(status == -EEXIST) { + logmsg(ANDROID_LOG_WARN,"set_default_ipv6_route/if_route failed due to the route already existing"); + } else { + logmsg(ANDROID_LOG_FATAL,"set_default_ipv6_route/if_route failed: %s",strerror(-status)); + exit(1); + } + } +} diff --git a/setroute.h b/setroute.h new file mode 100644 index 0000000..d0e7d79 --- /dev/null +++ b/setroute.h @@ -0,0 +1,28 @@ +/* + * 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. + * + * 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); +void set_default_ipv6_route(const char *device); + +#endif diff --git a/translate.c b/translate.c new file mode 100644 index 0000000..cdaca14 --- /dev/null +++ b/translate.c @@ -0,0 +1,404 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checksum.h" +#include "clatd.h" +#include "config.h" +#include "logging.h" +#include "debug.h" + +/* 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: ipv6_src_to_ipv4_src + * return the corresponding ipv4 address for the given ipv6 address + * sourceaddr - ipv6 source address + */ +uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) { + // assumes a /96 plat subnet + return sourceaddr->s6_addr32[3]; +} + +/* function: fill_ip_header + * generating an ipv4 header from an ipv6 header (called by the layer 4 protocol-specific functions) + * ip_targ - (ipv4) target packet header, source addr: original ipv4 addr, dest addr: local subnet addr + * payload_len - length of other data inside packet + * protocol - protocol number (tcp, udp, etc) + * old_header - (ipv6) source packet header, source addr: nat64 prefix, dest addr: local subnet prefix + */ +void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol, const struct ip6_hdr *old_header) { + memset(ip_targ, 0, sizeof(ip_targ)); + + ip_targ->ihl = 5; + ip_targ->version = 4; + ip_targ->tos = 0; + ip_targ->tot_len = htons(sizeof(struct iphdr) + payload_len); + ip_targ->id = 0; + ip_targ->frag_off = htons(IP_DF); + ip_targ->ttl = old_header->ip6_hlim; + ip_targ->protocol = protocol; + ip_targ->check = 0; + + ip_targ->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src); + ip_targ->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr; + + ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); +} + +/* function: ipv4_dst_to_ipv6_dst + * return the corresponding ipv6 address for the given ipv4 address + * destination - ipv4 destination address (network byte order) + */ +struct in6_addr ipv4_dst_to_ipv6_dst(uint32_t destination) { + struct in6_addr v6_destination; + + // assumes a /96 plat subnet + v6_destination = Global_Clatd_Config.plat_subnet; + v6_destination.s6_addr32[3] = destination; + + return v6_destination; +} + +/* function: fill_ip6_header + * generating an ipv6 header from an ipv4 header (called by the layer 4 protocol-specific functions) + * ip6 - (ipv6) target packet header, source addr: local subnet prefix, dest addr: nat64 prefix + * payload_len - length of other data inside packet + * protocol - protocol number (tcp, udp, etc) + * old_header - (ipv4) source packet header, source addr: local subnet addr, dest addr: 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 = Global_Clatd_Config.ipv6_local_subnet; + ip6->ip6_dst = ipv4_dst_to_ipv6_dst(old_header->daddr); +} + +/* function: icmp_to_icmp6 + * translate ipv4 icmp to ipv6 icmp (only currently supports echo/echo reply) + * fd - tun interface fd + * ip - source packet ipv4 header + * icmp - source packet icmp header + * payload - icmp payload + * payload_size - size of payload + */ +void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size) { + struct ip6_hdr ip6_targ; + struct icmp6_hdr icmp6_targ; + struct iovec io_targ[4]; + struct tun_pi tun_header; + uint32_t checksum_temp; + + if((icmp->type != ICMP_ECHO) && (icmp->type != ICMP_ECHOREPLY)) { + logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x",icmp->type); + return; + } + + fill_tun_header(&tun_header,ETH_P_IPV6); + + fill_ip6_header(&ip6_targ,payload_size + sizeof(icmp6_targ),IPPROTO_ICMPV6,ip); + + memset(&icmp6_targ, 0, sizeof(icmp6_targ)); + icmp6_targ.icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; + icmp6_targ.icmp6_code = 0; + icmp6_targ.icmp6_cksum = 0; + icmp6_targ.icmp6_id = icmp->un.echo.id; + icmp6_targ.icmp6_seq = icmp->un.echo.sequence; + + checksum_temp = ipv6_pseudo_header_checksum(0,&ip6_targ); + checksum_temp = ip_checksum_add(checksum_temp, &icmp6_targ, sizeof(icmp6_targ)); + checksum_temp = ip_checksum_add(checksum_temp, payload, payload_size); + icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip6_targ; + io_targ[1].iov_len = sizeof(ip6_targ); + io_targ[2].iov_base = &icmp6_targ; + io_targ[2].iov_len = sizeof(icmp6_targ); + io_targ[3].iov_base = (char *)payload; + io_targ[3].iov_len = payload_size; + + writev(fd, io_targ, 4); +} + +/* function: icmp6_to_icmp + * translate ipv6 icmp to ipv4 icmp (only currently supports echo/echo reply) + * fd - tun interface fd + * ip6 - source packet ipv6 header + * icmp6 - source packet icmp6 header + * payload - icmp6 payload + * payload_size - size of payload + */ +void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { + struct iphdr ip_targ; + struct icmphdr icmp_targ; + struct iovec io_targ[4]; + struct tun_pi tun_header; + uint32_t temp_icmp_checksum; + + if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) { + logmsg_dbg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type); + return; + } + + memset(&icmp_targ, 0, sizeof(icmp_targ)); + + fill_tun_header(&tun_header,ETH_P_IP); + fill_ip_header(&ip_targ,sizeof(icmp_targ) + payload_size, IPPROTO_ICMP, ip6); + + icmp_targ.type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; + icmp_targ.code = 0x0; + icmp_targ.checksum = 0; + icmp_targ.un.echo.id = icmp6->icmp6_id; + icmp_targ.un.echo.sequence = icmp6->icmp6_seq; + + temp_icmp_checksum = ip_checksum_add(0, &icmp_targ, sizeof(icmp_targ)); + temp_icmp_checksum = ip_checksum_add(temp_icmp_checksum, (void *)payload, payload_size); + icmp_targ.checksum = ip_checksum_finish(temp_icmp_checksum); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip_targ; + io_targ[1].iov_len = sizeof(ip_targ); + io_targ[2].iov_base = &icmp_targ; + io_targ[2].iov_len = sizeof(icmp_targ); + io_targ[3].iov_base = (char *)payload; + io_targ[3].iov_len = payload_size; + + writev(fd, io_targ, 4); +} + +/* function: udp_translate + * common between ipv4/ipv6 - setup checksum and send udp packet + * fd - tun interface fd + * udp - source packet udp header + * payload - udp payload + * payload_size - size of payload + * io_targ - iovec with tun and ipv4/ipv6 header (see below) + * array position 0 - tun header + * array position 1 - ipv4/ipv6 header + * array position 2 - empty (will be udp header) + * array position 3 - empty (will be payload) + * checksum - partial checksum covering ipv4/ipv6 header + */ +void udp_translate(int fd, const struct udphdr *udp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum) { + struct udphdr udp_targ; + + memcpy(&udp_targ, udp, sizeof(udp_targ)); + udp_targ.check = 0; // reset checksum, to be calculated + + checksum = ip_checksum_add(checksum, &udp_targ, sizeof(struct udphdr)); + checksum = ip_checksum_add(checksum, payload, payload_size); + udp_targ.check = ip_checksum_finish(checksum); + + io_targ[2].iov_base = &udp_targ; + io_targ[2].iov_len = sizeof(udp_targ); + io_targ[3].iov_base = (char *)payload; + io_targ[3].iov_len = payload_size; + + writev(fd, io_targ, 4); +} + +/* function: udp_to_udp6 + * translate ipv4 udp to ipv6 udp + * fd - tun interface fd + * ip - source packet ipv4 header + * udp - source packet udp header + * payload - udp payload + * payload_size - size of payload + */ +void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size) { + struct ip6_hdr ip6_targ; + struct iovec io_targ[4]; + struct tun_pi tun_header; + uint32_t checksum; + + fill_tun_header(&tun_header,ETH_P_IPV6); + + fill_ip6_header(&ip6_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip); + + checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip6_targ; + io_targ[1].iov_len = sizeof(ip6_targ); + + udp_translate(fd,udp,payload,payload_size,io_targ,checksum); +} + +/* function: udp6_to_udp + * translate ipv6 udp to ipv4 udp + * fd - tun interface fd + * ip6 - source packet ipv6 header + * udp - source packet udp header + * payload - udp payload + * payload_size - size of payload + */ +void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size) { + struct iphdr ip_targ; + struct iovec io_targ[4]; + struct tun_pi tun_header; + uint32_t checksum; + + fill_tun_header(&tun_header,ETH_P_IP); + + fill_ip_header(&ip_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip6); + + checksum = ipv4_pseudo_header_checksum(0, &ip_targ); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip_targ; + io_targ[1].iov_len = sizeof(ip_targ); + + udp_translate(fd,udp,payload,payload_size,io_targ,checksum); +} + +/* function: tcp_translate + * common between ipv4/ipv6 - setup checksum and send tcp packet + * fd - tun interface fd + * tcp - source packet tcp header + * payload - tcp payload + * payload_size - size of payload + * io_targ - iovec with tun and ipv4/ipv6 header (see below) + * array position 0 - tun header + * array position 1 - ipv4/ipv6 header + * array position 2 - empty (will be tcp header) + * array position 3 - empty (will be tcp options or payload) + * array position 4 - empty (can be payload) + * checksum - partial checksum covering ipv4/ipv6 header + * options - pointer to tcp option buffer + * options_size - size of tcp option buffer + * + * TODO: mss rewrite + * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) + */ +void tcp_translate(int fd, const struct tcphdr *tcp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum, const char *options, size_t options_size) { + struct tcphdr tcp_targ; + int targ_index = 2; + + memcpy(&tcp_targ, tcp, sizeof(tcp_targ)); + tcp_targ.check = 0; + + checksum = ip_checksum_add(checksum, &tcp_targ, sizeof(tcp_targ)); + if(options) { + checksum = ip_checksum_add(checksum, options, options_size); + } + checksum = ip_checksum_add(checksum, payload, payload_size); + tcp_targ.check = ip_checksum_finish(checksum); + + io_targ[targ_index].iov_base = &tcp_targ; + io_targ[targ_index].iov_len = sizeof(tcp_targ); + targ_index++; + + if(options) { + io_targ[targ_index].iov_base = (char *)options; + io_targ[targ_index].iov_len = options_size; + targ_index++; + } + + io_targ[targ_index].iov_base = (char *)payload; + io_targ[targ_index].iov_len = payload_size; + targ_index++; + + writev(fd, io_targ, targ_index); +} + +/* function: tcp_to_tcp6 + * translate ipv4 tcp to ipv6 tcp + * fd - tun interface fd + * ip - source packet ipv4 header + * tcp - source packet tcp header + * payload - tcp payload + * payload_size - size of payload + * options - tcp options + * options_size - size of options + */ +void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { + struct ip6_hdr ip6_targ; + struct iovec io_targ[5]; + struct tun_pi tun_header; + uint32_t checksum; + + fill_tun_header(&tun_header,ETH_P_IPV6); + + fill_ip6_header(&ip6_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip); + + checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip6_targ; + io_targ[1].iov_len = sizeof(ip6_targ); + + tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); +} + +/* function: tcp6_to_tcp + * translate ipv6 tcp to ipv4 tcp + * fd - tun interface fd + * ip6 - source packet ipv6 header + * tcp - source packet tcp header + * payload - tcp payload + * payload_size - size of payload + * options - tcp options + * options_size - size of options + */ +void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { + struct iphdr ip_targ; + struct iovec io_targ[5]; + struct tun_pi tun_header; + uint32_t checksum; + + fill_tun_header(&tun_header,ETH_P_IP); + + fill_ip_header(&ip_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip6); + + checksum = ipv4_pseudo_header_checksum(0, &ip_targ); + + io_targ[0].iov_base = &tun_header; + io_targ[0].iov_len = sizeof(tun_header); + io_targ[1].iov_base = &ip_targ; + io_targ[1].iov_len = sizeof(ip_targ); + + tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); +} diff --git a/translate.h b/translate.h new file mode 100644 index 0000000..6fc3c79 --- /dev/null +++ b/translate.h @@ -0,0 +1,30 @@ +/* + * 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__ + +void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size); +void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size); + +void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size); +void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size); + +void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size); +void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size); + +#endif /* __TRANSLATE_H__ */ -- cgit v1.2.3 From 70aba57df0f4a0ee800e7ba3694dbd9b6302a470 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 13 Nov 2012 19:27:14 -0800 Subject: build and include for debug builds. The builds the clatd and adds the needed conf file. Change-Id: Idb45a7b8b712ffaedddee9f3ca73d44001eaba81 --- Android.mk | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index b1238ce..6264b5a 100644 --- a/Android.mk +++ b/Android.mk @@ -7,8 +7,19 @@ LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils -LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_TAGS := debug 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) +LOCAL_MODULE_TAGS := debug + +include $(BUILD_PREBUILT) -- cgit v1.2.3 From f68200ab1a5c559866a87e51d0d52e3c74b02343 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Feb 2013 10:48:29 +0900 Subject: Don't compile packet dumping code by default. Bug: 7664960 Change-Id: Ieb56bcc010d0e12264d43e0a0dfcb1beb479d4e2 --- debug.h | 2 +- dump.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/debug.h b/debug.h index ba4971b..8e09672 100644 --- a/debug.h +++ b/debug.h @@ -18,7 +18,7 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ -// set to 1 to enable debug logging +// set to 1 to enable debug logging and packet dumping. #define CLAT_DEBUG 0 #endif /* __DEBUG_H__ */ diff --git a/dump.c b/dump.c index 3e4a6cc..e538f3e 100644 --- a/dump.c +++ b/dump.c @@ -28,10 +28,13 @@ #include #include +#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; @@ -223,3 +226,5 @@ void logcat_hexdump(const char *info, const char *data, size_t len) { logmsg(ANDROID_LOG_WARN,"info %s len %d data %s", info, len, output); } + +#endif // CLAT_DEBUG -- cgit v1.2.3 From 4f3d78640de4573ced186edd3a09b1247d981edf Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Feb 2013 13:18:35 +0900 Subject: Ensure netlink messages come from the kernel. Currently clatd accepts all netlink messages without validating that they come from the kernel. This could allow another app to spoof these messages. Fix this by connecting the socket to the kernel so that no other process can send it messages. Bug: 7664960 Change-Id: I994641ea13cfd07fb25ccf52fcbbf5d1c9633ec4 --- netlink_msg.c | 18 ++++++++++++++++++ netlink_msg.h | 1 + 2 files changed, 19 insertions(+) diff --git a/netlink_msg.c b/netlink_msg.c index 7363028..2ba237d 100644 --- a/netlink_msg.c +++ b/netlink_msg.c @@ -103,6 +103,21 @@ 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 @@ -121,6 +136,9 @@ void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) { 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: diff --git a/netlink_msg.h b/netlink_msg.h index bc85b2d..13e1f28 100644 --- a/netlink_msg.h +++ b/netlink_msg.h @@ -25,5 +25,6 @@ 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 -- cgit v1.2.3 From d6ef91bef28020ec019a64561e8fb5e353e42980 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 26 Feb 2013 09:44:17 +0900 Subject: Change the IP addresses used by CLAT. - Change the IPv4 address to 192.0.0.4. This is one of the IPv4 addresses reserved by IANA for DS-Lite, so it's close enough. - Change the IPv6 IID to ::464. The current code uses an IID in the IANA reserved range. Change-Id: I6039f13efc7d20c433b03cd322fc82a3a45b84cf --- clatd.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clatd.conf b/clatd.conf index e6336d7..086d39a 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1,9 +1,9 @@ # 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 ::200:5E10:0:0 +ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address -ipv4_local_subnet 192.168.255.1 +ipv4_local_subnet 192.0.0.4 # get the plat_subnet from dns lookups (requires DNS64) plat_from_dns64 yes -- cgit v1.2.3 From 2edb7567d3d5373617c236801766e928c6a3dbcb Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 28 Feb 2013 14:15:22 -0800 Subject: clatd: use correct header file. Change-Id: I8e7130c1c9b7d6397bc5e705e58667fb7b37b598 --- clatd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 17a3548..06dcdb3 100644 --- a/clatd.c +++ b/clatd.c @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3 From baf62997b598ac38a99fd30419ccf7c109ffb3bc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Mar 2013 20:29:39 +0900 Subject: Make clatd a bit more robust when started by netd. 1. When started from netd, DNS lookups (used to detect the NAT64 prefix) fail because ANDROID_DNS_MODE=local. Unset it. 2. Only add the SIGTERM handler just before starting the event loop. Otherwise, if clatd hangs before the event loop is started (e.g., when #1 happens), it can't be stopped. While I'm at it, add a couple of logging statements. Change-Id: Ie24b37e34b729ce6cd3769b5d64348f2c1b9627d --- clatd.c | 22 ++++++++++++++-------- dns64.c | 13 +++++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/clatd.c b/clatd.c index 17a3548..74aabcb 100644 --- a/clatd.c +++ b/clatd.c @@ -109,10 +109,10 @@ void set_accept_ra() { } } -/* function: got_sigterm - * signal handler: mark it time to clean up +/* function: stop_loop + * signal handler: stop the event loop */ -void got_sigterm(int signal) { +void stop_loop(int signal) { running = 0; } @@ -474,6 +474,7 @@ int main(int argc, char **argv) { printf("I need an interface\n"); exit(1); } + logmsg(ANDROID_LOG_INFO,"Starting clat on %s", uplink_interface); // open the tunnel device before dropping privs tunnel.fd6 = tun_open(); @@ -502,10 +503,9 @@ int main(int argc, char **argv) { // run under a regular user drop_root(); - if(signal(SIGTERM, got_sigterm) == SIG_ERR) { - logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); - exit(1); - } + // When run from netd, the environment variable ANDROID_DNS_MODE is set to + // "local", but that only works for the netd process itself. + unsetenv("ANDROID_DNS_MODE"); configure_interface(uplink_interface, plat_prefix, &tunnel); @@ -514,9 +514,15 @@ int main(int argc, char **argv) { set_default_ipv6_route(uplink_interface); set_forwarding(forwarding_fd,"1\n"); - event_loop(&tunnel); // event_loop returns if someone sets the tun interface down manually + // 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/dns64.c b/dns64.c index ab662c7..b139fe4 100644 --- a/dns64.c +++ b/dns64.c @@ -38,6 +38,9 @@ int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { 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; @@ -71,12 +74,12 @@ int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { 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 plat_addr_str[INET6_ADDRSTRLEN], this_plat_addr_str[INET6_ADDRSTRLEN]; + 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", - inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)), - inet_ntop(AF_INET6, &this_plat_addr, this_plat_addr_str, sizeof(this_plat_addr_str)) - ); + plat_addr_str,this_plat_addr_str); } } if(result != NULL) { @@ -86,6 +89,8 @@ int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { 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; } -- cgit v1.2.3 From 1373eac3f8305f6ad834ba8e3fc4f1a75cd8ac5a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 4 Mar 2013 18:13:40 +0900 Subject: Stop setting accept_ra, now that netd does it. Bug: 8276725 Change-Id: Iedbd34bc94f678afe51429078e6c06ff144e6373 --- clatd.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/clatd.c b/clatd.c index b46c355..203714c 100644 --- a/clatd.c +++ b/clatd.c @@ -79,36 +79,6 @@ void set_forwarding(int fd, const char *setting) { } } -/* function: set_accept_ra - * accepts IPv6 RAs on all interfaces, even when forwarding is on - */ -void set_accept_ra() { - int fd, i; - const char *interface_names[] = {"wlan0","default",NULL}; - const char ipv6_sysctl[] = "/proc/sys/net/ipv6/conf/"; - const char accept_ra[] = "/accept_ra"; - - for(i = 0; interface_names[i]; i++) { - ssize_t sysctl_path_len = strlen(ipv6_sysctl)+strlen(interface_names[i])+strlen(accept_ra)+1; - char *sysctl_path = malloc(sysctl_path_len); - if(!sysctl_path) { - logmsg(ANDROID_LOG_FATAL,"set_accept_ra: malloc failed"); - exit(1); - } - snprintf(sysctl_path, sysctl_path_len, "%s%s%s", ipv6_sysctl, interface_names[i], accept_ra); - - fd = open(sysctl_path, O_RDWR); - free(sysctl_path); - if(fd < 0) { - continue; - } - if(write(fd, "2\n", 2) < 0) { - logmsg(ANDROID_LOG_WARN,"write to (%s)accept_ra failed: %s",interface_names[i],strerror(errno)); - } - close(fd); - } -} - /* function: stop_loop * signal handler: stop the event loop */ @@ -496,10 +466,6 @@ int main(int argc, char **argv) { exit(1); } - // forwarding slows down IPv6 config while transitioning to wifi - // forwarding also removes default routes learned from a RA - set_accept_ra(); - // run under a regular user drop_root(); -- cgit v1.2.3 From 678a9b4bcf78d062675c9543323ad89f0342136c Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 5 Mar 2013 12:01:12 +0900 Subject: Remove code to copy the default route. This is no longer necessary now that the kernel removes it for us when accept_ra = 2. Bug: 8276725 Change-Id: If16d9fbc63af2829f1ee4c87031f895f15a3b3d7 --- clatd.c | 3 --- setroute.c | 44 -------------------------------------------- setroute.h | 1 - 3 files changed, 48 deletions(-) diff --git a/clatd.c b/clatd.c index 203714c..f826d46 100644 --- a/clatd.c +++ b/clatd.c @@ -475,9 +475,6 @@ int main(int argc, char **argv) { configure_interface(uplink_interface, plat_prefix, &tunnel); - // turning on forwarding in linux has a side effect of calling rt6_purge_dflt_routers - // workaround: turn the RA-learned default route into a static route - set_default_ipv6_route(uplink_interface); set_forwarding(forwarding_fd,"1\n"); // Loop until someone sends us a signal or brings down the tun interface. diff --git a/setroute.c b/setroute.c index 3edfe39..f264387 100644 --- a/setroute.c +++ b/setroute.c @@ -140,47 +140,3 @@ cleanup: return retval; } - -/* function: set_default_ipv6_route - * copies the default route on an interface (turns an RA route into a static - * route), which is needed to keep the route when forwarding is turned on - * device - interface to be the default route - */ -void set_default_ipv6_route(const char *device) { - struct in6_addr default_6 = IN6ADDR_ANY_INIT; - struct default_route_data default_route; - int status; - void *gateway = NULL; - - memset(&default_route, '\0', sizeof(default_route)); - default_route.request_family = AF_INET6; - default_route.request_interface_id = if_nametoindex(device); - if(default_route.request_interface_id == 0) { - logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route failed: no interface %s found", device); - exit(1); - } - - status = get_default_route(&default_route); - if(status < 0) { - logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: returned %d", status); - exit(1); - } - - if(!default_route.reply_found_route) { - logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: no default route found for %s", device); - exit(1); - } - - if(default_route.reply_has_gateway) { - gateway = &default_route.reply_gateway.ip6; - } - - if((status = if_route(device, AF_INET6, &default_6, 0, gateway, 1, 0, ROUTE_REPLACE)) < 0) { - if(status == -EEXIST) { - logmsg(ANDROID_LOG_WARN,"set_default_ipv6_route/if_route failed due to the route already existing"); - } else { - logmsg(ANDROID_LOG_FATAL,"set_default_ipv6_route/if_route failed: %s",strerror(-status)); - exit(1); - } - } -} diff --git a/setroute.h b/setroute.h index d0e7d79..58f61cf 100644 --- a/setroute.h +++ b/setroute.h @@ -23,6 +23,5 @@ #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); -void set_default_ipv6_route(const char *device); #endif -- cgit v1.2.3 From f2d2abde321c3b2d35edc2436be83611a75c576e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 16 Mar 2013 09:42:30 +0900 Subject: Remove debug tag, as clatd is now built by default https://googleplex-android-review.googlesource.com/285000 adds clatd to generic_no_telephony.mk, so clatd will be included on both user and debug builds. Bug: 8276725 Change-Id: I4b5292b6c48740f2ee3ac246f8ccd11c6ee3fdd8 --- Android.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/Android.mk b/Android.mk index 6264b5a..c31a361 100644 --- a/Android.mk +++ b/Android.mk @@ -7,8 +7,6 @@ LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils -LOCAL_MODULE_TAGS := debug - LOCAL_MODULE := clatd include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 41e758e33e6c3b43f363591d4113dea54a4330d9 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 13 Mar 2013 12:26:35 +0900 Subject: Don't add the IPv4 default route in clatd. The IPv4 default route is now added by ConnectivityService because the stacked LinkProperties object contains it. Bug: 8276725 Change-Id: I6513bfa4df29b8663cefd30ed0bae1a80b63447c --- clatd.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clatd.c b/clatd.c index f826d46..ea8363c 100644 --- a/clatd.c +++ b/clatd.c @@ -213,13 +213,6 @@ void configure_tun_ip(const struct tun_data *tunnel) { } configure_tun_ipv6(tunnel); - - /* setup default ipv4 route */ - status = if_route(tunnel->device4, AF_INET, &default_4, 0, NULL, 1, 0, ROUTE_REPLACE); - if(status < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_route failed: %s",strerror(-status)); - exit(1); - } } /* function: drop_root -- cgit v1.2.3 From 3ca0302031b14c6fcb9be147e875fb794bfc583b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 27 Mar 2013 12:39:10 +0900 Subject: Configure the interface before bringing it up Currently, clatd brings the clat4 interface up before assigning its IPv4 address. This can cause a race condition, because as soon as the interface comes up, the framework notices and reads the interface configuration into the LinkProperties. If this happens before the IPv4 address is configured, then the framework ends up thinking clat4's IPv4 address is 0.0.0.0/0. Fix this by configuring the address before the interface is brought up. Currently the framework does not use this address for anything, so this is purely cosmetic, but it could make debugging more confusing. Bug: 8276725 Change-Id: I2bfee586a0d70050c53b10cc3f7eb9a98173e11d --- clatd.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/clatd.c b/clatd.c index ea8363c..edd8a2d 100644 --- a/clatd.c +++ b/clatd.c @@ -192,25 +192,26 @@ void interface_poll(const struct tun_data *tunnel) { * tunnel - tun device data */ void configure_tun_ip(const struct tun_data *tunnel) { - struct in_addr default_4; int status; - default_4.s_addr = INADDR_ANY; + // 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); } - 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); - } configure_tun_ipv6(tunnel); } -- cgit v1.2.3 From 0278627f576832860af2d84e04e383ecaa92d74f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 18:02:24 +0900 Subject: Pass in the length to pseudo checksum functions Currently, the pseudo-header checksum calculation functions get the transport layer length from the IP header. This requires that the length be known at IP header construction time, which does not allow transport layer translation functions to change the length of the packet later. Have the transport functions pass in the size directly. Bug: 8276725 Change-Id: I76a93f5e66181bec21d68f779c68c54090a77c33 --- checksum.c | 44 +++++++++++++++++++++++--------------------- checksum.h | 6 +++--- dump.c | 8 ++++---- translate.c | 10 +++++----- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/checksum.c b/checksum.c index 14b7113..a4dc9b8 100644 --- a/checksum.c +++ b/checksum.c @@ -28,12 +28,12 @@ /* function: ip_checksum_add * adds data to a checksum - * current_sum - the current checksum (or 0 to start a new 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_sum, const void *data, int len) { - uint32_t checksum = current_sum; +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; @@ -76,38 +76,40 @@ uint16_t ip_checksum(const void *data, int len) { /* function: ipv6_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp/icmp headers - * current_sum - the current checksum or 0 to start a new checksum - * ip6 - the ipv6 header + * 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_sum, const struct ip6_hdr *ip6) { +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(ntohs(ip6->ip6_plen)); + checksum_len = htonl((uint32_t) len); checksum_next = htonl(ip6->ip6_nxt); - current_sum = ip_checksum_add(current_sum,&(ip6->ip6_src),sizeof(struct in6_addr)); - current_sum = ip_checksum_add(current_sum,&(ip6->ip6_dst),sizeof(struct in6_addr)); - current_sum = ip_checksum_add(current_sum,&checksum_len,sizeof(checksum_len)); - current_sum = ip_checksum_add(current_sum,&checksum_next,sizeof(checksum_next)); + 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_sum; + return current; } /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers - * current_sum - the current checksum or 0 to start a new checksum - * ip - the ipv4 header + * 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_sum, const struct iphdr *ip) { +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(ntohs(ip->tot_len) - ip->ihl*4); + temp_length = htons(len); - current_sum = ip_checksum_add(current_sum, &(ip->saddr), sizeof(uint32_t)); - current_sum = ip_checksum_add(current_sum, &(ip->daddr), sizeof(uint32_t)); - current_sum = ip_checksum_add(current_sum, &temp_protocol, sizeof(uint16_t)); - current_sum = ip_checksum_add(current_sum, &temp_length, sizeof(uint16_t)); + 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_sum; + return current; } diff --git a/checksum.h b/checksum.h index c8346a0..473f5f5 100644 --- a/checksum.h +++ b/checksum.h @@ -18,11 +18,11 @@ #ifndef __CHECKSUM_H__ #define __CHECKSUM_H__ -uint32_t ip_checksum_add(uint32_t current_sum, const void *data, int len); +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_sum, const struct ip6_hdr *ip6); -uint32_t ipv4_pseudo_header_checksum(uint32_t current_sum, const struct iphdr *ip); +uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); +uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); #endif /* __CHECKSUM_H__ */ diff --git a/dump.c b/dump.c index e538f3e..fe9721a 100644 --- a/dump.c +++ b/dump.c @@ -146,14 +146,14 @@ void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const ch /* 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); + 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); + temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } @@ -202,7 +202,7 @@ void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t opti 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); + 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); } @@ -210,7 +210,7 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payl 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); + 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); } diff --git a/translate.c b/translate.c index cdaca14..89a7e7b 100644 --- a/translate.c +++ b/translate.c @@ -142,7 +142,7 @@ void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, c icmp6_targ.icmp6_id = icmp->un.echo.id; icmp6_targ.icmp6_seq = icmp->un.echo.sequence; - checksum_temp = ipv6_pseudo_header_checksum(0,&ip6_targ); + checksum_temp = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(icmp6_targ) + payload_size); checksum_temp = ip_checksum_add(checksum_temp, &icmp6_targ, sizeof(icmp6_targ)); checksum_temp = ip_checksum_add(checksum_temp, payload, payload_size); icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp); @@ -255,7 +255,7 @@ void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const fill_ip6_header(&ip6_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip); - checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); + checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(*udp) + payload_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); @@ -283,7 +283,7 @@ void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, co fill_ip_header(&ip_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip6); - checksum = ipv4_pseudo_header_checksum(0, &ip_targ); + checksum = ipv4_pseudo_header_checksum(0, &ip_targ, sizeof(*udp) + payload_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); @@ -363,7 +363,7 @@ void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const fill_ip6_header(&ip6_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip); - checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); + checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(*tcp) + options_size + payload_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); @@ -393,7 +393,7 @@ void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, con fill_ip_header(&ip_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip6); - checksum = ipv4_pseudo_header_checksum(0, &ip_targ); + checksum = ipv4_pseudo_header_checksum(0, &ip_targ, sizeof(*tcp) + payload_size + options_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); -- cgit v1.2.3 From f913fe49272286a7f1e58d94a208b6dc06a51fd2 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 18:30:55 +0900 Subject: Slightly improve dumping packets Add a function for dumping iovecs, and make hexdumps clearer by adding spaces between bytes. Bug: 8276725 Change-Id: Ifb2f8317613d05fa6bd600700090a6df258cde08 --- dump.c | 17 +++++++++++++---- dump.h | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dump.c b/dump.c index fe9721a..b2118a4 100644 --- a/dump.c +++ b/dump.c @@ -216,15 +216,24 @@ void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const char * /* generic hex dump */ void logcat_hexdump(const char *info, const char *data, size_t len) { - char output[PACKETLEN*2+1]; + char output[PACKETLEN*3+2]; size_t i; for(i = 0; i < len && i < PACKETLEN; i++) { - snprintf(output + i*2, 3, "%02x", (uint8_t)data[i]); + snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); } - output[len*2+2] = '\0'; + output[len*3+3] = '\0'; - logmsg(ANDROID_LOG_WARN,"info %s len %d data %s", info, len, output); + logmsg(ANDROID_LOG_WARN,"info %s len %d data%s", info, len, output); } +void dump_iovec(const struct iovec *iov, int iov_len) { + int i; + char *str; + for (i = 0; i < iov_len; i++) { + asprintf(&str, "iov[%d]: ", i); + logcat_hexdump(str, iov[i].iov_base, iov[i].iov_len); + free(str); + } +} #endif // CLAT_DEBUG diff --git a/dump.h b/dump.h index 6ca44bd..9cb040c 100644 --- a/dump.h +++ b/dump.h @@ -29,5 +29,6 @@ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char * 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__ */ -- cgit v1.2.3 From f110707de9dd80288e128fbcf73a0e572977b762 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 9 Apr 2013 21:46:20 -0700 Subject: Add liblog Bug: 8580410 Change-Id: I22d0f0a30e5e42ff0c68805703747dd46ccf4fb6 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index c31a361..2d1b3a6 100644 --- a/Android.mk +++ b/Android.mk @@ -5,7 +5,7 @@ LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c ipv4.c ipv6.c config.c dn LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 -LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_MODULE := clatd -- cgit v1.2.3 From 5cc877d4fc20d66ca5a057a3dc445adb998409bd Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 19:12:43 +0900 Subject: Treat the options as part of the TCP header. This simplifies the code and makes UDP and TCP look the same. It will also make it easier to implement nested translation in the future because there will only be one iovec array entry for the transport layer header, regardless of whether we are translating UDP or TCP and regardless of the presence of options. Also get rid of a couple of memcpy statements by pointing to the original data instead. Bug: 8276725 Change-Id: I6a702aefdf3a070eedfc6f7d3ebec21880ecc22b --- ipv4.c | 30 ++++++++---------------- ipv6.c | 30 ++++++++---------------- translate.c | 77 +++++++++++++++++++++++++++++-------------------------------- translate.h | 6 +++-- 4 files changed, 61 insertions(+), 82 deletions(-) diff --git a/ipv4.c b/ipv4.c index 784f10c..243f9d0 100644 --- a/ipv4.c +++ b/ipv4.c @@ -64,40 +64,30 @@ void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { * ip - ip header */ void tcp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { - struct tcphdr tcp; + const struct tcphdr *tcp = (const struct tcphdr *) packet; const char *payload; - const char *options; - size_t payload_size, options_size; + size_t payload_size, header_size; if(len < sizeof(tcp)) { logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); return; } - memcpy(&tcp, packet, sizeof(tcp)); - - if(tcp.doff < 5) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x",tcp.doff); + if(tcp->doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff); return; } - if((size_t)tcp.doff*4 > len) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x",tcp.doff); + if((size_t) tcp->doff*4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff); return; } - if(tcp.doff > 5) { - options = packet + sizeof(tcp); - options_size = tcp.doff*4 - sizeof(tcp); - } else { - options = NULL; - options_size = 0; - } - - payload = packet + tcp.doff*4; - payload_size = len - tcp.doff*4; + header_size = tcp->doff * 4; + payload = packet + header_size; + payload_size = len - header_size; - tcp_to_tcp6(fd,ip,&tcp,payload,payload_size,options,options_size); + tcp_to_tcp6(fd, ip, tcp, header_size, payload, payload_size); } /* function: udp_packet diff --git a/ipv6.c b/ipv6.c index 8fc36a4..61013f2 100644 --- a/ipv6.c +++ b/ipv6.c @@ -66,40 +66,30 @@ void icmp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { * ip6 - ip6 header */ void tcp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { - struct tcphdr tcp; + const struct tcphdr *tcp = (const struct tcphdr *) packet; const char *payload; - const char *options; - size_t payload_size, options_size; + size_t payload_size, header_size; if(len < sizeof(tcp)) { logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/(too small)"); return; } - memcpy(&tcp, packet, sizeof(tcp)); - - if(tcp.doff < 5) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set to less than 5: %x",tcp.doff); + if(tcp->doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set to less than 5: %x", tcp->doff); return; } - if((size_t)tcp.doff*4 > len) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set too large: %x",tcp.doff); + if((size_t) tcp->doff*4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set too large: %x", tcp->doff); return; } - if(tcp.doff > 5) { - options = packet + sizeof(tcp); - options_size = tcp.doff*4 - sizeof(tcp); - } else { - options = NULL; - options_size = 0; - } - - payload = packet + tcp.doff*4; - payload_size = len - tcp.doff*4; + header_size = tcp->doff * 4; + payload = packet + header_size; + payload_size = len - header_size; - tcp6_to_tcp(fd,ip6,&tcp,payload,payload_size,options,options_size); + tcp6_to_tcp(fd, ip6, tcp, header_size, payload, payload_size); } /* function: udp6_packet diff --git a/translate.c b/translate.c index 89a7e7b..936c781 100644 --- a/translate.c +++ b/translate.c @@ -303,44 +303,41 @@ void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, co * array position 0 - tun header * array position 1 - ipv4/ipv6 header * array position 2 - empty (will be tcp header) - * array position 3 - empty (will be tcp options or payload) - * array position 4 - empty (can be payload) + * array position 3 - empty (will be payload) * checksum - partial checksum covering ipv4/ipv6 header - * options - pointer to tcp option buffer - * options_size - size of tcp option buffer * * TODO: mss rewrite * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ -void tcp_translate(int fd, const struct tcphdr *tcp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum, const char *options, size_t options_size) { - struct tcphdr tcp_targ; - int targ_index = 2; - - memcpy(&tcp_targ, tcp, sizeof(tcp_targ)); - tcp_targ.check = 0; - - checksum = ip_checksum_add(checksum, &tcp_targ, sizeof(tcp_targ)); - if(options) { - checksum = ip_checksum_add(checksum, options, options_size); +void tcp_translate(int fd, const struct tcphdr *tcp, size_t header_size, const char *payload, + size_t payload_size, struct iovec *io_targ, uint32_t checksum) { + union { + // Reserve space for the maximum size of the TCP header, including options. The TCP header + // length field is 4 bits long and counts 4-byte words, so it can be at most 60 bytes. + char buf[15 * 4]; + struct tcphdr tcp; + } header; + + if (header_size > sizeof(header.buf)) { + // A TCP header cannot be more than 60 bytes long, 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, sizeof(header.buf)); + header_size = sizeof(header.buf); } - checksum = ip_checksum_add(checksum, payload, payload_size); - tcp_targ.check = ip_checksum_finish(checksum); - io_targ[targ_index].iov_base = &tcp_targ; - io_targ[targ_index].iov_len = sizeof(tcp_targ); - targ_index++; + memcpy(&header, tcp, header_size); - if(options) { - io_targ[targ_index].iov_base = (char *)options; - io_targ[targ_index].iov_len = options_size; - targ_index++; - } + header.tcp.check = 0; + checksum = ip_checksum_add(checksum, &header, header_size); + checksum = ip_checksum_add(checksum, payload, payload_size); + header.tcp.check = ip_checksum_finish(checksum); - io_targ[targ_index].iov_base = (char *)payload; - io_targ[targ_index].iov_len = payload_size; - targ_index++; + io_targ[2].iov_base = &header; + io_targ[2].iov_len = header_size; - writev(fd, io_targ, targ_index); + io_targ[3].iov_base = (char *)payload; + io_targ[3].iov_len = payload_size; } /* function: tcp_to_tcp6 @@ -348,12 +345,12 @@ void tcp_translate(int fd, const struct tcphdr *tcp, const char *payload, size_t * fd - tun interface fd * ip - source packet ipv4 header * tcp - source packet tcp header + * header_size - size of tcp header including options * payload - tcp payload * payload_size - size of payload - * options - tcp options - * options_size - size of options */ -void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { +void tcp_to_tcp6(int fd, const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size, + const char *payload, size_t payload_size) { struct ip6_hdr ip6_targ; struct iovec io_targ[5]; struct tun_pi tun_header; @@ -361,16 +358,16 @@ void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const fill_tun_header(&tun_header,ETH_P_IPV6); - fill_ip6_header(&ip6_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip); + fill_ip6_header(&ip6_targ, header_size + payload_size, IPPROTO_TCP, ip); - checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(*tcp) + options_size + payload_size); + checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, header_size + payload_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); io_targ[1].iov_base = &ip6_targ; io_targ[1].iov_len = sizeof(ip6_targ); - tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); + tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum); } /* function: tcp6_to_tcp @@ -378,12 +375,12 @@ void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const * fd - tun interface fd * ip6 - source packet ipv6 header * tcp - source packet tcp header + * header_size - size of tcp header including options * payload - tcp payload * payload_size - size of payload - * options - tcp options - * options_size - size of options */ -void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { +void tcp6_to_tcp(int fd, const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size, + const char *payload, size_t payload_size) { struct iphdr ip_targ; struct iovec io_targ[5]; struct tun_pi tun_header; @@ -391,14 +388,14 @@ void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, con fill_tun_header(&tun_header,ETH_P_IP); - fill_ip_header(&ip_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip6); + fill_ip_header(&ip_targ, header_size + payload_size, IPPROTO_TCP, ip6); - checksum = ipv4_pseudo_header_checksum(0, &ip_targ, sizeof(*tcp) + payload_size + options_size); + checksum = ipv4_pseudo_header_checksum(0, &ip_targ, header_size + payload_size); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); io_targ[1].iov_base = &ip_targ; io_targ[1].iov_len = sizeof(ip_targ); - tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); + tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum); } diff --git a/translate.h b/translate.h index 6fc3c79..641768e 100644 --- a/translate.h +++ b/translate.h @@ -24,7 +24,9 @@ void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *ic void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size); void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size); -void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size); -void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size); +void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size, + const char *payload, size_t payload_size); +void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size, + const char *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ -- cgit v1.2.3 From d90841824dc00f65a48a789396c7f428807432ca Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 22 Mar 2013 00:42:21 +0900 Subject: Pass around packet data instead of fds The current code calls all the translation functions one after another, accumulating the translated packet into local variables on the stack and calling writev() at the end. This does not allow calling the translation functions re-entrantly, which is needed, for example, to translate ICMP errors (which contain the packet that caused them). Define a clat_packet type to wrap the array of iovecs and an enum of packet positions. Also clean up the code a bit: get rid of a fair bit of duplicated code (though there is still some left), get rid of some redundant memcpy statements, fix style issues, etc. Bug: 8276725 Change-Id: Ib58d2348894e82275234fc67dbdb1f82753f204f --- clatd.c | 52 +++++--- clatd.h | 1 + dump.c | 1 + ipv4.c | 173 +++++++++++--------------- ipv4.h | 4 +- ipv6.c | 167 +++++++++++-------------- ipv6.h | 4 +- translate.c | 407 ++++++++++++++++++++++++------------------------------------ translate.h | 41 ++++-- 9 files changed, 383 insertions(+), 467 deletions(-) diff --git a/clatd.c b/clatd.c index edd8a2d..8dddc96 100644 --- a/clatd.c +++ b/clatd.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include "ipv4.h" #include "ipv6.h" +#include "translate.h" #include "clatd.h" #include "config.h" #include "logging.h" @@ -306,20 +308,43 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, * 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) { - tun_header->proto = ntohs(tun_header->proto); +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]; + + // 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. + { 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(tun_header->proto == ETH_P_IP) { - ip_packet(tunnel->fd6,packet,packetsize); - } else if(tun_header->proto == ETH_P_IPV6) { - ipv6_packet(tunnel->fd4,packet,packetsize); + if(ntohs(tun_header->proto) == ETH_P_IP) { + fd = tunnel->fd6; + fill_tun_header(&tun_targ, ETH_P_IPV6); + iov_len = ipv4_packet(out, 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, 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 @@ -351,9 +376,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - memcpy(&tun_header, packet, header_size); - - packet_handler(tunnel, &tun_header, packet+header_size, readlen-header_size); + packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); } } @@ -434,29 +457,30 @@ int main(int argc, char **argv) { } if(uplink_interface == NULL) { - logmsg(ANDROID_LOG_FATAL,"clatd called without an interface"); + logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); printf("I need an interface\n"); exit(1); } - logmsg(ANDROID_LOG_INFO,"Starting clat on %s", uplink_interface); + 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)); + 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)); + 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)); + logmsg(ANDROID_LOG_FATAL,"open /proc/sys/net/ipv6/conf/all/forwarding failed: %s", + strerror(errno)); exit(1); } diff --git a/clatd.h b/clatd.h index 00a4094..3459b09 100644 --- a/clatd.h +++ b/clatd.h @@ -22,6 +22,7 @@ #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 diff --git a/dump.c b/dump.c index b2118a4..8567655 100644 --- a/dump.c +++ b/dump.c @@ -17,6 +17,7 @@ */ #include #include +#include #include #include diff --git a/ipv4.c b/ipv4.c index 243f9d0..1d34e1e 100644 --- a/ipv4.c +++ b/ipv4.c @@ -31,127 +31,73 @@ #include "ipv4.h" #include "logging.h" #include "debug.h" +#include "dump.h" /* function: icmp_packet - * takes an icmp packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip - ip header + * 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 */ -void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { - struct icmphdr icmp; +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(icmp)) { - logmsg_dbg(ANDROID_LOG_ERROR,"icmp_packet/(too small)"); - return; + if(len < sizeof(struct icmphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)"); + return 0; } - memcpy(&icmp, packet, sizeof(icmp)); - payload = packet + sizeof(icmp); - payload_size = len - sizeof(icmp); + payload = (const char *) (icmp + 1); + payload_size = len - sizeof(struct icmphdr); - icmp_to_icmp6(fd,ip,&icmp,payload,payload_size); + return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size); } -/* function: tcp_packet - * takes a tcp packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip - ip header - */ -void tcp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) { - const struct tcphdr *tcp = (const struct tcphdr *) packet; - const char *payload; - size_t payload_size, header_size; - - if(len < sizeof(tcp)) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); - return; - } - - if(tcp->doff < 5) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff); - return; - } - - if((size_t) tcp->doff*4 > len) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff); - return; - } - - header_size = tcp->doff * 4; - payload = packet + header_size; - payload_size = len - header_size; - - tcp_to_tcp6(fd, ip, tcp, header_size, payload, payload_size); -} - -/* function: udp_packet - * takes a udp packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip - ip header - */ -void udp_packet(int fd, const char *packet, size_t len, const struct iphdr *ip) { - struct udphdr udp; - const char *payload; - size_t payload_size; - - if(len < sizeof(udp)) { - logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)"); - return; - } - - memcpy(&udp, packet, sizeof(udp)); - payload = packet + sizeof(udp); - payload_size = len - sizeof(udp); - - udp_to_udp6(fd,ip,&udp,payload,payload_size); -} - -/* function: ip_packet - * takes an ip packet and hands it off to the layer 4 protocol function - * fd - tun interface fd +/* 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 */ -void ip_packet(int fd, const char *packet, size_t len) { - struct iphdr header; +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(header)) { - logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/too short for an ip header"); - return; + if(len < sizeof(struct iphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header"); + return 0; } - memcpy(&header, packet, sizeof(header)); - - frag_flags = ntohs(header.frag_off); + 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; + 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; + 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; + 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; + 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 @@ -159,19 +105,40 @@ void ip_packet(int fd, const char *packet, size_t len) { * translate the options. */ - next_header = packet + header.ihl*4; - len_left = len - header.ihl*4; + next_header = packet + header->ihl*4; + len_left = len - header->ihl * 4; + + nxthdr = header->protocol; + if (nxthdr == IPPROTO_ICMP) { + // ICMP and ICMPv6 have different protocol numbers. + nxthdr = IPPROTO_ICMPV6; + } - if(header.protocol == IPPROTO_ICMP) { - icmp_packet(fd,next_header,len_left,&header); - } else if(header.protocol == IPPROTO_TCP) { - tcp_packet(fd,next_header,len_left,&header); - } else if(header.protocol == IPPROTO_UDP) { - udp_packet(fd,next_header,len_left,&header); + /* 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); + 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(payload_length(out, pos)); + return iov_len; } diff --git a/ipv4.h b/ipv4.h index eef3f0c..00befd2 100644 --- a/ipv4.h +++ b/ipv4.h @@ -18,6 +18,8 @@ #ifndef __IPV4_H__ #define __IPV4_H__ -void ip_packet(int fd, const char *packet, size_t len); +#include "translate.h" + +int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); #endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c index 61013f2..8011ce9 100644 --- a/ipv6.c +++ b/ipv6.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "translate.h" #include "checksum.h" @@ -36,84 +37,26 @@ /* function: icmp6_packet * takes an icmp6 packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip6 - ip6 header + * 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 */ -void icmp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { - struct icmp6_hdr icmp6; +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(icmp6)) { - logmsg_dbg(ANDROID_LOG_ERROR,"icmp6_packet/(too small)"); - return; + if(len < sizeof(struct icmp6_hdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)"); + return 0; } - memcpy(&icmp6, packet, sizeof(icmp6)); - payload = packet + sizeof(icmp6); - payload_size = len - sizeof(icmp6); + payload = (const char *) (icmp6 + 1); + payload_size = len - sizeof(struct icmp6_hdr); - icmp6_to_icmp(fd, ip6, &icmp6, payload, payload_size); -} - -/* function: tcp6_packet - * takes a tcp packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip6 - ip6 header - */ -void tcp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { - const struct tcphdr *tcp = (const struct tcphdr *) packet; - const char *payload; - size_t payload_size, header_size; - - if(len < sizeof(tcp)) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/(too small)"); - return; - } - - if(tcp->doff < 5) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set to less than 5: %x", tcp->doff); - return; - } - - if((size_t) tcp->doff*4 > len) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set too large: %x", tcp->doff); - return; - } - - header_size = tcp->doff * 4; - payload = packet + header_size; - payload_size = len - header_size; - - tcp6_to_tcp(fd, ip6, tcp, header_size, payload, payload_size); -} - -/* function: udp6_packet - * takes a udp packet and sets it up for translation - * fd - tun interface fd - * packet - ip payload - * len - size of ip payload - * ip6 - ip6 header - */ -void udp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) { - struct udphdr udp; - const char *payload; - size_t payload_size; - - if(len < sizeof(udp)) { - logmsg_dbg(ANDROID_LOG_ERROR,"udp6_packet/(too small)"); - return; - } - - memcpy(&udp, packet, sizeof(udp)); - payload = packet + sizeof(udp); - payload_size = len - sizeof(udp); - - udp6_to_udp(fd,ip6,&udp,payload,payload_size); + return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size); } /* function: log_bad_address @@ -132,53 +75,81 @@ void log_bad_address(const char *fmt, const struct in6_addr *badaddr) { /* function: ipv6_packet * takes an ipv6 packet and hands it off to the layer 4 protocol function - * fd - tun interface fd + * out - output packet * packet - packet data * len - size of packet + * returns: the highest position in the output clat_packet that's filled in */ -void ipv6_packet(int fd, const char *packet, size_t len) { - struct ip6_hdr header; +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(header)) { - logmsg_dbg(ANDROID_LOG_ERROR,"ipv6_packet/too short for an ip6 header"); - return; + if(len < sizeof(struct ip6_hdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header"); + return 0; } - memcpy(&header, packet, sizeof(header)); - - next_header = packet + sizeof(header); - len_left = len - sizeof(header); - - if(IN6_IS_ADDR_MULTICAST(&header.ip6_dst)) { - log_bad_address("ipv6_packet/multicast %s", &header.ip6_dst); - return; // silently ignore + if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + log_bad_address("ipv6_packet/multicast %s", &ip6->ip6_dst); + return 0; // silently ignore } for(i = 0; i < 3; i++) { - if(header.ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) { - log_bad_address("ipv6_packet/wrong source address: %s", &header.ip6_src); - return; + if(ip6->ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) { + log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src); + return 0; } } - if(!IN6_ARE_ADDR_EQUAL(&header.ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) { - log_bad_address("ipv6_packet/wrong destination address: %s", &header.ip6_dst); - return; + if(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) { + log_bad_address("ipv6_packet/wrong destination address: %s", &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(header.ip6_nxt == IPPROTO_ICMPV6) { - icmp6_packet(fd,next_header,len_left,&header); - } else if(header.ip6_nxt == IPPROTO_TCP) { - tcp6_packet(fd,next_header,len_left,&header); - } else if(header.ip6_nxt == IPPROTO_UDP) { - udp6_packet(fd,next_header,len_left,&header); + 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",header.ip6_nxt); + 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) + payload_length(out, pos)); + ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); + return iov_len; } diff --git a/ipv6.h b/ipv6.h index b898ad9..019de17 100644 --- a/ipv6.h +++ b/ipv6.h @@ -18,6 +18,8 @@ #ifndef __IPV6_H__ #define __IPV6_H__ -void ipv6_packet(int fd, const char *packet, size_t len); +#include "translate.h" + +int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); #endif /* __IPV6_H__ */ diff --git a/translate.c b/translate.c index 936c781..c0bd59a 100644 --- a/translate.c +++ b/translate.c @@ -16,7 +16,6 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include -#include #include #include @@ -27,12 +26,28 @@ #include #include +#include "translate.h" #include "checksum.h" #include "clatd.h" #include "config.h" #include "logging.h" #include "debug.h" +/* function: payload_length + * calculates the total length of the packet components after pos + * packet - packet to calculate the length of + * pos - position to start counting from + * returns: the total length of the packet components after pos + */ +uint16_t payload_length(clat_packet packet, int pos) { + size_t len = 0; + int i; + for (i = pos + 1; i < POS_MAX; i++) { + len += packet[i].iov_len; + } + return len; +} + /* function: fill_tun_header * fill in the header for the tun fd * tun_header - tunnel header, already allocated @@ -46,6 +61,7 @@ void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) { /* function: ipv6_src_to_ipv4_src * return the corresponding ipv4 address for the given ipv6 address * sourceaddr - ipv6 source address + * returns: the IPv4 address */ uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) { // assumes a /96 plat subnet @@ -53,29 +69,28 @@ uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) { } /* function: fill_ip_header - * generating an ipv4 header from an ipv6 header (called by the layer 4 protocol-specific functions) - * ip_targ - (ipv4) target packet header, source addr: original ipv4 addr, dest addr: local subnet addr + * 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 addr: nat64 prefix, dest addr: local subnet prefix + * old_header - (ipv6) source packet header, source: nat64 prefix, dest: local subnet prefix */ -void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol, const struct ip6_hdr *old_header) { - memset(ip_targ, 0, sizeof(ip_targ)); - - ip_targ->ihl = 5; - ip_targ->version = 4; - ip_targ->tos = 0; - ip_targ->tot_len = htons(sizeof(struct iphdr) + payload_len); - ip_targ->id = 0; - ip_targ->frag_off = htons(IP_DF); - ip_targ->ttl = old_header->ip6_hlim; - ip_targ->protocol = protocol; - ip_targ->check = 0; - - ip_targ->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src); - ip_targ->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr; - - ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); +void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, + const struct ip6_hdr *old_header) { + 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_src_to_ipv4_src(&old_header->ip6_src); + ip->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr; } /* function: ipv4_dst_to_ipv6_dst @@ -93,13 +108,14 @@ struct in6_addr ipv4_dst_to_ipv6_dst(uint32_t destination) { } /* function: fill_ip6_header - * generating an ipv6 header from an ipv4 header (called by the layer 4 protocol-specific functions) - * ip6 - (ipv6) target packet header, source addr: local subnet prefix, dest addr: nat64 prefix + * 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 addr: local subnet addr, dest addr: internet's ipv4 addr + * 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) { +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; @@ -113,289 +129,196 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp (only currently supports echo/echo reply) - * fd - tun interface fd - * ip - source packet ipv4 header + * 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 */ -void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size) { - struct ip6_hdr ip6_targ; - struct icmp6_hdr icmp6_targ; - struct iovec io_targ[4]; - struct tun_pi tun_header; +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; uint32_t checksum_temp; if((icmp->type != ICMP_ECHO) && (icmp->type != ICMP_ECHOREPLY)) { - logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x",icmp->type); - return; + logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x", icmp->type); + return 0; } - fill_tun_header(&tun_header,ETH_P_IPV6); + memset(icmp6_targ, 0, sizeof(struct icmp6_hdr)); + icmp6_targ->icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; + icmp6_targ->icmp6_code = 0; + icmp6_targ->icmp6_id = icmp->un.echo.id; + icmp6_targ->icmp6_seq = icmp->un.echo.sequence; - fill_ip6_header(&ip6_targ,payload_size + sizeof(icmp6_targ),IPPROTO_ICMPV6,ip); - - memset(&icmp6_targ, 0, sizeof(icmp6_targ)); - icmp6_targ.icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; - icmp6_targ.icmp6_code = 0; - icmp6_targ.icmp6_cksum = 0; - icmp6_targ.icmp6_id = icmp->un.echo.id; - icmp6_targ.icmp6_seq = icmp->un.echo.sequence; - - checksum_temp = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(icmp6_targ) + payload_size); - checksum_temp = ip_checksum_add(checksum_temp, &icmp6_targ, sizeof(icmp6_targ)); - checksum_temp = ip_checksum_add(checksum_temp, payload, payload_size); - icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp); + icmp6_targ->icmp6_cksum = 0; + checksum = ip_checksum_add(checksum, icmp6_targ, sizeof(struct icmp6_hdr)); + checksum = ip_checksum_add(checksum, payload, payload_size); + icmp6_targ->icmp6_cksum = ip_checksum_finish(checksum); - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip6_targ; - io_targ[1].iov_len = sizeof(ip6_targ); - io_targ[2].iov_base = &icmp6_targ; - io_targ[2].iov_len = sizeof(icmp6_targ); - io_targ[3].iov_base = (char *)payload; - io_targ[3].iov_len = payload_size; + out[pos].iov_len = sizeof(struct icmp6_hdr); + out[POS_PAYLOAD].iov_base = (char *) payload; + out[POS_PAYLOAD].iov_len = payload_size; - writev(fd, io_targ, 4); + return POS_PAYLOAD + 1; } /* function: icmp6_to_icmp * translate ipv6 icmp to ipv4 icmp (only currently supports echo/echo reply) - * fd - tun interface fd - * ip6 - source packet ipv6 header + * 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 */ -void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { - struct iphdr ip_targ; - struct icmphdr icmp_targ; - struct iovec io_targ[4]; - struct tun_pi tun_header; - uint32_t temp_icmp_checksum; +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; if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) { logmsg_dbg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type); - return; + return 0; } - memset(&icmp_targ, 0, sizeof(icmp_targ)); + memset(icmp_targ, 0, sizeof(struct icmphdr)); - fill_tun_header(&tun_header,ETH_P_IP); - fill_ip_header(&ip_targ,sizeof(icmp_targ) + payload_size, IPPROTO_ICMP, ip6); + icmp_targ->type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; + icmp_targ->code = 0x0; + icmp_targ->un.echo.id = icmp6->icmp6_id; + icmp_targ->un.echo.sequence = icmp6->icmp6_seq; - icmp_targ.type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; - icmp_targ.code = 0x0; - icmp_targ.checksum = 0; - icmp_targ.un.echo.id = icmp6->icmp6_id; - icmp_targ.un.echo.sequence = icmp6->icmp6_seq; + icmp_targ->checksum = 0; + checksum = ip_checksum_add(0, icmp_targ, sizeof(struct icmphdr)); + checksum = ip_checksum_add(checksum, (void *)payload, payload_size); + icmp_targ->checksum = ip_checksum_finish(checksum); - temp_icmp_checksum = ip_checksum_add(0, &icmp_targ, sizeof(icmp_targ)); - temp_icmp_checksum = ip_checksum_add(temp_icmp_checksum, (void *)payload, payload_size); - icmp_targ.checksum = ip_checksum_finish(temp_icmp_checksum); + out[pos].iov_len = sizeof(struct icmphdr); + out[POS_PAYLOAD].iov_base = (char *) payload; + out[POS_PAYLOAD].iov_len = payload_size; - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip_targ; - io_targ[1].iov_len = sizeof(ip_targ); - io_targ[2].iov_base = &icmp_targ; - io_targ[2].iov_len = sizeof(icmp_targ); - io_targ[3].iov_base = (char *)payload; - io_targ[3].iov_len = payload_size; - - writev(fd, io_targ, 4); + return POS_PAYLOAD + 1; } -/* function: udp_translate - * common between ipv4/ipv6 - setup checksum and send udp packet - * fd - tun interface fd - * udp - source packet udp header - * payload - udp payload - * payload_size - size of payload - * io_targ - iovec with tun and ipv4/ipv6 header (see below) - * array position 0 - tun header - * array position 1 - ipv4/ipv6 header - * array position 2 - empty (will be udp header) - * array position 3 - empty (will be payload) - * checksum - partial checksum covering ipv4/ipv6 header +/* 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 */ -void udp_translate(int fd, const struct udphdr *udp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum) { - struct udphdr udp_targ; - - memcpy(&udp_targ, udp, sizeof(udp_targ)); - udp_targ.check = 0; // reset checksum, to be calculated +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; - checksum = ip_checksum_add(checksum, &udp_targ, sizeof(struct udphdr)); - checksum = ip_checksum_add(checksum, payload, payload_size); - udp_targ.check = ip_checksum_finish(checksum); + if(len < sizeof(struct udphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)"); + return 0; + } - io_targ[2].iov_base = &udp_targ; - io_targ[2].iov_len = sizeof(udp_targ); - io_targ[3].iov_base = (char *)payload; - io_targ[3].iov_len = payload_size; + payload = (const char *) (udp + 1); + payload_size = len - sizeof(struct udphdr); - writev(fd, io_targ, 4); + return udp_translate(out, pos, udp, checksum, payload, payload_size); } -/* function: udp_to_udp6 - * translate ipv4 udp to ipv6 udp - * fd - tun interface fd - * ip - source packet ipv4 header - * udp - source packet udp header - * payload - udp payload - * payload_size - size of payload +/* 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 */ -void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size) { - struct ip6_hdr ip6_targ; - struct iovec io_targ[4]; - struct tun_pi tun_header; - uint32_t checksum; +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; - fill_tun_header(&tun_header,ETH_P_IPV6); + if(len < sizeof(struct tcphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); + return 0; + } - fill_ip6_header(&ip6_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip); + if(tcp->doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff); + return 0; + } - checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(*udp) + payload_size); + 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; + } - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip6_targ; - io_targ[1].iov_len = sizeof(ip6_targ); + header_size = tcp->doff * 4; + payload = ((const char *) tcp) + header_size; + payload_size = len - header_size; - udp_translate(fd,udp,payload,payload_size,io_targ,checksum); + return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); } -/* function: udp6_to_udp - * translate ipv6 udp to ipv4 udp - * fd - tun interface fd - * ip6 - source packet ipv6 header - * udp - source packet udp header - * payload - udp payload +/* 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 */ -void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size) { - struct iphdr ip_targ; - struct iovec io_targ[4]; - struct tun_pi tun_header; - uint32_t checksum; +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; - fill_tun_header(&tun_header,ETH_P_IP); + memcpy(udp_targ, udp, sizeof(struct udphdr)); + udp_targ->check = 0; // reset checksum, to be calculated - fill_ip_header(&ip_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip6); - - checksum = ipv4_pseudo_header_checksum(0, &ip_targ, sizeof(*udp) + payload_size); + checksum = ip_checksum_add(checksum, udp_targ, sizeof(struct udphdr)); + checksum = ip_checksum_add(checksum, payload, payload_size); + udp_targ->check = ip_checksum_finish(checksum); - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip_targ; - io_targ[1].iov_len = sizeof(ip_targ); + out[pos].iov_len = sizeof(struct udphdr); + out[POS_PAYLOAD].iov_base = (char *) payload; + out[POS_PAYLOAD].iov_len = payload_size; - udp_translate(fd,udp,payload,payload_size,io_targ,checksum); + return POS_PAYLOAD + 1; } /* function: tcp_translate * common between ipv4/ipv6 - setup checksum and send tcp packet - * fd - tun interface fd - * tcp - source packet tcp header + * 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 - * io_targ - iovec with tun and ipv4/ipv6 header (see below) - * array position 0 - tun header - * array position 1 - ipv4/ipv6 header - * array position 2 - empty (will be tcp header) - * array position 3 - empty (will be payload) - * checksum - partial checksum covering ipv4/ipv6 header + * 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) */ -void tcp_translate(int fd, const struct tcphdr *tcp, size_t header_size, const char *payload, - size_t payload_size, struct iovec *io_targ, uint32_t checksum) { - union { - // Reserve space for the maximum size of the TCP header, including options. The TCP header - // length field is 4 bits long and counts 4-byte words, so it can be at most 60 bytes. - char buf[15 * 4]; - struct tcphdr tcp; - } header; - - if (header_size > sizeof(header.buf)) { - // A TCP header cannot be more than 60 bytes long, so this can never happen unless there is a - // bug in the caller. +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, sizeof(header.buf)); - header_size = sizeof(header.buf); + header_size, MAX_TCP_HDR); + header_size = MAX_TCP_HDR; } - memcpy(&header, tcp, header_size); + memcpy(tcp_targ, tcp, header_size); - header.tcp.check = 0; - checksum = ip_checksum_add(checksum, &header, header_size); + tcp_targ->check = 0; + checksum = ip_checksum_add(checksum, tcp_targ, header_size); checksum = ip_checksum_add(checksum, payload, payload_size); - header.tcp.check = ip_checksum_finish(checksum); - - io_targ[2].iov_base = &header; - io_targ[2].iov_len = header_size; - - io_targ[3].iov_base = (char *)payload; - io_targ[3].iov_len = payload_size; -} - -/* function: tcp_to_tcp6 - * translate ipv4 tcp to ipv6 tcp - * fd - tun interface fd - * ip - source packet ipv4 header - * tcp - source packet tcp header - * header_size - size of tcp header including options - * payload - tcp payload - * payload_size - size of payload - */ -void tcp_to_tcp6(int fd, const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size, - const char *payload, size_t payload_size) { - struct ip6_hdr ip6_targ; - struct iovec io_targ[5]; - struct tun_pi tun_header; - uint32_t checksum; - - fill_tun_header(&tun_header,ETH_P_IPV6); - - fill_ip6_header(&ip6_targ, header_size + payload_size, IPPROTO_TCP, ip); - - checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, header_size + payload_size); - - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip6_targ; - io_targ[1].iov_len = sizeof(ip6_targ); - - tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum); -} - -/* function: tcp6_to_tcp - * translate ipv6 tcp to ipv4 tcp - * fd - tun interface fd - * ip6 - source packet ipv6 header - * tcp - source packet tcp header - * header_size - size of tcp header including options - * payload - tcp payload - * payload_size - size of payload - */ -void tcp6_to_tcp(int fd, const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size, - const char *payload, size_t payload_size) { - struct iphdr ip_targ; - struct iovec io_targ[5]; - struct tun_pi tun_header; - uint32_t checksum; - - fill_tun_header(&tun_header,ETH_P_IP); - - fill_ip_header(&ip_targ, header_size + payload_size, IPPROTO_TCP, ip6); - - checksum = ipv4_pseudo_header_checksum(0, &ip_targ, header_size + payload_size); + tcp_targ->check = ip_checksum_finish(checksum); - io_targ[0].iov_base = &tun_header; - io_targ[0].iov_len = sizeof(tun_header); - io_targ[1].iov_base = &ip_targ; - io_targ[1].iov_len = sizeof(ip_targ); + out[POS_PAYLOAD].iov_base = (char *)payload; + out[POS_PAYLOAD].iov_len = payload_size; - tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum); + return POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 641768e..07db023 100644 --- a/translate.h +++ b/translate.h @@ -18,15 +18,40 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ -void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size); -void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size); +#include -void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size); -void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size); +#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. -void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size, - const char *payload, size_t payload_size); -void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size, - const char *payload, size_t payload_size); +// A clat_packet is an array of iovec structures representing a packet that we are translating. +// The POS_XXX constants represent the array indices within the clat_packet that contain specific +// parts of the packet. +enum clat_packet_index { POS_TUNHDR, POS_IPHDR, POS_TRANSPORTHDR, POS_ICMPIPHDR, + POS_PAYLOAD, POS_MAX }; +typedef struct iovec clat_packet[POS_MAX]; + +// Returns the total length of the packet components after index. +uint16_t payload_length(clat_packet packet, int index); + +// 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__ */ -- cgit v1.2.3 From ee80ca65907d214e2483e315a1ba7f610184de03 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2013 16:52:22 +0900 Subject: Improve checksum calculation and address checking 1. Add a function that calculates the checksum of all the packet components starting from the specified position. This simplifies the code a bit and makes it easier to translate nested packets like ICMP error messages. 2. Don't hardcode IP source and destination addresses. This is required to translate ICMP error messages. Bug: 8276725 Change-Id: I2cae45683ae3943e508608fd0a140180dbc60823 --- clatd.c | 4 +- ipv4.c | 2 +- ipv6.c | 11 ++--- translate.c | 154 +++++++++++++++++++++++++++++++++++------------------------- translate.h | 23 +++++---- 5 files changed, 114 insertions(+), 80 deletions(-) diff --git a/clatd.c b/clatd.c index 8dddc96..a914f02 100644 --- a/clatd.c +++ b/clatd.c @@ -333,11 +333,11 @@ void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, co if(ntohs(tun_header->proto) == ETH_P_IP) { fd = tunnel->fd6; fill_tun_header(&tun_targ, ETH_P_IPV6); - iov_len = ipv4_packet(out, POS_IPHDR, packet, packetsize); + 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, POS_IPHDR, packet, packetsize); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); } else { logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); } diff --git a/ipv4.c b/ipv4.c index 1d34e1e..89e47e4 100644 --- a/ipv4.c +++ b/ipv4.c @@ -139,6 +139,6 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { } // Set the length. - ip6_targ->ip6_plen = htons(payload_length(out, pos)); + ip6_targ->ip6_plen = htons(packet_length(out, pos)); return iov_len; } diff --git a/ipv6.c b/ipv6.c index 8011ce9..bb1dc24 100644 --- a/ipv6.c +++ b/ipv6.c @@ -100,12 +100,11 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { return 0; // silently ignore } - for(i = 0; i < 3; i++) { - if(ip6->ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) { - log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src); - return 0; - } + if (!is_in_plat_subnet(&ip6->ip6_src) && ip6->ip6_nxt) { + log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src); + return 0; } + if(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) { log_bad_address("ipv6_packet/wrong destination address: %s", &ip6->ip6_dst); return 0; @@ -149,7 +148,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { } // Set the length and calculate the checksum. - ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + payload_length(out, pos)); + ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos)); ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); return iov_len; } diff --git a/translate.c b/translate.c index c0bd59a..4092bcc 100644 --- a/translate.c +++ b/translate.c @@ -33,21 +33,81 @@ #include "logging.h" #include "debug.h" -/* function: payload_length - * calculates the total length of the packet components after pos +/* 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 from * returns: the total length of the packet components after pos */ -uint16_t payload_length(clat_packet packet, int pos) { +uint16_t packet_length(clat_packet packet, int pos) { size_t len = 0; int i; - for (i = pos + 1; i < POS_MAX; 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 { + // Currently this can only be our own address; other packets are dropped by ipv6_packet. + return Global_Clatd_Config.ipv4_local_subnet.s_addr; + } +} + +/* 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 @@ -58,16 +118,6 @@ void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) { tun_header->proto = htons(proto); } -/* function: ipv6_src_to_ipv4_src - * return the corresponding ipv4 address for the given ipv6 address - * sourceaddr - ipv6 source address - * returns: the IPv4 address - */ -uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) { - // assumes a /96 plat subnet - return sourceaddr->s6_addr32[3]; -} - /* 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 @@ -89,22 +139,8 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, ip->protocol = protocol; ip->check = 0; - ip->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src); - ip->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr; -} - -/* function: ipv4_dst_to_ipv6_dst - * return the corresponding ipv6 address for the given ipv4 address - * destination - ipv4 destination address (network byte order) - */ -struct in6_addr ipv4_dst_to_ipv6_dst(uint32_t destination) { - struct in6_addr v6_destination; - - // assumes a /96 plat subnet - v6_destination = Global_Clatd_Config.plat_subnet; - v6_destination.s6_addr32[3] = destination; - - return v6_destination; + ip->saddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_src); + ip->daddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_dst); } /* function: fill_ip6_header @@ -123,8 +159,8 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_nxt = protocol; ip6->ip6_hlim = old_header->ttl; - ip6->ip6_src = Global_Clatd_Config.ipv6_local_subnet; - ip6->ip6_dst = ipv4_dst_to_ipv6_dst(old_header->daddr); + 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 @@ -152,16 +188,14 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t icmp6_targ->icmp6_id = icmp->un.echo.id; icmp6_targ->icmp6_seq = icmp->un.echo.sequence; - icmp6_targ->icmp6_cksum = 0; - checksum = ip_checksum_add(checksum, icmp6_targ, sizeof(struct icmp6_hdr)); - checksum = ip_checksum_add(checksum, payload, payload_size); - icmp6_targ->icmp6_cksum = ip_checksum_finish(checksum); - out[pos].iov_len = sizeof(struct icmp6_hdr); - out[POS_PAYLOAD].iov_base = (char *) payload; - out[POS_PAYLOAD].iov_len = payload_size; + out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; + + icmp6_targ->icmp6_cksum = 0; // Checksum field must be 0 when calculating checksum. + icmp6_targ->icmp6_cksum = packet_checksum(checksum, out, pos); - return POS_PAYLOAD + 1; + return CLAT_POS_PAYLOAD + 1; } /* function: icmp6_to_icmp @@ -189,16 +223,14 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint3 icmp_targ->un.echo.id = icmp6->icmp6_id; icmp_targ->un.echo.sequence = icmp6->icmp6_seq; - icmp_targ->checksum = 0; - checksum = ip_checksum_add(0, icmp_targ, sizeof(struct icmphdr)); - checksum = ip_checksum_add(checksum, (void *)payload, payload_size); - icmp_targ->checksum = ip_checksum_finish(checksum); - out[pos].iov_len = sizeof(struct icmphdr); - out[POS_PAYLOAD].iov_base = (char *) payload; - out[POS_PAYLOAD].iov_len = payload_size; + out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; + + icmp_targ->checksum = 0; // Checksum field must be 0 when calculating checksum. + icmp_targ->checksum = packet_checksum(0, out, pos); - return POS_PAYLOAD + 1; + return CLAT_POS_PAYLOAD + 1; } /* function: udp_packet @@ -271,17 +303,15 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); - udp_targ->check = 0; // reset checksum, to be calculated - - checksum = ip_checksum_add(checksum, udp_targ, sizeof(struct udphdr)); - checksum = ip_checksum_add(checksum, payload, payload_size); - udp_targ->check = ip_checksum_finish(checksum); out[pos].iov_len = sizeof(struct udphdr); - out[POS_PAYLOAD].iov_base = (char *) payload; - out[POS_PAYLOAD].iov_len = payload_size; + 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 POS_PAYLOAD + 1; + return CLAT_POS_PAYLOAD + 1; } /* function: tcp_translate @@ -312,13 +342,11 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea memcpy(tcp_targ, tcp, header_size); - tcp_targ->check = 0; - checksum = ip_checksum_add(checksum, tcp_targ, header_size); - checksum = ip_checksum_add(checksum, payload, payload_size); - tcp_targ->check = ip_checksum_finish(checksum); + out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; - out[POS_PAYLOAD].iov_base = (char *)payload; - out[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 POS_PAYLOAD + 1; + return CLAT_POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 07db023..120fecf 100644 --- a/translate.h +++ b/translate.h @@ -23,14 +23,21 @@ #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 POS_XXX constants represent the array indices within the clat_packet that contain specific -// parts of the packet. -enum clat_packet_index { POS_TUNHDR, POS_IPHDR, POS_TRANSPORTHDR, POS_ICMPIPHDR, - POS_PAYLOAD, POS_MAX }; -typedef struct iovec clat_packet[POS_MAX]; - -// Returns the total length of the packet components after index. -uint16_t payload_length(clat_packet packet, int index); +// 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_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); -- cgit v1.2.3 From cd70b354eb985678175904a937085bed6094af77 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2013 12:24:56 +0900 Subject: Support translating ICMP errors. When receiving ICMPv6 messages from IPv6-only nodes, use 255.0.0. as a fake IPv4 source address. It's better than nothing. Bug: 8276725 Change-Id: Iae93f75764cb9cd875af9bb5f1862a0dce2c2fa7 --- Android.mk | 2 +- clatd.c | 4 ++ icmp.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ icmp.h | 45 +++++++++++++++ ipv6.c | 35 +++++++----- translate.c | 109 +++++++++++++++++++++++++----------- translate.h | 1 + 7 files changed, 331 insertions(+), 46 deletions(-) create mode 100644 icmp.c create mode 100644 icmp.h diff --git a/Android.mk b/Android.mk index 2d1b3a6..42cc168 100644 --- a/Android.mk +++ b/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.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_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 diff --git a/clatd.c b/clatd.c index a914f02..063026d 100644 --- a/clatd.c +++ b/clatd.c @@ -317,12 +317,16 @@ void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, co 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. }; diff --git a/icmp.c b/icmp.c new file mode 100644 index 0000000..af96b83 --- /dev/null +++ b/icmp.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * icmp.c - convenience functions for translating ICMP and ICMPv6 packets. + */ + +#include +#include +#include +#include + +#include "logging.h" +#include "icmp.h" + +/* function: icmp_guess_ttl + * Guesses the number of hops a received packet has traversed based on its TTL. + * ttl - the ttl of the received packet. + */ +uint8_t icmp_guess_ttl(uint8_t ttl) { + if (ttl > 128) { + return 255 - ttl; + } else if (ttl > 64) { + return 128 - ttl; + } else if (ttl > 32) { + return 64 - ttl; + } else { + return 32 - ttl; + } +} + +/* function: is_icmp_error + * Determines whether an ICMP type is an error message. + * type: the ICMP type + */ +int is_icmp_error(uint8_t type) { + return type == 3 || type == 11 || type == 12; +} + +/* function: is_icmp6_error + * Determines whether an ICMPv6 type is an error message. + * type: the ICMPv6 type + */ +int is_icmp6_error(uint8_t type) { + return type < 128; +} + +/* function: icmp_to_icmp6_type + * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2. + * type - the ICMPv6 type + */ +uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) { + switch (type) { + case ICMP_ECHO: + return ICMP6_ECHO_REQUEST; + + case ICMP_ECHOREPLY: + return ICMP6_ECHO_REPLY; + + case ICMP_TIME_EXCEEDED: + return ICMP6_TIME_EXCEEDED; + + case ICMP_DEST_UNREACH: + // These two types need special translation which we don't support yet. + if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) { + return ICMP6_DST_UNREACH; + } + } + + // We don't understand this ICMP type. Return parameter problem so the caller will bail out. + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type); + return ICMP6_PARAM_PROB; +} + +/* function: icmp_to_icmp6_code + * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2. + * type - the ICMP type + * code - the ICMP code + */ +uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) { + switch (type) { + case ICMP_ECHO: + case ICMP_ECHOREPLY: + return 0; + + case ICMP_TIME_EXCEEDED: + return code; + + case ICMP_DEST_UNREACH: + switch (code) { + case ICMP_UNREACH_NET: + case ICMP_UNREACH_HOST: + return ICMP6_DST_UNREACH_NOROUTE; + + case ICMP_UNREACH_PORT: + return ICMP6_DST_UNREACH_NOPORT; + + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_FILTER_PROHIB: + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + return ICMP6_DST_UNREACH_ADMIN; + + // Otherwise, we don't understand this ICMP type/code combination. Fall through. + } + } + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code); + return 0; +} + +/* function: icmp6_to_icmp_type + * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2. + * type - the ICMP type + */ +uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) { + switch (type) { + case ICMP6_ECHO_REQUEST: + return ICMP_ECHO; + + case ICMP6_ECHO_REPLY: + return ICMP_ECHOREPLY; + + case ICMP6_DST_UNREACH: + return ICMP_DEST_UNREACH; + + case ICMP6_TIME_EXCEEDED: + return ICMP_TIME_EXCEEDED; + } + + // We don't understand this ICMP type. Return parameter problem so the caller will bail out. + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type); + return ICMP_PARAMETERPROB; +} + +/* function: icmp6_to_icmp_code + * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2. + * type - the ICMPv6 type + * code - the ICMPv6 code + */ +uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) { + switch (type) { + case ICMP6_ECHO_REQUEST: + case ICMP6_ECHO_REPLY: + case ICMP6_TIME_EXCEEDED: + return code; + + case ICMP6_DST_UNREACH: + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + return ICMP_UNREACH_HOST; + + case ICMP6_DST_UNREACH_ADMIN: + return ICMP_UNREACH_HOST_PROHIB; + + case ICMP6_DST_UNREACH_BEYONDSCOPE: + return ICMP_UNREACH_HOST; + + case ICMP6_DST_UNREACH_ADDR: + return ICMP_HOST_UNREACH; + + case ICMP6_DST_UNREACH_NOPORT: + return ICMP_UNREACH_PORT; + + // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through. + } + } + + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code); + return 0; +} diff --git a/icmp.h b/icmp.h new file mode 100644 index 0000000..632e92d --- /dev/null +++ b/icmp.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * icmp.c - convenience functions for translating ICMP and ICMPv6 packets. + */ + +#ifndef __ICMP_H__ +#define __ICMP_H__ + +#include + +// Guesses the number of hops a received packet has traversed based on its TTL. +uint8_t icmp_guess_ttl(uint8_t ttl); + +// Determines whether an ICMP type is an error message. +int is_icmp_error(uint8_t type); + +// Determines whether an ICMPv6 type is an error message. +int is_icmp6_error(uint8_t type); + +// Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2. +uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code); + +// Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2. +uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code); + +// Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2. +uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code); + +// Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2. +uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code); + +#endif /* __ICMP_H__ */ diff --git a/ipv6.c b/ipv6.c index bb1dc24..ef1e62f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -64,12 +64,14 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32 * 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 *badaddr) { +void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { #if CLAT_DEBUG - char badaddr_str[INET6_ADDRSTRLEN]; + char srcstr[INET6_ADDRSTRLEN]; + char dststr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, badaddr, badaddr_str, sizeof(badaddr_str)); - logmsg_dbg(ANDROID_LOG_ERROR,fmt,badaddr_str); + 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 } @@ -91,22 +93,27 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { int i; if(len < sizeof(struct ip6_hdr)) { - logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header"); + 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", &ip6->ip6_dst); + log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst); return 0; // silently ignore } - if (!is_in_plat_subnet(&ip6->ip6_src) && ip6->ip6_nxt) { - log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src); - return 0; - } - - if(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) { - log_bad_address("ipv6_packet/wrong destination address: %s", &ip6->ip6_dst); + // 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; } @@ -141,7 +148,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { len_left); } else { #if CLAT_DEBUG - logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x",ip6->ip6_nxt); + logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); logcat_hexdump("ipv6/nxthdr", packet, len); #endif return 0; diff --git a/translate.c b/translate.c index 4092bcc..fc70f3d 100644 --- a/translate.c +++ b/translate.c @@ -26,6 +26,7 @@ #include #include +#include "icmp.h" #include "translate.h" #include "checksum.h" #include "clatd.h" @@ -53,7 +54,7 @@ uint16_t packet_checksum(uint32_t checksum, clat_packet packet, int pos) { /* 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 from + * 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) { @@ -80,13 +81,15 @@ int is_in_plat_subnet(const struct in6_addr *addr6) { * 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 { - // Currently this can only be our own address; other packets are dropped by ipv6_packet. + } 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; } } @@ -127,6 +130,7 @@ void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) { */ 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; @@ -141,6 +145,14 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, 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., 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 @@ -164,7 +176,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol } /* function: icmp_to_icmp6 - * translate ipv4 icmp to ipv6 icmp (only currently supports echo/echo reply) + * translate ipv4 icmp to ipv6 icmp * out - output packet * icmp - source packet icmp header * checksum - pseudo-header checksum @@ -175,31 +187,53 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol 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; - uint32_t checksum_temp; - - if((icmp->type != ICMP_ECHO) && (icmp->type != ICMP_ECHOREPLY)) { - logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x", icmp->type); - return 0; - } + uint8_t icmp6_type; + int clat_packet_len; memset(icmp6_targ, 0, sizeof(struct icmp6_hdr)); - icmp6_targ->icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; - icmp6_targ->icmp6_code = 0; - icmp6_targ->icmp6_id = icmp->un.echo.id; - icmp6_targ->icmp6_seq = icmp->un.echo.sequence; + + 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); - out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; + + 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_POS_PAYLOAD + 1; + return clat_packet_len; } /* function: icmp6_to_icmp - * translate ipv6 icmp to ipv4 icmp (only currently supports echo/echo reply) + * translate ipv6 icmp to ipv4 icmp * out - output packet * icmp6 - source packet icmp6 header * checksum - pseudo-header checksum (unused) @@ -210,27 +244,40 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t 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; - - if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) { - logmsg_dbg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type); - return 0; - } + uint8_t icmp_type; + int ttl; + int clat_packet_len; memset(icmp_targ, 0, sizeof(struct icmphdr)); - icmp_targ->type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; - icmp_targ->code = 0x0; - icmp_targ->un.echo.id = icmp6->icmp6_id; - icmp_targ->un.echo.sequence = icmp6->icmp6_seq; + 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); - out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; + + 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_POS_PAYLOAD + 1; + return clat_packet_len; } /* function: udp_packet diff --git a/translate.h b/translate.h index 120fecf..fded251 100644 --- a/translate.h +++ b/translate.h @@ -27,6 +27,7 @@ // 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]; -- cgit v1.2.3 From 0d7837b9dddce300713bfcf2b84aecf2e360a502 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Jul 2013 19:25:16 +0900 Subject: Remove debug tag from the clatd.conf build target Bug: 9730936 Change-Id: I2c50422c7b502d5b9bdc01afb55d25353f6c7088 --- Android.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/Android.mk b/Android.mk index 42cc168..b0d19d9 100644 --- a/Android.mk +++ b/Android.mk @@ -18,6 +18,5 @@ LOCAL_MODULE := clatd.conf LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/etc LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_TAGS := debug include $(BUILD_PREBUILT) -- cgit v1.2.3