From 62ea9b6658a4c2116eeb6450c8244269448e79aa Mon Sep 17 00:00:00 2001 From: Anatol Pomazau Date: Tue, 6 Mar 2012 12:15:40 -0800 Subject: [PATCH 001/261] Initial empty repository From a45056e35c1af2a0f0a6eed258fd5fdf4846a79f Mon Sep 17 00:00:00 2001 From: Daniel Drown Date: Fri, 23 Mar 2012 10:42:54 -0500 Subject: [PATCH 002/261] 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 0000000000..b1238cecfa --- /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 0000000000..5494d6bf21 --- /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 0000000000..261eeb9e9f --- /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 0000000000..e69de29bb2 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..5943b54b26 --- /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 0000000000..14b7113d83 --- /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 0000000000..c8346a0609 --- /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 0000000000..17a354802f --- /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 0000000000..e6336d7206 --- /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 0000000000..00a409432a --- /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 0000000000..e7ec80e9ee --- /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 0000000000..a83cbecab9 --- /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 0000000000..ba4971becf --- /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 0000000000..ab662c714f --- /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 0000000000..f295e5097b --- /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 0000000000..3e4a6ccca3 --- /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 0000000000..5718e6236d --- /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 0000000000..5f9475e9ec --- /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 0000000000..e7b8670341 --- /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 0000000000..784f10c589 --- /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 0000000000..eef3f0c21e --- /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 0000000000..8fc36a49c0 --- /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 0000000000..b898ad9342 --- /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 0000000000..f2f3d501f8 --- /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 0000000000..1f4b6b60ce --- /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 0000000000..975bf0e06d --- /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 0000000000..c330c24906 --- /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 0000000000..5e0f34edaf --- /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 0000000000..298ad3e393 --- /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 0000000000..7363028ed7 --- /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 0000000000..bc85b2d290 --- /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 0000000000..5ee00bccff --- /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 0000000000..7f83f73dee --- /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 0000000000..3edfe39d59 --- /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 0000000000..d0e7d79acf --- /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 0000000000..cdaca14724 --- /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 0000000000..6fc3c79ac4 --- /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__ */ From 70aba57df0f4a0ee800e7ba3694dbd9b6302a470 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 13 Nov 2012 19:27:14 -0800 Subject: [PATCH 003/261] 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 b1238cecfa..6264b5ab79 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) From f68200ab1a5c559866a87e51d0d52e3c74b02343 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Feb 2013 10:48:29 +0900 Subject: [PATCH 004/261] 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 ba4971becf..8e096721bd 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 3e4a6ccca3..e538f3edb7 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 From 4f3d78640de4573ced186edd3a09b1247d981edf Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Feb 2013 13:18:35 +0900 Subject: [PATCH 005/261] 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 7363028ed7..2ba237d0dc 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 bc85b2d290..13e1f285e5 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 From d6ef91bef28020ec019a64561e8fb5e353e42980 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 26 Feb 2013 09:44:17 +0900 Subject: [PATCH 006/261] 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 e6336d7206..086d39ab24 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 From 2edb7567d3d5373617c236801766e928c6a3dbcb Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 28 Feb 2013 14:15:22 -0800 Subject: [PATCH 007/261] 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 17a354802f..06dcdb3e5b 100644 --- a/clatd.c +++ b/clatd.c @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include #include From baf62997b598ac38a99fd30419ccf7c109ffb3bc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 1 Mar 2013 20:29:39 +0900 Subject: [PATCH 008/261] 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 17a354802f..74aabcb159 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 ab662c714f..b139fe4ee0 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; } From 1373eac3f8305f6ad834ba8e3fc4f1a75cd8ac5a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 4 Mar 2013 18:13:40 +0900 Subject: [PATCH 009/261] 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 b46c3555a3..203714ccb1 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(); From 678a9b4bcf78d062675c9543323ad89f0342136c Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 5 Mar 2013 12:01:12 +0900 Subject: [PATCH 010/261] 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 203714ccb1..f826d466fd 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 3edfe39d59..f264387953 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 d0e7d79acf..58f61cf3af 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 From f2d2abde321c3b2d35edc2436be83611a75c576e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 16 Mar 2013 09:42:30 +0900 Subject: [PATCH 011/261] 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 6264b5ab79..c31a361c90 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) From 41e758e33e6c3b43f363591d4113dea54a4330d9 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 13 Mar 2013 12:26:35 +0900 Subject: [PATCH 012/261] 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 f826d466fd..ea8363cc34 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 From 3ca0302031b14c6fcb9be147e875fb794bfc583b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 27 Mar 2013 12:39:10 +0900 Subject: [PATCH 013/261] 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 ea8363cc34..edd8a2def0 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); } From 0278627f576832860af2d84e04e383ecaa92d74f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 18:02:24 +0900 Subject: [PATCH 014/261] 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 14b7113d83..a4dc9b859c 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 c8346a0609..473f5f52ed 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 e538f3edb7..fe9721a873 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 cdaca14724..89a7e7bb63 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); From f913fe49272286a7f1e58d94a208b6dc06a51fd2 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 18:30:55 +0900 Subject: [PATCH 015/261] 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 fe9721a873..b2118a4e1b 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 6ca44bdf86..9cb040c60e 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__ */ From f110707de9dd80288e128fbcf73a0e572977b762 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 9 Apr 2013 21:46:20 -0700 Subject: [PATCH 016/261] 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 c31a361c90..2d1b3a6b62 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 From 5cc877d4fc20d66ca5a057a3dc445adb998409bd Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Apr 2013 19:12:43 +0900 Subject: [PATCH 017/261] 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 784f10c589..243f9d0675 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; - } + header_size = tcp->doff * 4; + payload = packet + header_size; + payload_size = len - header_size; - payload = packet + tcp.doff*4; - payload_size = len - tcp.doff*4; - - 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 8fc36a49c0..61013f2aec 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; - } + header_size = tcp->doff * 4; + payload = packet + header_size; + payload_size = len - header_size; - payload = packet + tcp.doff*4; - payload_size = len - tcp.doff*4; - - 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 89a7e7bb63..936c7816f5 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; +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; - 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); + 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); } + + memcpy(&header, tcp, header_size); + + header.tcp.check = 0; + checksum = ip_checksum_add(checksum, &header, header_size); checksum = ip_checksum_add(checksum, payload, payload_size); - tcp_targ.check = ip_checksum_finish(checksum); + header.tcp.check = ip_checksum_finish(checksum); - io_targ[targ_index].iov_base = &tcp_targ; - io_targ[targ_index].iov_len = sizeof(tcp_targ); - targ_index++; + io_targ[2].iov_base = &header; + io_targ[2].iov_len = header_size; - 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); + 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 6fc3c79ac4..641768e0cf 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__ */ From d90841824dc00f65a48a789396c7f428807432ca Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 22 Mar 2013 00:42:21 +0900 Subject: [PATCH 018/261] 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 | 421 +++++++++++++++++++++------------------------------- translate.h | 41 ++++- 9 files changed, 390 insertions(+), 474 deletions(-) diff --git a/clatd.c b/clatd.c index edd8a2def0..8dddc9623a 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 00a409432a..3459b09edb 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 b2118a4e1b..856765551e 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 243f9d0675..1d34e1e42d 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; - 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); + nxthdr = header->protocol; + if (nxthdr == IPPROTO_ICMP) { + // ICMP and ICMPv6 have different protocol numbers. + nxthdr = IPPROTO_ICMPV6; + } + + /* Fill in the IPv6 header. We need to do this before we translate the packet because TCP and + * UDP include parts of the IP header in the checksum. Set the length to zero because we don't + * know it yet. + */ + fill_ip6_header(ip6_targ, 0, nxthdr, header); + out[pos].iov_len = sizeof(struct ip6_hdr); + + // Calculate the pseudo-header checksum. + checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + + if(nxthdr == IPPROTO_ICMPV6) { + iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); + } else if(nxthdr == IPPROTO_TCP) { + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); + } else if(nxthdr == IPPROTO_UDP) { + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); } else { #if CLAT_DEBUG - logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/unknown protocol: %x",header.protocol); + 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 eef3f0c21e..00befd251f 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 61013f2aec..8011ce9f20 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 b898ad9342..019de17c91 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 936c7816f5..c0bd59a05f 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)); +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_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->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_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)); + 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); + 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); - 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; + out[pos].iov_len = sizeof(struct icmp6_hdr); + out[POS_PAYLOAD].iov_base = (char *) payload; + out[POS_PAYLOAD].iov_len = payload_size; - 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); - - 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); + 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; + return POS_PAYLOAD + 1; +} - writev(fd, io_targ, 4); +/* function: udp_packet + * takes a udp packet and sets it up for translation + * out - output packet + * udp - pointer to udp header in packet + * checksum - pseudo-header checksum + * len - size of ip payload + */ +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) { + const char *payload; + size_t payload_size; + + if(len < sizeof(struct udphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)"); + return 0; + } + + payload = (const char *) (udp + 1); + payload_size = len - sizeof(struct udphdr); + + return udp_translate(out, pos, udp, checksum, payload, payload_size); +} + +/* function: tcp_packet + * takes a tcp packet and sets it up for translation + * out - output packet + * tcp - pointer to tcp header in packet + * checksum - pseudo-header checksum + * len - size of ip payload + * returns: the highest position in the output clat_packet that's filled in + */ +int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) { + const char *payload; + size_t payload_size, header_size; + + if(len < sizeof(struct tcphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); + return 0; + } + + if(tcp->doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff); + return 0; + } + + if((size_t) tcp->doff*4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff); + return 0; + } + + header_size = tcp->doff * 4; + payload = ((const char *) tcp) + header_size; + payload_size = len - header_size; + + return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); } /* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet - * fd - tun interface fd - * udp - source packet udp header - * payload - udp payload + * out - output packet + * udp - udp header + * checksum - pseudo-header checksum + * 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 udp 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 */ -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; +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, + const char *payload, size_t payload_size) { + struct udphdr *udp_targ = out[pos].iov_base; - memcpy(&udp_targ, udp, sizeof(udp_targ)); - udp_targ.check = 0; // reset checksum, to be calculated + 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, udp_targ, sizeof(struct udphdr)); checksum = ip_checksum_add(checksum, payload, payload_size); - udp_targ.check = ip_checksum_finish(checksum); + 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; + out[pos].iov_len = sizeof(struct udphdr); + out[POS_PAYLOAD].iov_base = (char *) payload; + out[POS_PAYLOAD].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, sizeof(*udp) + 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); - - 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, sizeof(*udp) + 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); - - 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; +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 > 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. + 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); + tcp_targ->check = ip_checksum_finish(checksum); - io_targ[2].iov_base = &header; - io_targ[2].iov_len = header_size; + out[POS_PAYLOAD].iov_base = (char *)payload; + out[POS_PAYLOAD].iov_len = payload_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); - - 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, header_size, payload, payload_size, io_targ, checksum); + return POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 641768e0cf..07db0238d4 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__ */ From ee80ca65907d214e2483e315a1ba7f610184de03 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2013 16:52:22 +0900 Subject: [PATCH 019/261] 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 | 21 ++++--- 5 files changed, 113 insertions(+), 79 deletions(-) diff --git a/clatd.c b/clatd.c index 8dddc9623a..a914f02303 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 1d34e1e42d..89e47e475f 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 8011ce9f20..bb1dc24b73 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 c0bd59a05f..4092bccfb2 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; - return POS_PAYLOAD + 1; + 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; } /* 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; - return POS_PAYLOAD + 1; + 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; } /* 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; - return POS_PAYLOAD + 1; + udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + udp_targ->check = packet_checksum(checksum, out, pos); + + return CLAT_POS_PAYLOAD + 1; } /* function: tcp_translate @@ -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 07db0238d4..120fecf1b3 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]; +// 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]; -// Returns the total length of the packet components after index. -uint16_t payload_length(clat_packet packet, int index); +// 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); From cd70b354eb985678175904a937085bed6094af77 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2013 12:24:56 +0900 Subject: [PATCH 020/261] 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 2d1b3a6b62..42cc168627 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 a914f02303..063026d667 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 0000000000..af96b83582 --- /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 0000000000..632e92d5e7 --- /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 bb1dc24b73..ef1e62f165 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 4092bccfb2..fc70f3da0b 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 120fecf1b3..fded251433 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]; From 2c7b747d57fb07b5aef381554989ffb59ec34afe Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Jul 2013 19:25:16 +0900 Subject: [PATCH 021/261] Remove debug tag from the clatd.conf build target clatd is already built by default by generic_no_telephony.mk, and clatd.conf will be included in the same file by Id9ddf5541e894852eda8c471b9931c7c8bbbdc0a. Bug: 9730936 Change-Id: I0d457c99642264abac4957d614b4eb40111c23b9 --- Android.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/Android.mk b/Android.mk index 42cc168627..b0d19d9626 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) From 0d7837b9dddce300713bfcf2b84aecf2e360a502 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Jul 2013 19:25:16 +0900 Subject: [PATCH 022/261] 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 42cc168627..b0d19d9626 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) From 815d325bd3f2e0afdad4dd396aa0df6e0e60c84c Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Jul 2013 19:25:16 +0900 Subject: [PATCH 023/261] 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 42cc168627..b0d19d9626 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) From 07fc7614a14f024857fcb4880b308df6e8c297d0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 13:33:30 +0900 Subject: [PATCH 024/261] Remove two almost-unused include files. This fixes a compiler warning. Change-Id: I14b5d3c871e9b2fdc8400d999e76e494ad510328 --- clatd.c | 2 -- ipv4.c | 1 - ipv4.h | 25 ------------------------- ipv6.c | 1 - ipv6.h | 25 ------------------------- translate.h | 4 ++++ 6 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 ipv4.h delete mode 100644 ipv6.h diff --git a/clatd.c b/clatd.c index 063026d667..8199b7b33b 100644 --- a/clatd.c +++ b/clatd.c @@ -47,8 +47,6 @@ #include -#include "ipv4.h" -#include "ipv6.h" #include "translate.h" #include "clatd.h" #include "config.h" diff --git a/ipv4.c b/ipv4.c index 89e47e475f..47f242279b 100644 --- a/ipv4.c +++ b/ipv4.c @@ -28,7 +28,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv4.h" #include "logging.h" #include "debug.h" #include "dump.h" diff --git a/ipv4.h b/ipv4.h deleted file mode 100644 index 00befd251f..0000000000 --- a/ipv4.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function - */ -#ifndef __IPV4_H__ -#define __IPV4_H__ - -#include "translate.h" - -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c index ef1e62f165..faf27addbb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -29,7 +29,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv6.h" #include "logging.h" #include "dump.h" #include "config.h" diff --git a/ipv6.h b/ipv6.h deleted file mode 100644 index 019de17c91..0000000000 --- a/ipv6.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function - */ -#ifndef __IPV6_H__ -#define __IPV6_H__ - -#include "translate.h" - -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV6_H__ */ diff --git a/translate.h b/translate.h index fded251433..356050f77a 100644 --- a/translate.h +++ b/translate.h @@ -47,6 +47,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate IPv4 and IPv6 packets. +int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); +int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); + // 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); From 9477a464bd70849bcddcb8d11613cff117235cb0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 15:56:02 +0900 Subject: [PATCH 025/261] Fix compiler warnings and enable -Wall -Werror Change-Id: I24847fa35644d92bff0fe31ccfc5c4b3e8705779 --- Android.mk | 1 + clatd.c | 2 +- ipv6.c | 14 +++++++------- translate.c | 5 ++--- translate.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index b0d19d9626..240b25b8a9 100644 --- a/Android.mk +++ b/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c +LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index 8199b7b33b..b305ef3103 100644 --- a/clatd.c +++ b/clatd.c @@ -82,7 +82,7 @@ void set_forwarding(int fd, const char *setting) { /* function: stop_loop * signal handler: stop the event loop */ -void stop_loop(int signal) { +void stop_loop() { running = 0; } diff --git a/ipv6.c b/ipv6.c index faf27addbb..9eaa6ab56e 100644 --- a/ipv6.c +++ b/ipv6.c @@ -42,8 +42,7 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, - size_t len) { +int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { const char *payload; size_t payload_size; @@ -55,7 +54,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32 payload = (const char *) (icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); - return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size); + return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); } /* function: log_bad_address @@ -63,16 +62,18 @@ 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 *src, const struct in6_addr *dst) { #if CLAT_DEBUG +void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr)); inet_ntop(AF_INET6, dst, dststr, sizeof(dststr)); logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr); -#endif } +#else +#define log_bad_address(fmt, src, dst) +#endif /* function: ipv6_packet * takes an ipv6 packet and hands it off to the layer 4 protocol function @@ -137,8 +138,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { // does not support IPv6 extension headers, this will drop any packet with them if(protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, checksum, - len_left); + iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if(ip6->ip6_nxt == IPPROTO_TCP) { iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); diff --git a/translate.c b/translate.c index fc70f3da0b..75ad08affb 100644 --- a/translate.c +++ b/translate.c @@ -149,7 +149,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, // Third-party ICMPv6 message. This may have been originated by an native IPv6 address. // In that case, the source IPv6 address can't be translated and we need to make up an IPv4 // source address. For now, use 255.0.0., which at least looks useful in traceroute. - if (ip->saddr == (uint32_t) INADDR_NONE) { + if ((uint32_t) ip->saddr == INADDR_NONE) { ttl_guess = icmp_guess_ttl(old_header->ip6_hlim); ip->saddr = htonl((0xff << 24) + ttl_guess); } @@ -236,12 +236,11 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * translate ipv6 icmp to ipv4 icmp * out - output packet * icmp6 - source packet icmp6 header - * checksum - pseudo-header checksum (unused) * payload - icmp6 payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; diff --git a/translate.h b/translate.h index 356050f77a..c4d8ede85e 100644 --- a/translate.h +++ b/translate.h @@ -54,7 +54,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); // 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, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size); // Translate TCP and UDP packets. From 12c72b23e469e9c95e28fcd19be98c688f0ee3d7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 21 Nov 2013 19:16:45 +0900 Subject: [PATCH 026/261] Use ipv4only.arpa for DNS64 detection. We currently use DNS lokoups to ipv4.google.com to do DNS64 detection. Now that RFC 7050 has been published and ipv4only.arpa has been published by IANA, switch to using that. Change-Id: Ia9ebc5db77364104f5888edb53468d29dc618d34 --- clatd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.conf b/clatd.conf index 086d39ab24..b17b862227 100644 --- a/clatd.conf +++ b/clatd.conf @@ -8,7 +8,7 @@ ipv4_local_subnet 192.0.0.4 # get the plat_subnet from dns lookups (requires DNS64) plat_from_dns64 yes # hostname to use to lookup plat subnet. must contain only A records -plat_from_dns64_hostname ipv4.google.com +plat_from_dns64_hostname ipv4only.arpa # plat subnet to send ipv4 traffic to. This is a /96 subnet. # This setting only makes sense with: plat_from_dns64 no From c9f4c89da6c76ebc59a0ec1047853a13ce5f5d96 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 12:59:44 +0900 Subject: [PATCH 027/261] Add generic IP packet code and use it for GRE. Bug: 11542311 Change-Id: I174e0b268869bc77927feeee57003580c47c30f2 --- ipv4.c | 8 +++++--- ipv6.c | 8 +++++--- translate.c | 16 ++++++++++++++++ translate.h | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ipv4.c b/ipv4.c index 47f242279b..b5cbf80661 100644 --- a/ipv4.c +++ b/ipv4.c @@ -123,12 +123,14 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { // Calculate the pseudo-header checksum. checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); - if(nxthdr == IPPROTO_ICMPV6) { + if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); - } else if(nxthdr == IPPROTO_TCP) { + } 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) { + } else if (nxthdr == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (nxthdr == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index 9eaa6ab56e..04d9f9cfaf 100644 --- a/ipv6.c +++ b/ipv6.c @@ -137,14 +137,16 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them - if(protocol == IPPROTO_ICMP) { + if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if(ip6->ip6_nxt == IPPROTO_TCP) { + } 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) { + } else if (ip6->ip6_nxt == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (ip6->ip6_nxt == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index 75ad08affb..927b49c265 100644 --- a/translate.c +++ b/translate.c @@ -279,6 +279,22 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, return clat_packet_len; } +/* function: generic_packet + * takes a generic IP packet and sets it up for translation + * out - output packet + * pos - position in the output packet of the transport header + * payload - pointer to IP payload + * len - size of ip payload + * returns: the highest position in the output clat_packet that's filled in + */ +int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { + out[pos].iov_len = 0; + out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_len = len; + + return CLAT_POS_PAYLOAD + 1; +} + /* function: udp_packet * takes a udp packet and sets it up for translation * out - output packet diff --git a/translate.h b/translate.h index c4d8ede85e..9f1ac15a3f 100644 --- a/translate.h +++ b/translate.h @@ -57,6 +57,9 @@ 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, const char *payload, size_t payload_size); +// Translate generic IP packets. +int generic_packet(clat_packet out, int pos, const char *payload, size_t len); + // 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); From a94a1a921bba6303cb7414627cb127af859a2d9b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 20 Dec 2013 14:51:26 -0800 Subject: [PATCH 028/261] Add ip6 dummy address to keep data usage stats consistent. Because of the way the tunnel pumps packets into the networking stack, the netfilter xt_qtaguid module can't track stats accurately: the totals don't add up. With "clat" having an ip address, qtaguid will track stats against it, which then can be deducted from the external iface. Bug: 11687690 Change-Id: I22ebf26dd9249e821da87665d2bfb0e54d3cdf64 --- clatd.c | 9 ++++++++- clatd.conf | 3 +++ config.c | 4 ++++ config.h | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index b305ef3103..09a86de842 100644 --- a/clatd.c +++ b/clatd.c @@ -203,6 +203,13 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } + status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address, + 64, NULL); + if(status < 0) { + logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) 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); @@ -468,7 +475,7 @@ int main(int argc, char **argv) { // 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_open6 failed: %s", strerror(errno)); exit(1); } diff --git a/clatd.conf b/clatd.conf index b17b862227..0d4b79ef4a 100644 --- a/clatd.conf +++ b/clatd.conf @@ -5,6 +5,9 @@ ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 +# ipv6 extra link local address for the ip6 iface. +ipv6_local_address fe80::c000:0004 + # 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 diff --git a/config.c b/config.c index e7ec80e9ee..61a4ebb441 100644 --- a/config.c +++ b/config.c @@ -255,6 +255,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; + if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address)) + 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); @@ -295,6 +298,7 @@ void dump_config() { 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_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer))); 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))); diff --git a/config.h b/config.h index a83cbecab9..18760c3e5b 100644 --- a/config.h +++ b/config.h @@ -22,10 +22,13 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" +#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004" + #define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" struct clat_config { int16_t mtu, ipv4mtu; + struct in6_addr ipv6_local_address; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; From 4e0dd83686869c292e162dc76992863d3f3a3c38 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 20 Dec 2013 14:51:26 -0800 Subject: [PATCH 029/261] Add ip6 dummy address to keep data usage stats consistent. Because of the way the tunnel pumps packets into the networking stack, the netfilter xt_qtaguid module can't track stats accurately: the totals don't add up. With "clat" having an ip address, qtaguid will track stats against it, which then can be deducted from the external iface. Bug: 11687690 Change-Id: I22ebf26dd9249e821da87665d2bfb0e54d3cdf64 --- clatd.c | 9 ++++++++- clatd.conf | 3 +++ config.c | 4 ++++ config.h | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index b305ef3103..09a86de842 100644 --- a/clatd.c +++ b/clatd.c @@ -203,6 +203,13 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } + status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address, + 64, NULL); + if(status < 0) { + logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) 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); @@ -468,7 +475,7 @@ int main(int argc, char **argv) { // 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_open6 failed: %s", strerror(errno)); exit(1); } diff --git a/clatd.conf b/clatd.conf index b17b862227..0d4b79ef4a 100644 --- a/clatd.conf +++ b/clatd.conf @@ -5,6 +5,9 @@ ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 +# ipv6 extra link local address for the ip6 iface. +ipv6_local_address fe80::c000:0004 + # 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 diff --git a/config.c b/config.c index e7ec80e9ee..61a4ebb441 100644 --- a/config.c +++ b/config.c @@ -255,6 +255,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; + if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address)) + 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); @@ -295,6 +298,7 @@ void dump_config() { 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_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer))); 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))); diff --git a/config.h b/config.h index a83cbecab9..18760c3e5b 100644 --- a/config.h +++ b/config.h @@ -22,10 +22,13 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" +#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004" + #define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" struct clat_config { int16_t mtu, ipv4mtu; + struct in6_addr ipv6_local_address; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; From a33592bd08a20c6a521b8508975b7a74ecdf4f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Thu, 12 Dec 2013 10:28:16 +0100 Subject: [PATCH 030/261] Remove unused variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused variables -- they cause warnings (and errors with -Werror) when building with clang 3.4. Change-Id: I03fff9b1b9d620763f41c8b31a19c4724726739c Signed-off-by: Bernhard Rosenkränzer --- clatd.c | 2 -- config.c | 2 +- icmp.c | 2 +- ipv6.c | 1 - translate.c | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index 09a86de842..a0ee07def2 100644 --- a/clatd.c +++ b/clatd.c @@ -144,7 +144,6 @@ void deconfigure_tun_ipv6(const struct tun_data *tunnel) { * 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, @@ -377,7 +376,6 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { 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) { diff --git a/config.c b/config.c index 61a4ebb441..b82ac6c9d0 100644 --- a/config.c +++ b/config.c @@ -154,7 +154,7 @@ void free_config() { * 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; + int backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; diff --git a/icmp.c b/icmp.c index af96b83582..75a4a4d1c4 100644 --- a/icmp.c +++ b/icmp.c @@ -139,7 +139,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) { } // We don't understand this ICMP type. Return parameter problem so the caller will bail out. - logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type); + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code); return ICMP_PARAMETERPROB; } diff --git a/ipv6.c b/ipv6.c index 04d9f9cfaf..79303ec7c9 100644 --- a/ipv6.c +++ b/ipv6.c @@ -90,7 +90,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { size_t len_left; uint32_t checksum; int iov_len; - int i; if(len < sizeof(struct ip6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); diff --git a/translate.c b/translate.c index 927b49c265..00ea0b99d8 100644 --- a/translate.c +++ b/translate.c @@ -244,7 +244,6 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; - int ttl; int clat_packet_len; memset(icmp_targ, 0, sizeof(struct icmphdr)); From 5a50c0283346a197cda7af19e68f611f14b8fe57 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 10 Feb 2014 09:20:05 +0900 Subject: [PATCH 031/261] Fix up checksums instead of recalculating them. Currently the checksums of translated packets are calculated from scratch by checksumming the translated packet. This is slow and does not work in the case of fragments, because the whole packet is not available. Instead, calculate the checksum by adjusting the checksum of the original packet. Bug: 11542311 Bug: 12116252 Change-Id: I6b78a94ca5bd96b13ee2653b6200551193b3dcc1 --- checksum.c | 38 +++++++++++++++++++++++++++++++++----- checksum.h | 2 ++ ipv4.c | 13 ++++++++----- ipv6.c | 9 +++++---- translate.c | 49 ++++++++++++++++++++++++++++++++----------------- translate.h | 12 +++++++----- 6 files changed, 87 insertions(+), 36 deletions(-) diff --git a/checksum.c b/checksum.c index a4dc9b859c..099be6a0da 100644 --- a/checksum.c +++ b/checksum.c @@ -49,19 +49,27 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { return checksum; } -/* function: ip_checksum_finish - * close the checksum +/* function: ip_checksum_fold + * folds a 32-bit partial checksum into 16 bits * temp_sum - sum from ip_checksum_add + * returns: the folded checksum in network byte order */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { +uint16_t ip_checksum_fold(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_finish + * folds and closes the checksum + * temp_sum - sum from ip_checksum_add + * returns: a header checksum value in network byte order + */ +uint16_t ip_checksum_finish(uint32_t temp_sum) { + return ~ip_checksum_fold(temp_sum); +} + /* function: ip_checksum * combined ip_checksum_add and ip_checksum_finish * data - data to checksum @@ -113,3 +121,23 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u return current; } + +/* function: ip_checksum_adjust + * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums + * checksum - the header checksum in the original packet in network byte order + * old_hdr_sum - the pseudo-header checksum of the original packet + * new_hdr_sum - the pseudo-header checksum of the translated packet + * returns: the new header checksum in network byte order + */ +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { + // Algorithm suggested in RFC 1624. + // http://tools.ietf.org/html/rfc1624#section-3 + checksum = ~checksum; + uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); + uint16_t folded_old = ip_checksum_fold(old_hdr_sum); + if (folded_sum > folded_old) { + return ~(folded_sum - folded_old); + } else { + return ~(folded_sum - folded_old - 1); // end-around borrow + } +} diff --git a/checksum.h b/checksum.h index 473f5f52ed..44921f0d07 100644 --- a/checksum.h +++ b/checksum.h @@ -25,4 +25,6 @@ uint16_t ip_checksum(const void *data, int len); uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); + #endif /* __CHECKSUM_H__ */ diff --git a/ipv4.c b/ipv4.c index b5cbf80661..1d5b0b255c 100644 --- a/ipv4.c +++ b/ipv4.c @@ -70,7 +70,7 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t nxthdr; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct iphdr)) { @@ -121,14 +121,17 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(0, header, len_left); + new_sum = 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); + iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { diff --git a/ipv6.c b/ipv6.c index 79303ec7c9..e4a73fe7cb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -88,7 +88,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t protocol; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct ip6_hdr)) { @@ -133,16 +133,17 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); + new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); diff --git a/translate.c b/translate.c index 00ea0b99d8..9a0f1b5a0d 100644 --- a/translate.c +++ b/translate.c @@ -208,12 +208,10 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t // 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). + // an IPv6 header, which is 20 bytes longer. Fix it up here. // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum). - checksum = htonl(ntohl(checksum) + 20); + checksum = checksum + htons(20); } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) { // Ping packet. icmp6_targ->icmp6_id = icmp->un.echo.id; @@ -298,10 +296,12 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { * takes a udp packet and sets it up for translation * out - output packet * udp - pointer to udp header in packet - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * len - size of ip payload */ -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) { +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size; @@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec payload = (const char *) (udp + 1); payload_size = len - sizeof(struct udphdr); - return udp_translate(out, pos, udp, checksum, payload, payload_size); + return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); } /* function: tcp_packet @@ -324,7 +324,8 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) { +int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size, header_size; @@ -347,20 +348,21 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t chec payload = ((const char *) tcp) + header_size; payload_size = len - header_size; - return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); + return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); } /* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet * out - output packet * udp - udp header - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, - const char *payload, size_t payload_size) { +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, + uint32_t new_sum, const char *payload, size_t payload_size) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); @@ -369,8 +371,22 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c 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); + if (udp_targ->check) { + udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum); + } else { + // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that + // the transmitter generated no checksum (for debugging or for higher level protocols that + // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So + // for safety we recompute it. + udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + udp_targ->check = packet_checksum(new_sum, out, pos); + } + + // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent + // in one's complement arithmetic)." + if (!udp_targ->check) { + udp_targ->check = 0xffff; + } return CLAT_POS_PAYLOAD + 1; } @@ -389,7 +405,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t checksum, const char *payload, size_t payload_size) { + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; out[pos].iov_len = header_size; @@ -406,8 +422,7 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; - tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum. - tcp_targ->check = packet_checksum(checksum, out, pos); + tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); return CLAT_POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 9f1ac15a3f..cfb7bbbf3a 100644 --- a/translate.h +++ b/translate.h @@ -61,12 +61,14 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, int generic_packet(clat_packet out, int pos, const char *payload, size_t len); // 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_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len); +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, 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); + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ From f9390605bacda7bbe8ea33aa0a39c1581ff6aea2 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 13 Feb 2014 12:53:35 +0900 Subject: [PATCH 032/261] Move translation entry point into translate.c. The entry point to the translation code is currently called packet_handler and lives in clatd.c. Move it into translate.c and rename it to translate_packet, since that's what it does. Also get rid of some redundant includes. Bug: 11542311 Change-Id: I8529fb87f3a86ee6724fad54787c33a5e86c56ab --- clatd.c | 66 +---------------------------------------------------- clatd.h | 6 +++++ ipv4.c | 9 -------- ipv6.c | 8 ------- translate.c | 61 +++++++++++++++++++++++++++++++++++++++++-------- translate.h | 14 ++++++++++++ 6 files changed, 73 insertions(+), 91 deletions(-) diff --git a/clatd.c b/clatd.c index a0ee07def2..3ab23711ef 100644 --- a/clatd.c +++ b/clatd.c @@ -29,15 +29,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -63,11 +54,6 @@ 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 */ @@ -305,56 +291,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, configure_tun_ip(tunnel); } -/* function: packet_handler - * takes a tun header and a packet and sends it down the stack - * tunnel - tun device data - * tun_header - tun header - * packet - packet - * packetsize - size of packet - */ -void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize) { - int fd; - int iov_len = 0; - - // Allocate buffers for all packet headers. - struct tun_pi tun_targ; - char iphdr[sizeof(struct ip6_hdr)]; - char transporthdr[MAX_TCP_HDR]; - char icmp_iphdr[sizeof(struct ip6_hdr)]; - char icmp_transporthdr[MAX_TCP_HDR]; - - // iovec of the packets we'll send. This gets passed down to the translation functions. - clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. - { iphdr, 0 }, // IP header. - { transporthdr, 0 }, // Transport layer header. - { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_transporthdr, 0 }, // ICMP error transport layer header. - { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. - }; - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); - iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else { - logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); - } - - if (iov_len > 0) { - writev(fd, out, iov_len); - } -} - /* function: read_packet * reads a packet from the tunnel fd and passes it down the stack * active_fd - tun file descriptor marked ready for reading @@ -383,7 +319,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); } } diff --git a/clatd.h b/clatd.h index 3459b09edb..44a655e0f8 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include #define MAXMTU 1500 @@ -30,4 +31,9 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 +struct tun_data { + char device6[IFNAMSIZ], device4[IFNAMSIZ]; + int fd6, fd4; +}; + #endif /* __CLATD_H__ */ diff --git a/ipv4.c b/ipv4.c index 1d5b0b255c..c828ffa1e0 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,15 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include - #include "translate.h" #include "checksum.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index e4a73fe7cb..371d9e180e 100644 --- a/ipv6.c +++ b/ipv6.c @@ -17,14 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include "translate.h" diff --git a/translate.c b/translate.c index 9a0f1b5a0d..f7f09cbb24 100644 --- a/translate.c +++ b/translate.c @@ -16,15 +16,7 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include - -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "icmp.h" #include "translate.h" @@ -175,6 +167,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } + /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp * out - output packet @@ -426,3 +419,53 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea return CLAT_POS_PAYLOAD + 1; } + +/* function: translate_packet + * 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 translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize) { + int fd; + int iov_len = 0; + + // Allocate buffers for all packet headers. + struct tun_pi tun_targ; + char iphdr[sizeof(struct ip6_hdr)]; + char transporthdr[MAX_TCP_HDR]; + char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_transporthdr[MAX_TCP_HDR]; + + // iovec of the packets we'll send. This gets passed down to the translation functions. + clat_packet out = { + { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { iphdr, 0 }, // IP header. + { transporthdr, 0 }, // Transport layer header. + { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_transporthdr, 0 }, // ICMP error transport layer header. + { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. + }; + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); + } + + if(ntohs(tun_header->proto) == ETH_P_IP) { + fd = tunnel->fd6; + fill_tun_header(&tun_targ, ETH_P_IPV6); + iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { + fd = tunnel->fd4; + fill_tun_header(&tun_targ, ETH_P_IP); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else { + logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); + } + + if (iov_len > 0) { + writev(fd, out, iov_len); + } +} diff --git a/translate.h b/translate.h index cfb7bbbf3a..3378254af8 100644 --- a/translate.h +++ b/translate.h @@ -18,8 +18,18 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ +#include +#include +#include +#include +#include +#include +#include +#include #include +#include "clatd.h" + #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. // A clat_packet is an array of iovec structures representing a packet that we are translating. @@ -47,6 +57,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate and send packets. +void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize); + // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); From fbef82d10366495bbd0dbb30cea015cb67c8105a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 21 Feb 2014 15:31:54 -0800 Subject: [PATCH 033/261] android-clat: link against libnl instead of libnl_2 Change-Id: Ida397d0e8f347a78fb25353ea38760f3b4b499a6 --- Android.mk | 4 ++-- netlink_msg.c | 1 + setroute.c | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 240b25b8a9..635f2f64c8 100644 --- a/Android.mk +++ b/Android.mk @@ -4,8 +4,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c LOCAL_CFLAGS := -Wall -Werror -LOCAL_C_INCLUDES := external/libnl-headers -LOCAL_STATIC_LIBRARIES := libnl_2 +LOCAL_C_INCLUDES := external/libnl/include +LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_MODULE := clatd diff --git a/netlink_msg.c b/netlink_msg.c index 2ba237d0dc..958559c0fa 100644 --- a/netlink_msg.c +++ b/netlink_msg.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/setroute.c b/setroute.c index f264387953..cffee9fb99 100644 --- a/setroute.c +++ b/setroute.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "netlink_msg.h" #include "setroute.h" From 07f0265830fcae2632159e9993b93a161d7ea23b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 20 Feb 2014 14:28:43 +0900 Subject: [PATCH 034/261] Modify the pseudo-header checksum functions. - Remove the initial checksum, which we don't use anywhere. - Add the upper-layer protocol to the IPv6 function. Currently this is always the same as the next header field in the IPv6 header, but technically it's the upper-layer header after all the extension headers. This is required to support fragments. Bug: 11542311 Change-Id: Ie1a20fa74ee5bc933c1014bab74ae2957979b2b8 --- checksum.c | 16 ++++++++-------- checksum.h | 4 ++-- dump.c | 8 ++++---- ipv4.c | 4 ++-- ipv6.c | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/checksum.c b/checksum.c index 099be6a0da..3dd1e00bef 100644 --- a/checksum.c +++ b/checksum.c @@ -84,16 +84,16 @@ 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 - the current checksum or 0 to start a new checksum - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) + * ip6 - the ipv6 header + * len - the transport length (transport header + payload) + * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments */ -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) { +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t) len); - checksum_next = htonl(ip6->ip6_nxt); + checksum_next = htonl(protocol); + uint32_t current = 0; current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); @@ -104,16 +104,16 @@ uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6 /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers - * current - the current checksum or 0 to start a new checksum * ip - the ipv4 header * len - the transport length (transport header + payload) */ -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) { +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { uint16_t temp_protocol, temp_length; temp_protocol = htons(ip->protocol); temp_length = htons(len); + uint32_t current = 0; current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); diff --git a/checksum.h b/checksum.h index 44921f0d07..619581011d 100644 --- a/checksum.h +++ b/checksum.h @@ -22,8 +22,8 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len); uint16_t ip_checksum_finish(uint32_t temp_sum); uint16_t ip_checksum(const void *data, int len); -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol); +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len); uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); diff --git a/dump.c b/dump.c index 856765551e..94e4796d49 100644 --- a/dump.c +++ b/dump.c @@ -147,14 +147,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, sizeof(*udp) + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print ipv6/udp header */ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size) { uint32_t temp_checksum; - temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); dump_udp_generic(udp, temp_checksum, payload, payload_size); } @@ -203,7 +203,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } @@ -211,7 +211,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } diff --git a/ipv4.c b/ipv4.c index c828ffa1e0..e2636b4b3a 100644 --- a/ipv4.c +++ b/ipv4.c @@ -112,8 +112,8 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - old_sum = ipv4_pseudo_header_checksum(0, header, len_left); - new_sum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(header, len_left); + new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); diff --git a/ipv6.c b/ipv6.c index 371d9e180e..d188e47550 100644 --- a/ipv6.c +++ b/ipv6.c @@ -125,8 +125,8 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); - new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); + new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { From 57d480d2b425ef20d8b6f84abd4e9e3209fa9422 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sun, 9 Feb 2014 10:35:38 +0900 Subject: [PATCH 035/261] Support translating fragmented packets. Bug: 11542311 Change-Id: I14a20b9ac669cdb5927f6ac26147bb0109099497 --- clatd.c | 8 ++++++-- dump.c | 1 + ipv4.c | 33 +++++++++++++++++++------------- ipv6.c | 52 +++++++++++++++++++++++++++++++++++++-------------- translate.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++--- translate.h | 13 ++++++++++--- 6 files changed, 126 insertions(+), 35 deletions(-) diff --git a/clatd.c b/clatd.c index 3ab23711ef..a89c588780 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,9 @@ #define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" +/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ +#define MTU_DELTA 28 + int forwarding_fd = -1; volatile sig_atomic_t running = 1; @@ -271,8 +274,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, 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; + if(Global_Clatd_Config.ipv4mtu <= 0 || + Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { + Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } diff --git a/dump.c b/dump.c index 94e4796d49..ba5fa3e35a 100644 --- a/dump.c +++ b/dump.c @@ -220,6 +220,7 @@ void logcat_hexdump(const char *info, const char *data, size_t len) { char output[PACKETLEN*3+2]; size_t i; + output[0] = '\0'; for(i = 0; i < len && i < PACKETLEN; i++) { snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); } diff --git a/ipv4.c b/ipv4.c index e2636b4b3a..84dccd5bc7 100644 --- a/ipv4.c +++ b/ipv4.c @@ -57,7 +57,8 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c 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; + struct ip6_frag *frag_hdr; + size_t frag_hdr_len; uint8_t nxthdr; const char *next_header; size_t len_left; @@ -69,12 +70,6 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { return 0; } - frag_flags = ntohs(header->frag_off); - if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't - logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping"); - return 0; - } - if(header->ihl < 5) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl); return 0; @@ -111,20 +106,32 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip6_header(ip6_targ, 0, nxthdr, header); out[pos].iov_len = sizeof(struct ip6_hdr); - // Calculate the pseudo-header checksum. + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv4_pseudo_header_checksum(header, len_left); new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); - if (nxthdr == IPPROTO_ICMPV6) { - iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); + // If the IPv4 packet is fragmented, add a Fragment header. + frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base; + frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); + out[pos + 1].iov_len = frag_hdr_len; + + if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) { + // Non-first fragment. Copy the rest of the packet as is. + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (nxthdr == IPPROTO_ICMPV6) { + iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index d188e47550..2e83be137f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -77,6 +77,7 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i 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; + struct ip6_frag *frag_hdr = NULL; uint8_t protocol; const char *next_header; size_t len_left; @@ -112,10 +113,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { 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 @@ -124,21 +121,48 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip_header(ip_targ, 0, protocol, ip6); out[pos].iov_len = sizeof(struct iphdr); - // Calculate the pseudo-header checksum. + // If there's a Fragment header, parse it and decide what the next header is. + // Do this before calculating the pseudo-header checksum because it updates the next header value. + if (protocol == IPPROTO_FRAGMENT) { + frag_hdr = (struct ip6_frag *) next_header; + if (len_left < sizeof(*frag_hdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); + return 0; + } + + next_header += sizeof(*frag_hdr); + len_left -= sizeof(*frag_hdr); + + protocol = parse_frag_header(frag_hdr, ip_targ); + } + + // ICMP and ICMPv6 have different protocol numbers. + if (protocol == IPPROTO_ICMPV6) { + protocol = IPPROTO_ICMP; + ip_targ->protocol = IPPROTO_ICMP; + } + + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); - // does not support IPv6 extension headers, this will drop any packet with them - if (protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + // Does not support IPv6 extension headers except Fragment. + if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (protocol == IPPROTO_ICMP) { + iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left); + } else if (protocol == IPPROTO_TCP) { + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + } else if (protocol == IPPROTO_UDP) { + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + } else if (protocol == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index f7f09cbb24..758509226d 100644 --- a/translate.c +++ b/translate.c @@ -167,6 +167,53 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } +/* function: maybe_fill_frag_header + * fills a fragmentation header + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - target (ipv6) fragmentation header + * ip6_targ - target (ipv6) header + * old_header - (ipv4) source packet header + * returns: the length of the fragmentation header if present, or zero if not present + */ +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header) { + uint16_t frag_flags = ntohs(old_header->frag_off); + uint16_t frag_off = frag_flags & IP_OFFMASK; + if (frag_off == 0 && (frag_flags & IP_MF) == 0) { + // Not a fragment. + return 0; + } + + frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; + frag_hdr->ip6f_reserved = 0; + // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits. + frag_hdr->ip6f_offlg = htons(frag_off << 3); + if (frag_flags & IP_MF) { + frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG; + } + frag_hdr->ip6f_ident = htonl(ntohs(old_header->id)); + ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; + + return sizeof(*frag_hdr); +} + +/* function: parse_frag_header + * return the length of the fragmentation header if present, or zero if not present + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - (ipv6) fragmentation header + * ip_targ - target (ipv4) header + * returns: the next header value + */ +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) { + uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3); + if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) { + frag_off |= IP_MF; + } + ip_targ->frag_off = htons(frag_off); + ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); + ip_targ->protocol = frag_hdr->ip6f_nxt; + return frag_hdr->ip6f_nxt; +} /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp @@ -393,9 +440,6 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in - * - * TODO: mss rewrite - * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { @@ -435,16 +479,20 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, // Allocate buffers for all packet headers. struct tun_pi tun_targ; char iphdr[sizeof(struct ip6_hdr)]; + char fraghdr[sizeof(struct ip6_frag)]; char transporthdr[MAX_TCP_HDR]; char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_fraghdr[sizeof(struct ip6_frag)]; char icmp_transporthdr[MAX_TCP_HDR]; // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { { &tun_targ, sizeof(tun_targ) }, // Tunnel header. { iphdr, 0 }, // IP header. + { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_fraghdr, 0 }, // ICMP error fragmentation header. { icmp_transporthdr, 0 }, // ICMP error transport layer header. { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; diff --git a/translate.h b/translate.h index 3378254af8..5efa81725b 100644 --- a/translate.h +++ b/translate.h @@ -36,9 +36,11 @@ // The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain // specific parts of the packet. The packet_* functions operate on all the packet segments past a // given position. -enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX }; +enum clat_packet_index { + CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX +}; typedef struct iovec clat_packet[CLAT_POS_MAX]; // Calculates the checksum over all the packet components starting from pos. @@ -65,6 +67,11 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); +// Deal with fragmented packets. +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header); +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ); + // Translate ICMP packets. int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, const char *payload, size_t payload_size); From f3beefc96a5a4f295562f60d648bd6e795980a10 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 14 Feb 2014 13:19:27 +0900 Subject: [PATCH 036/261] Add a unit test for 464xlat translation code. Bug: 11542311 Change-Id: Ibc7f156a6abb96c9bf4983ecd63b052fe308e422 --- Android.mk | 19 ++ clatd_test.cpp | 672 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 691 insertions(+) create mode 100644 clatd_test.cpp diff --git a/Android.mk b/Android.mk index 635f2f64c8..886c0e08ef 100644 --- a/Android.mk +++ b/Android.mk @@ -8,11 +8,13 @@ LOCAL_C_INCLUDES := external/libnl/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog +# The clat daemon. LOCAL_MODULE := clatd include $(BUILD_EXECUTABLE) +# The configuration file. include $(CLEAR_VARS) LOCAL_MODULE := clatd.conf @@ -21,3 +23,20 @@ LOCAL_MODULE_PATH := $(TARGET_OUT)/etc LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) + + +# Unit tests. +ifneq ($(TARGET_SIMULATOR),true) + +include $(CLEAR_VARS) + +LOCAL_MODULE := clatd_test +LOCAL_CFLAGS := -Wall -Werror +LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c +LOCAL_MODULE_TAGS := eng tests +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_MODULE_PATH := $(TARGET_OUT)/bin + +include $(BUILD_NATIVE_TEST) + +endif diff --git a/clatd_test.cpp b/clatd_test.cpp new file mode 100644 index 0000000000..b70f9afe77 --- /dev/null +++ b/clatd_test.cpp @@ -0,0 +1,672 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * clatd_test.cpp - unit tests for clatd + */ + +#include + +#include +#include +#include + +#include + +extern "C" { +#include "checksum.h" +#include "translate.h" +#include "config.h" +#include "clatd.h" +} + +// For convenience. +#define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0]) + +// Default translation parameters. +static const char kIPv4LocalAddr[] = "192.0.0.4"; +static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464"; +static const char kIPv6PlatSubnet[] = "64:ff9b::"; + +// Test packet portions. Defined as macros because it's easy to concatenate them to make packets. +#define IPV4_HEADER(p, c1, c2) \ + 0x45, 0x00, 0, 41, /* Version=4, IHL=5, ToS=0x80, len=41 */ \ + 0x00, 0x00, 0x40, 0x00, /* ID=0x0000, flags=IP_DF, offset=0 */ \ + 55, (p), (c1), (c2), /* TTL=55, protocol=p, checksum=c1,c2 */ \ + 192, 0, 0, 4, /* Src=192.0.0.4 */ \ + 8, 8, 8, 8, /* Dst=8.8.8.8 */ +#define IPV4_UDP_HEADER IPV4_HEADER(IPPROTO_UDP, 0x73, 0xb0) +#define IPV4_ICMP_HEADER IPV4_HEADER(IPPROTO_ICMP, 0x73, 0xc0) + +#define IPV6_HEADER(p) \ + 0x60, 0x00, 0, 0, /* Version=6, tclass=0x00, flowlabel=0 */ \ + 0, 21, (p), 55, /* plen=11, nxthdr=p, hlim=55 */ \ + 0x20, 0x01, 0x0d, 0xb8, /* Src=2001:db8:0:b11::464 */ \ + 0x00, 0x00, 0x0b, 0x11, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x04, 0x64, \ + 0x00, 0x64, 0xff, 0x9b, /* Dst=64:ff9b::8.8.8.8 */ \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x08, 0x08, 0x08, 0x08, +#define IPV6_UDP_HEADER IPV6_HEADER(IPPROTO_UDP) +#define IPV6_ICMPV6_HEADER IPV6_HEADER(IPPROTO_ICMPV6) + +#define UDP_LEN 21 +#define UDP_HEADER \ + 0xc8, 0x8b, 0, 53, /* Port 51339->53 */ \ + 0x00, UDP_LEN, 0, 0, /* Length 21, checksum empty for now */ + +#define PAYLOAD 'H', 'e', 'l', 'l', 'o', ' ', 0x4e, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x00 + +#define IPV4_PING \ + 0x08, 0x00, 0x88, 0xd0, /* Type 8, code 0, checksum 0x88d0 */ \ + 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */ + +#define IPV6_PING \ + 0x80, 0x00, 0xc3, 0x42, /* Type 128, code 0, checksum 0xc342 */ \ + 0xd0, 0x0d, 0x00, 0x03, /* ID=0xd00d, seq=3 */ + +// Macros to return pseudo-headers from packets. +#define IPV4_PSEUDOHEADER(ip, tlen) \ + ip[12], ip[13], ip[14], ip[15], /* Source address */ \ + ip[16], ip[17], ip[18], ip[19], /* Destination address */ \ + 0, ip[9], /* 0, protocol */ \ + ((tlen) >> 16) & 0xff, (tlen) & 0xff, /* Transport length */ + +#define IPV6_PSEUDOHEADER(ip6, protocol, tlen) \ + ip6[8], ip6[9], ip6[10], ip6[11], /* Source address */ \ + ip6[12], ip6[13], ip6[14], ip6[15], \ + ip6[16], ip6[17], ip6[18], ip6[19], \ + ip6[20], ip6[21], ip6[22], ip6[23], \ + ip6[24], ip6[25], ip6[26], ip6[27], /* Destination address */ \ + ip6[28], ip6[29], ip6[30], ip6[31], \ + ip6[32], ip6[33], ip6[34], ip6[35], \ + ip6[36], ip6[37], ip6[38], ip6[39], \ + ((tlen) >> 24) & 0xff, /* Transport length */ \ + ((tlen) >> 16) & 0xff, \ + ((tlen) >> 8) & 0xff, \ + (tlen) & 0xff, \ + 0, 0, 0, (protocol), + +// A fragmented DNS request. +static const char kIPv4Frag1[] = { + 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x00, 0x40, 0x11, + 0x8c, 0x6d, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00 +}; +static const char kIPv4Frag2[] = { + 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x02, 0x40, 0x11, + 0x8c, 0x6b, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65 +}; +static const char kIPv4Frag3[] = { + 0x45, 0x00, 0x00, 0x1d, 0xfe, 0x47, 0x00, 0x04, 0x40, 0x11, + 0xac, 0x70, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const char *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 }; +static const int kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2), + sizeof(kIPv4Frag3) }; + +static const char kIPv6Frag1[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x47, 0x14, 0x5d, + 0x00, 0x35, 0x00, 0x29, 0xeb, 0x91, 0x50, 0x47, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00 +}; + +static const char kIPv6Frag2[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0xfe, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65 +}; + +static const char kIPv6Frag3[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2c, 0x40, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0xfe, 0x47, 0x03, 0x63, + 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const char *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 }; +static const int kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2), + sizeof(kIPv6Frag3) }; + +static const char kReassembledIPv4[] = { + 0x45, 0x00, 0x00, 0x3d, 0xfe, 0x47, 0x00, 0x00, 0x40, 0x11, + 0xac, 0x54, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, + 0x01 +}; + +// Expected checksums. +static const uint32_t kUdpPartialChecksum = 0xd5c8; +static const uint32_t kPayloadPartialChecksum = 0x31e9c; +static const uint16_t kUdpV4Checksum = 0xd0c7; +static const uint16_t kUdpV6Checksum = 0xa74a; + +int ip_version(const char *packet) { + int version = packet[0] >> 4; + return version; +} + +int is_ipv4_fragment(struct iphdr *ip) { + // A packet is a fragment if its fragment offset is nonzero or if the MF flag is set. + return ntohs(ip->frag_off) & (IP_OFFMASK | IP_MF); +} + +int is_ipv6_fragment(struct ip6_hdr *ip6, size_t len) { + if (ip6->ip6_nxt != IPPROTO_FRAGMENT) { + return 0; + } + struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + return len >= sizeof(*ip6) + sizeof(*frag) && + (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG)); +} + +int ipv4_fragment_offset(struct iphdr *ip) { + return ntohs(ip->frag_off) & IP_OFFMASK; +} + +int ipv6_fragment_offset(struct ip6_frag *frag) { + return ntohs((frag->ip6f_offlg & IP6F_OFF_MASK) >> 3); +} + +void check_packet(const char *packet, size_t len, const char *msg) { + void *payload; + size_t payload_length = 0; + uint32_t pseudo_checksum = 0; + uint8_t protocol = 0; + int version = ip_version(packet); + switch (version) { + case 4: { + struct iphdr *ip = (struct iphdr *) packet; + ASSERT_GE(len, sizeof(*ip)) << msg << ": IPv4 packet shorter than IPv4 header\n"; + EXPECT_EQ(5, ip->ihl) << msg << ": Unsupported IP header length\n"; + EXPECT_EQ(len, ntohs(ip->tot_len)) << msg << ": Incorrect IPv4 length\n"; + EXPECT_EQ(0, ip_checksum(ip, sizeof(*ip))) << msg << ": Incorrect IP checksum\n"; + protocol = ip->protocol; + payload = ip + 1; + if (!is_ipv4_fragment(ip)) { + payload_length = len - sizeof(*ip); + pseudo_checksum = ipv4_pseudo_header_checksum(ip, payload_length); + } + ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMP) + << msg << ": Unsupported IPv4 protocol " << protocol << "\n"; + break; + } + case 6: { + struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; + ASSERT_GE(len, sizeof(*ip6)) << msg << ": IPv6 packet shorter than IPv6 header\n"; + EXPECT_EQ(len - sizeof(*ip6), htons(ip6->ip6_plen)) << msg << ": Incorrect IPv6 length\n"; + + if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { + struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag)) + << msg << ": IPv6 fragment: short fragment header\n"; + protocol = frag->ip6f_nxt; + payload = frag + 1; + // Even though the packet has a Fragment header, it might not be a fragment. + if (!is_ipv6_fragment(ip6, len)) { + payload_length = len - sizeof(*ip6) - sizeof(*frag); + } + } else { + // Since there are no extension headers except Fragment, this must be the payload. + protocol = ip6->ip6_nxt; + payload = ip6 + 1; + payload_length = len - sizeof(*ip6); + } + ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6) + << msg << ": Unsupported IPv6 next header " << protocol; + if (payload_length) { + pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol); + } + break; + } + default: + FAIL() << msg << ": Unsupported IP version " << version << "\n"; + return; + } + + // If we understand the payload, verify the checksum. + if (payload_length) { + uint16_t checksum; + switch(protocol) { + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ICMPV6: + checksum = ip_checksum_finish(ip_checksum_add(pseudo_checksum, payload, payload_length)); + break; + case IPPROTO_ICMP: + checksum = ip_checksum(payload, payload_length); + break; + default: + checksum = 0; // Don't check. + break; + } + EXPECT_EQ(0, checksum) << msg << ": Incorrect transport checksum\n"; + } + + if (protocol == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr *) payload; + EXPECT_NE(0, udp->check) << msg << ": UDP checksum 0 should be 0xffff"; + // If this is not a fragment, check the UDP length field. + if (payload_length) { + EXPECT_EQ(payload_length, ntohs(udp->len)) << msg << ": Incorrect UDP length\n"; + } + } +} + +void reassemble_packet(const char **fragments, const int lengths[], int numpackets, + char *reassembled, size_t *reassembled_len, const char *msg) { + struct iphdr *ip = NULL; + struct ip6_hdr *ip6 = NULL; + int total_length, pos = 0; + uint8_t protocol; + int version = ip_version(fragments[0]); + + for (int i = 0; i < numpackets; i++) { + const char *packet = fragments[i]; + int len = lengths[i]; + int headersize, payload_offset; + + ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n"; + check_packet(packet, len, "Fragment sanity check"); + + switch (version) { + case 4: { + struct iphdr *ip_orig = (struct iphdr *) packet; + headersize = sizeof(*ip_orig); + ASSERT_TRUE(is_ipv4_fragment(ip_orig)) + << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip): 0)) + << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n"; + + headersize = sizeof(*ip_orig); + payload_offset = headersize; + if (pos == 0) { + ip = (struct iphdr *) reassembled; + } + break; + } + case 6: { + struct ip6_hdr *ip6_orig = (struct ip6_hdr *) packet; + struct ip6_frag *frag = (struct ip6_frag *) (ip6_orig + 1); + ASSERT_TRUE(is_ipv6_fragment(ip6_orig, len)) + << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6): 0)) + << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n"; + + headersize = sizeof(*ip6_orig); + payload_offset = sizeof(*ip6_orig) + sizeof(*frag); + if (pos == 0) { + ip6 = (struct ip6_hdr *) reassembled; + protocol = frag->ip6f_nxt; + } + break; + } + default: + FAIL() << msg << ": Invalid IP version << " << version; + } + + // If this is the first fragment, copy the header. + if (pos == 0) { + ASSERT_LT(headersize, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + memcpy(reassembled, packet, headersize); + total_length = headersize; + pos += headersize; + } + + // Copy the payload. + int payload_length = len - payload_offset; + total_length += payload_length; + ASSERT_LT(total_length, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + memcpy(reassembled + pos, packet + payload_offset, payload_length); + pos += payload_length; + } + + + // Fix up the reassembled headers to reflect fragmentation and length (and IPv4 checksum). + ASSERT_EQ(total_length, pos) << msg << ": Reassembled packet length incorrect\n"; + if (ip) { + ip->frag_off &= ~htons(IP_MF); + ip->tot_len = htons(total_length); + ip->check = 0; + ip->check = ip_checksum(ip, sizeof(*ip)); + ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n"; + } + if (ip6) { + ip6->ip6_nxt = protocol; + ip6->ip6_plen = htons(total_length - sizeof(*ip6)); + ASSERT_FALSE(is_ipv6_fragment(ip6, ip6->ip6_plen)) + << msg << ": reassembled IPv6 packet is a fragment!\n"; + } + + *reassembled_len = total_length; +} + +void check_data_matches(const char *expected, const char *actual, size_t len, const char *msg) { + if (memcmp(expected, actual, len)) { + // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4. + int hexdump_len = len * 3 + (len / 20 + 1) * 5; + char expected_hexdump[hexdump_len], actual_hexdump[hexdump_len]; + unsigned pos = 0; + for (unsigned i = 0; i < len; i++) { + if (i % 20 == 0) { + sprintf(expected_hexdump + pos, "\n "); + sprintf(actual_hexdump + pos, "\n "); + pos += 4; + } + sprintf(expected_hexdump + pos, " %02x", expected[i]); + sprintf(actual_hexdump + pos, " %02x", actual[i]); + pos += 3; + } + FAIL() << msg << ": Translated packet doesn't match" + << "\n Expected:" << (char *) expected_hexdump + << "\n Actual:" << (char *) actual_hexdump << "\n"; + } +} + +void fix_udp_checksum(char* packet) { + uint32_t pseudo_checksum; + int version = ip_version(packet); + struct udphdr *udp; + switch (version) { + case 4: { + struct iphdr *ip = (struct iphdr *) packet; + udp = (struct udphdr *) (ip + 1); + pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len)); + break; + } + case 6: { + struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; + udp = (struct udphdr *) (ip6 + 1); + pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP); + break; + } + default: + FAIL() << "unsupported IP version" << version << "\n"; + return; + } + + udp->check = 0; + udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); +} + +void do_translate_packet(const char *original, size_t original_len, char *out, size_t *outlen, + const char *msg) { + int fds[2]; + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { + abort(); + } + struct tun_data tunnel = { + "clat", "clat4", + fds[0], fds[1] + }; + struct tun_pi tun_header = { 0, 0 }; + + char foo[512]; + snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); + check_packet(original, original_len, foo); + + int read_fd; + uint16_t expected_proto; + int version = ip_version(original); + switch (version) { + case 4: + tun_header.proto = htons(ETH_P_IP); + expected_proto = htons(ETH_P_IPV6); + read_fd = fds[1]; + break; + case 6: + tun_header.proto = htons(ETH_P_IPV6); + expected_proto = htons(ETH_P_IP); + read_fd = fds[0]; + break; + default: + FAIL() << msg << ": Unsupported IP version " << version << "\n"; + break; + } + + translate_packet(&tunnel, &tun_header, original, original_len); + + struct tun_pi new_tun_header; + struct iovec iov[] = { + { &new_tun_header, sizeof(new_tun_header) }, + { out, *outlen } + }; + int len = readv(read_fd, iov, 2); + if (len > (int) sizeof(new_tun_header)) { + ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; + EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; + *outlen = len - sizeof(new_tun_header); + } else { + FAIL() << msg << ": Packet was not translated"; + *outlen = 0; + } +} + +void check_translated_packet(const char *original, size_t original_len, + const char *expected, size_t expected_len, const char *msg) { + char translated[MAXMTU]; + size_t translated_len = sizeof(translated); + do_translate_packet(original, original_len, translated, &translated_len, msg); + EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n"; + check_data_matches(expected, translated, translated_len, msg); +} + +void check_fragment_translation(const char *original[], const int original_lengths[], + const char *expected[], const int expected_lengths[], + int numfragments, const char *msg) { + for (int i = 0; i < numfragments; i++) { + // Check that each of the fragments translates as expected. + char frag_msg[512]; + snprintf(frag_msg, sizeof(frag_msg), "%s: fragment #%d", msg, i + 1); + check_translated_packet(original[i], original_lengths[i], + expected[i], expected_lengths[i], frag_msg); + } + + // Sanity check that reassembling the original and translated fragments produces valid packets. + char reassembled[MAXMTU]; + size_t reassembled_len = sizeof(reassembled); + reassemble_packet(original, original_lengths, numfragments, reassembled, &reassembled_len, msg); + check_packet(reassembled, reassembled_len, msg); + + char translated[MAXMTU]; + size_t translated_len = sizeof(translated); + do_translate_packet(reassembled, reassembled_len, translated, &translated_len, msg); + check_packet(translated, translated_len, msg); +} + +struct clat_config Global_Clatd_Config; + +class ClatdTest : public ::testing::Test { + protected: + virtual void SetUp() { + inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); + inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); + inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + } +}; + +TEST_F(ClatdTest, Sanitycheck) { + // Sanity checks the data. + char v4_header[] = { IPV4_UDP_HEADER }; + ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n"; + + char v6_header[] = { IPV6_UDP_HEADER }; + ASSERT_EQ(sizeof(struct ip6_hdr), sizeof(v6_header)) << "Test IPv6 header: incorrect length\n"; + + char udp_header[] = { UDP_HEADER }; + ASSERT_EQ(sizeof(struct udphdr), sizeof(udp_header)) << "Test UDP header: incorrect length\n"; + + // Sanity checks check_packet. + struct udphdr *udp; + char v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + udp = (struct udphdr *) (v4_udp_packet + sizeof(struct iphdr)); + fix_udp_checksum(v4_udp_packet); + ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n"; + check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check"); + + char v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + udp = (struct udphdr *) (v6_udp_packet + sizeof(struct ip6_hdr)); + fix_udp_checksum(v6_udp_packet); + ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n"; + check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check"); + + char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + check_packet(ipv4_ping, sizeof(ipv4_ping), "IPv4 ping sanity check"); + + char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + check_packet(ipv6_ping, sizeof(ipv6_ping), "IPv6 ping sanity check"); + + // Sanity checks reassemble_packet. + char reassembled[MAXMTU]; + size_t total_length = sizeof(reassembled); + reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), + reassembled, &total_length, "Reassembly sanity check"); + check_packet(reassembled, total_length, "IPv4 Reassembled packet is valid"); + ASSERT_EQ(sizeof(kReassembledIPv4), total_length) << "IPv4 reassembly sanity check: length\n"; + ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *) reassembled)) + << "Sanity check: reassembled packet is a fragment!\n"; + check_data_matches(kReassembledIPv4, reassembled, total_length, "IPv4 reassembly sanity check"); + + total_length = sizeof(reassembled); + reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), + reassembled, &total_length, "IPv6 reassembly sanity check"); + ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *) reassembled, total_length)) + << "Sanity check: reassembled packet is a fragment!\n"; + check_packet(reassembled, total_length, "IPv6 Reassembled packet is valid"); +} + +TEST_F(ClatdTest, PseudoChecksum) { + uint32_t pseudo_checksum; + + char v4_header[] = { IPV4_UDP_HEADER }; + char v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) }; + pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *) v4_header, UDP_LEN); + EXPECT_EQ(ip_checksum_finish(pseudo_checksum), + ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header))) + << "ipv4_pseudo_header_checksum incorrect\n"; + + char v6_header[] = { IPV6_UDP_HEADER }; + char v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) }; + pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *) v6_header, UDP_LEN, IPPROTO_UDP); + EXPECT_EQ(ip_checksum_finish(pseudo_checksum), + ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header))) + << "ipv6_pseudo_header_checksum incorrect\n"; +} + +TEST_F(ClatdTest, TransportChecksum) { + char udphdr[] = { UDP_HEADER }; + char payload[] = { PAYLOAD }; + EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr))) + << "UDP partial checksum\n"; + EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload))) + << "Payload partial checksum\n"; + + char ip[] = { IPV4_UDP_HEADER }; + char ip6[] = { IPV6_UDP_HEADER }; + uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *) ip, UDP_LEN); + uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN, + IPPROTO_UDP); + + EXPECT_EQ(0x3ad0, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; + EXPECT_EQ(0x2644b, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; + EXPECT_EQ( + kUdpV4Checksum, + ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) + << "Unexpected UDP/IPv4 checksum\n"; + EXPECT_EQ( + kUdpV6Checksum, + ip_checksum_finish(ipv6_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) + << "Unexpected UDP/IPv6 checksum\n"; + + EXPECT_EQ(kUdpV6Checksum, + ip_checksum_adjust(kUdpV4Checksum, ipv4_pseudo_sum, ipv6_pseudo_sum)) + << "Adjust IPv4/UDP checksum to IPv6\n"; + EXPECT_EQ(kUdpV4Checksum, + ip_checksum_adjust(kUdpV6Checksum, ipv6_pseudo_sum, ipv4_pseudo_sum)) + << "Adjust IPv6/UDP checksum to IPv4\n"; +} + +TEST_F(ClatdTest, AdjustChecksum) { + struct checksum_data { + uint16_t checksum; + uint32_t old_hdr_sum; + uint32_t new_hdr_sum; + uint16_t result; + } DATA[] = { + { 0x1423, 0xb8ec, 0x2d757, 0xf5b5 }, + { 0xf5b5, 0x2d757, 0xb8ec, 0x1423 }, + { 0xdd2f, 0x5555, 0x3285, 0x0000 }, + { 0x1215, 0x5560, 0x15560 + 20, 0x1200 }, + { 0xd0c7, 0x3ad0, 0x2644b, 0xa74a }, + }; + unsigned i, failed = 0; + + for (i = 0; i < ARRAYSIZE(DATA); i++) { + struct checksum_data *data = DATA + i; + uint16_t result = ip_checksum_adjust(data->checksum, data->old_hdr_sum, data->new_hdr_sum); + EXPECT_EQ(result, data->result) + << "Incorrect checksum" << std::showbase << std::hex + << "\n Expected: " << data->result + << "\n Actual: " << result + << "\n checksum=" << data->checksum + << " old_sum=" << data->old_hdr_sum << " new_sum=" << data->new_hdr_sum << "\n"; + } +} + +TEST_F(ClatdTest, Translate) { + char udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + char udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + fix_udp_checksum(udp_ipv4); + fix_udp_checksum(udp_ipv6); + check_translated_packet(udp_ipv4, sizeof(udp_ipv4), udp_ipv6, sizeof(udp_ipv6), + "UDP/IPv4 -> UDP/IPv6 translation"); + check_translated_packet(udp_ipv6, sizeof(udp_ipv6), udp_ipv4, sizeof(udp_ipv4), + "UDP/IPv6 -> UDP/IPv4 translation"); + + char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + check_translated_packet(ipv4_ping, sizeof(ipv4_ping), ipv6_ping, sizeof(ipv6_ping), + "ICMP->ICMPv6 translation"); + check_translated_packet(ipv6_ping, sizeof(ipv6_ping), ipv4_ping, sizeof(ipv4_ping), + "ICMPv6->ICMP translation"); +} + +TEST_F(ClatdTest, Fragmentation) { + int len, i; + check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, + kIPv6Fragments, kIPv6FragLengths, + ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation"); + + check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, + kIPv4Fragments, kIPv4FragLengths, + ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); +} From fcac410fa15613873a07143ccd46470b869346a3 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 24 Feb 2014 20:03:01 -0800 Subject: [PATCH 037/261] Use uint8_t not char for binary data to avoid -Wall -Werror issues on x86 builds Change-Id: Ib61e67f15360c27c3ebf61f870a9482217c52357 --- clatd.c | 2 +- clatd_test.cpp | 102 ++++++++++++++++++++++++------------------------- dump.c | 21 ++++++---- dump.h | 16 +++++--- ipv4.c | 8 ++-- ipv6.c | 8 ++-- translate.c | 37 +++++++++--------- translate.h | 21 +++++----- 8 files changed, 117 insertions(+), 98 deletions(-) diff --git a/clatd.c b/clatd.c index a89c588780..63c4d6d6b9 100644 --- a/clatd.c +++ b/clatd.c @@ -302,7 +302,7 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, */ void read_packet(int active_fd, const struct tun_data *tunnel) { ssize_t readlen; - char packet[PACKETLEN]; + uint8_t packet[PACKETLEN]; // in case something ignores the packet length memset(packet, 0, PACKETLEN); diff --git a/clatd_test.cpp b/clatd_test.cpp index b70f9afe77..ba7889891c 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -101,28 +101,28 @@ static const char kIPv6PlatSubnet[] = "64:ff9b::"; 0, 0, 0, (protocol), // A fragmented DNS request. -static const char kIPv4Frag1[] = { +static const uint8_t kIPv4Frag1[] = { 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x00, 0x40, 0x11, 0x8c, 0x6d, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00 }; -static const char kIPv4Frag2[] = { +static const uint8_t kIPv4Frag2[] = { 0x45, 0x00, 0x00, 0x24, 0xfe, 0x47, 0x20, 0x02, 0x40, 0x11, 0x8c, 0x6b, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0x69, 0x70, 0x76, 0x34, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65 }; -static const char kIPv4Frag3[] = { +static const uint8_t kIPv4Frag3[] = { 0x45, 0x00, 0x00, 0x1d, 0xfe, 0x47, 0x00, 0x04, 0x40, 0x11, 0xac, 0x70, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; -static const char *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 }; -static const int kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2), - sizeof(kIPv4Frag3) }; +static const uint8_t *kIPv4Fragments[] = { kIPv4Frag1, kIPv4Frag2, kIPv4Frag3 }; +static const size_t kIPv4FragLengths[] = { sizeof(kIPv4Frag1), sizeof(kIPv4Frag2), + sizeof(kIPv4Frag3) }; -static const char kIPv6Frag1[] = { +static const uint8_t kIPv6Frag1[] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, @@ -132,7 +132,7 @@ static const char kIPv6Frag1[] = { 0x00, 0x01, 0x00, 0x00 }; -static const char kIPv6Frag2[] = { +static const uint8_t kIPv6Frag2[] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2c, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, @@ -142,7 +142,7 @@ static const char kIPv6Frag2[] = { 0x6f, 0x67, 0x6c, 0x65 }; -static const char kIPv6Frag3[] = { +static const uint8_t kIPv6Frag3[] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x11, 0x2c, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x64, 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, @@ -150,11 +150,11 @@ static const char kIPv6Frag3[] = { 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0xfe, 0x47, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; -static const char *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 }; -static const int kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2), - sizeof(kIPv6Frag3) }; +static const uint8_t *kIPv6Fragments[] = { kIPv6Frag1, kIPv6Frag2, kIPv6Frag3 }; +static const size_t kIPv6FragLengths[] = { sizeof(kIPv6Frag1), sizeof(kIPv6Frag2), + sizeof(kIPv6Frag3) }; -static const char kReassembledIPv4[] = { +static const uint8_t kReassembledIPv4[] = { 0x45, 0x00, 0x00, 0x3d, 0xfe, 0x47, 0x00, 0x00, 0x40, 0x11, 0xac, 0x54, 0xc0, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x14, 0x5d, 0x00, 0x35, 0x00, 0x29, 0x68, 0xbb, 0x50, 0x47, @@ -170,8 +170,8 @@ static const uint32_t kPayloadPartialChecksum = 0x31e9c; static const uint16_t kUdpV4Checksum = 0xd0c7; static const uint16_t kUdpV6Checksum = 0xa74a; -int ip_version(const char *packet) { - int version = packet[0] >> 4; +uint8_t ip_version(const uint8_t *packet) { + uint8_t version = packet[0] >> 4; return version; } @@ -197,7 +197,7 @@ int ipv6_fragment_offset(struct ip6_frag *frag) { return ntohs((frag->ip6f_offlg & IP6F_OFF_MASK) >> 3); } -void check_packet(const char *packet, size_t len, const char *msg) { +void check_packet(const uint8_t *packet, size_t len, const char *msg) { void *payload; size_t payload_length = 0; uint32_t pseudo_checksum = 0; @@ -282,16 +282,16 @@ void check_packet(const char *packet, size_t len, const char *msg) { } } -void reassemble_packet(const char **fragments, const int lengths[], int numpackets, - char *reassembled, size_t *reassembled_len, const char *msg) { +void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets, + uint8_t *reassembled, size_t *reassembled_len, const char *msg) { struct iphdr *ip = NULL; struct ip6_hdr *ip6 = NULL; int total_length, pos = 0; uint8_t protocol; - int version = ip_version(fragments[0]); + uint8_t version = ip_version(fragments[0]); for (int i = 0; i < numpackets; i++) { - const char *packet = fragments[i]; + const uint8_t *packet = fragments[i]; int len = lengths[i]; int headersize, payload_offset; @@ -370,7 +370,7 @@ void reassemble_packet(const char **fragments, const int lengths[], int numpacke *reassembled_len = total_length; } -void check_data_matches(const char *expected, const char *actual, size_t len, const char *msg) { +void check_data_matches(const uint8_t *expected, const uint8_t *actual, size_t len, const char *msg) { if (memcmp(expected, actual, len)) { // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4. int hexdump_len = len * 3 + (len / 20 + 1) * 5; @@ -392,9 +392,9 @@ void check_data_matches(const char *expected, const char *actual, size_t len, co } } -void fix_udp_checksum(char* packet) { +void fix_udp_checksum(uint8_t* packet) { uint32_t pseudo_checksum; - int version = ip_version(packet); + uint8_t version = ip_version(packet); struct udphdr *udp; switch (version) { case 4: { @@ -418,7 +418,7 @@ void fix_udp_checksum(char* packet) { udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); } -void do_translate_packet(const char *original, size_t original_len, char *out, size_t *outlen, +void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen, const char *msg) { int fds[2]; if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { @@ -471,17 +471,17 @@ void do_translate_packet(const char *original, size_t original_len, char *out, s } } -void check_translated_packet(const char *original, size_t original_len, - const char *expected, size_t expected_len, const char *msg) { - char translated[MAXMTU]; +void check_translated_packet(const uint8_t *original, size_t original_len, + const uint8_t *expected, size_t expected_len, const char *msg) { + uint8_t translated[MAXMTU]; size_t translated_len = sizeof(translated); do_translate_packet(original, original_len, translated, &translated_len, msg); EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n"; check_data_matches(expected, translated, translated_len, msg); } -void check_fragment_translation(const char *original[], const int original_lengths[], - const char *expected[], const int expected_lengths[], +void check_fragment_translation(const uint8_t *original[], const size_t original_lengths[], + const uint8_t *expected[], const size_t expected_lengths[], int numfragments, const char *msg) { for (int i = 0; i < numfragments; i++) { // Check that each of the fragments translates as expected. @@ -492,12 +492,12 @@ void check_fragment_translation(const char *original[], const int original_lengt } // Sanity check that reassembling the original and translated fragments produces valid packets. - char reassembled[MAXMTU]; + uint8_t reassembled[MAXMTU]; size_t reassembled_len = sizeof(reassembled); reassemble_packet(original, original_lengths, numfragments, reassembled, &reassembled_len, msg); check_packet(reassembled, reassembled_len, msg); - char translated[MAXMTU]; + uint8_t translated[MAXMTU]; size_t translated_len = sizeof(translated); do_translate_packet(reassembled, reassembled_len, translated, &translated_len, msg); check_packet(translated, translated_len, msg); @@ -516,37 +516,37 @@ class ClatdTest : public ::testing::Test { TEST_F(ClatdTest, Sanitycheck) { // Sanity checks the data. - char v4_header[] = { IPV4_UDP_HEADER }; + uint8_t v4_header[] = { IPV4_UDP_HEADER }; ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n"; - char v6_header[] = { IPV6_UDP_HEADER }; + uint8_t v6_header[] = { IPV6_UDP_HEADER }; ASSERT_EQ(sizeof(struct ip6_hdr), sizeof(v6_header)) << "Test IPv6 header: incorrect length\n"; - char udp_header[] = { UDP_HEADER }; + uint8_t udp_header[] = { UDP_HEADER }; ASSERT_EQ(sizeof(struct udphdr), sizeof(udp_header)) << "Test UDP header: incorrect length\n"; // Sanity checks check_packet. struct udphdr *udp; - char v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + uint8_t v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; udp = (struct udphdr *) (v4_udp_packet + sizeof(struct iphdr)); fix_udp_checksum(v4_udp_packet); ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n"; check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check"); - char v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + uint8_t v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; udp = (struct udphdr *) (v6_udp_packet + sizeof(struct ip6_hdr)); fix_udp_checksum(v6_udp_packet); ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n"; check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check"); - char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; check_packet(ipv4_ping, sizeof(ipv4_ping), "IPv4 ping sanity check"); - char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; check_packet(ipv6_ping, sizeof(ipv6_ping), "IPv6 ping sanity check"); // Sanity checks reassemble_packet. - char reassembled[MAXMTU]; + uint8_t reassembled[MAXMTU]; size_t total_length = sizeof(reassembled); reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), reassembled, &total_length, "Reassembly sanity check"); @@ -567,15 +567,15 @@ TEST_F(ClatdTest, Sanitycheck) { TEST_F(ClatdTest, PseudoChecksum) { uint32_t pseudo_checksum; - char v4_header[] = { IPV4_UDP_HEADER }; - char v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) }; + uint8_t v4_header[] = { IPV4_UDP_HEADER }; + uint8_t v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) }; pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *) v4_header, UDP_LEN); EXPECT_EQ(ip_checksum_finish(pseudo_checksum), ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header))) << "ipv4_pseudo_header_checksum incorrect\n"; - char v6_header[] = { IPV6_UDP_HEADER }; - char v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) }; + uint8_t v6_header[] = { IPV6_UDP_HEADER }; + uint8_t v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) }; pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *) v6_header, UDP_LEN, IPPROTO_UDP); EXPECT_EQ(ip_checksum_finish(pseudo_checksum), ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header))) @@ -583,15 +583,15 @@ TEST_F(ClatdTest, PseudoChecksum) { } TEST_F(ClatdTest, TransportChecksum) { - char udphdr[] = { UDP_HEADER }; - char payload[] = { PAYLOAD }; + uint8_t udphdr[] = { UDP_HEADER }; + uint8_t payload[] = { PAYLOAD }; EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr))) << "UDP partial checksum\n"; EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload))) << "Payload partial checksum\n"; - char ip[] = { IPV4_UDP_HEADER }; - char ip6[] = { IPV6_UDP_HEADER }; + uint8_t ip[] = { IPV4_UDP_HEADER }; + uint8_t ip6[] = { IPV6_UDP_HEADER }; uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *) ip, UDP_LEN); uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN, IPPROTO_UDP); @@ -643,8 +643,8 @@ TEST_F(ClatdTest, AdjustChecksum) { } TEST_F(ClatdTest, Translate) { - char udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; - char udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; fix_udp_checksum(udp_ipv4); fix_udp_checksum(udp_ipv6); check_translated_packet(udp_ipv4, sizeof(udp_ipv4), udp_ipv6, sizeof(udp_ipv6), @@ -652,8 +652,8 @@ TEST_F(ClatdTest, Translate) { check_translated_packet(udp_ipv6, sizeof(udp_ipv6), udp_ipv4, sizeof(udp_ipv4), "UDP/IPv6 -> UDP/IPv4 translation"); - char ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; - char ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; + uint8_t ipv4_ping[] = { IPV4_ICMP_HEADER IPV4_PING PAYLOAD }; + uint8_t ipv6_ping[] = { IPV6_ICMPV6_HEADER IPV6_PING PAYLOAD }; check_translated_packet(ipv4_ping, sizeof(ipv4_ping), ipv6_ping, sizeof(ipv6_ping), "ICMP->ICMPv6 translation"); check_translated_packet(ipv6_ping, sizeof(ipv6_ping), ipv4_ping, sizeof(ipv4_ping), diff --git a/dump.c b/dump.c index ba5fa3e35a..0fda4e7b0d 100644 --- a/dump.c +++ b/dump.c @@ -130,7 +130,8 @@ void dump_icmp6(struct icmp6_hdr *icmp6) { } /* print udp header */ -void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const char *payload, size_t payload_size) { +void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, + const uint8_t *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, udp, sizeof(struct udphdr)); @@ -145,14 +146,16 @@ 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) { +void dump_udp(const struct udphdr *udp, const struct iphdr *ip, + const uint8_t *payload, size_t payload_size) { uint32_t temp_checksum; temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print ipv6/udp header */ -void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size) { +void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, + const uint8_t *payload, size_t payload_size) { uint32_t temp_checksum; temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); dump_udp_generic(udp, temp_checksum, payload, payload_size); @@ -200,7 +203,9 @@ void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t opti } /* print ipv4/tcp header */ -void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payload, size_t payload_size, const char *options, size_t options_size) { +void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, + const uint8_t *payload, size_t payload_size, + const char *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); @@ -208,7 +213,9 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payl } /* print ipv6/tcp header */ -void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size, const char *options, size_t options_size) { +void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, + const uint8_t *payload, size_t payload_size, + const char *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); @@ -216,13 +223,13 @@ 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) { +void logcat_hexdump(const char *info, const uint8_t *data, size_t len) { char output[PACKETLEN*3+2]; size_t i; output[0] = '\0'; for(i = 0; i < len && i < PACKETLEN; i++) { - snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); + snprintf(output + i*3, 4, " %02x", data[i]); } output[len*3+3] = '\0'; diff --git a/dump.h b/dump.h index 9cb040c60e..bb41b3bcd2 100644 --- a/dump.h +++ b/dump.h @@ -20,15 +20,21 @@ void dump_ip(struct iphdr *header); void dump_icmp(struct icmphdr *icmp); -void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const char *payload, size_t payload_size); -void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payload, size_t payload_size, const char *options, size_t options_size); +void dump_udp(const struct udphdr *udp, const struct iphdr *ip, + const uint8_t *payload, size_t payload_size); +void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, + const uint8_t *payload, size_t payload_size, + const char *options, size_t options_size); void dump_ip6(struct ip6_hdr *header); void dump_icmp6(struct icmp6_hdr *icmp6); -void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size); -void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size, const char *options, size_t options_size); +void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, + const uint8_t *payload, size_t payload_size); +void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, + const uint8_t *payload, size_t payload_size, + const char *options, size_t options_size); -void logcat_hexdump(const char *info, const char *data, size_t len); +void logcat_hexdump(const char *info, const uint8_t *data, size_t len); void dump_iovec(const struct iovec *iov, int iov_len); #endif /* __DUMP_H__ */ diff --git a/ipv4.c b/ipv4.c index 84dccd5bc7..2695a860dd 100644 --- a/ipv4.c +++ b/ipv4.c @@ -33,7 +33,7 @@ */ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, size_t len) { - const char *payload; + const uint8_t *payload; size_t payload_size; if(len < sizeof(struct icmphdr)) { @@ -41,7 +41,7 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c return 0; } - payload = (const char *) (icmp + 1); + payload = (const uint8_t *) (icmp + 1); payload_size = len - sizeof(struct icmphdr); return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size); @@ -54,13 +54,13 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c * len - size of packet * returns: the highest position in the output clat_packet that's filled in */ -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { +int ipv4_packet(clat_packet out, int pos, const uint8_t *packet, size_t len) { const struct iphdr *header = (struct iphdr *) packet; struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base; struct ip6_frag *frag_hdr; size_t frag_hdr_len; uint8_t nxthdr; - const char *next_header; + const uint8_t *next_header; size_t len_left; uint32_t old_sum, new_sum; int iov_len; diff --git a/ipv6.c b/ipv6.c index 2e83be137f..d519e1bd69 100644 --- a/ipv6.c +++ b/ipv6.c @@ -35,7 +35,7 @@ * returns: the highest position in the output clat_packet that's filled in */ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { - const char *payload; + const uint8_t *payload; size_t payload_size; if(len < sizeof(struct icmp6_hdr)) { @@ -43,7 +43,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t return 0; } - payload = (const char *) (icmp6 + 1); + payload = (const uint8_t *) (icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); @@ -74,12 +74,12 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i * len - size of packet * returns: the highest position in the output clat_packet that's filled in */ -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { +int ipv6_packet(clat_packet out, int pos, const uint8_t *packet, size_t len) { const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base; struct ip6_frag *frag_hdr = NULL; uint8_t protocol; - const char *next_header; + const uint8_t *next_header; size_t len_left; uint32_t old_sum, new_sum; int iov_len; diff --git a/translate.c b/translate.c index 758509226d..b357057b56 100644 --- a/translate.c +++ b/translate.c @@ -225,7 +225,7 @@ uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ * returns: the highest position in the output clat_packet that's filled in */ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, - const char *payload, size_t payload_size) { + const uint8_t *payload, size_t payload_size) { struct icmp6_hdr *icmp6_targ = out[pos].iov_base; uint8_t icmp6_type; int clat_packet_len; @@ -256,7 +256,7 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t // 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_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; clat_packet_len = CLAT_POS_PAYLOAD + 1; } else { @@ -279,7 +279,7 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * returns: the highest position in the output clat_packet that's filled in */ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, - const char *payload, size_t payload_size) { + const uint8_t *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; int clat_packet_len; @@ -302,7 +302,7 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, // 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_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; clat_packet_len = CLAT_POS_PAYLOAD + 1; } else { @@ -324,9 +324,10 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { +int generic_packet(clat_packet out, int pos, + const uint8_t *payload, size_t len) { out[pos].iov_len = 0; - out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = len; return CLAT_POS_PAYLOAD + 1; @@ -342,7 +343,7 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { */ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, uint32_t new_sum, size_t len) { - const char *payload; + const uint8_t *payload; size_t payload_size; if(len < sizeof(struct udphdr)) { @@ -350,7 +351,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, return 0; } - payload = (const char *) (udp + 1); + payload = (const uint8_t *) (udp + 1); payload_size = len - sizeof(struct udphdr); return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); @@ -366,7 +367,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, */ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t old_sum, uint32_t new_sum, size_t len) { - const char *payload; + const uint8_t *payload; size_t payload_size, header_size; if(len < sizeof(struct tcphdr)) { @@ -385,7 +386,7 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, } header_size = tcp->doff * 4; - payload = ((const char *) tcp) + header_size; + payload = ((const uint8_t *) tcp) + header_size; payload_size = len - header_size; return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); @@ -401,14 +402,15 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, - uint32_t new_sum, const char *payload, size_t payload_size) { +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, + const uint8_t *payload, size_t payload_size) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); out[pos].iov_len = sizeof(struct udphdr); - out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; if (udp_targ->check) { @@ -442,7 +444,8 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * returns: the highest position in the output clat_packet that's filled in */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { + uint32_t old_sum, uint32_t new_sum, + const uint8_t *payload, size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; out[pos].iov_len = header_size; @@ -456,7 +459,7 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea memcpy(tcp_targ, tcp, header_size); - out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); @@ -471,8 +474,8 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea * packet - packet * packetsize - size of packet */ -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize) { +void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, + const uint8_t *packet, size_t packetsize) { int fd; int iov_len = 0; diff --git a/translate.h b/translate.h index 5efa81725b..ab20a55c18 100644 --- a/translate.h +++ b/translate.h @@ -60,12 +60,12 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol const struct iphdr *old_header); // Translate and send packets. -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize); +void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, + const uint8_t *packet, size_t packetsize); // Translate IPv4 and IPv6 packets. -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); +int ipv4_packet(clat_packet out, int pos, const uint8_t *packet, size_t len); +int ipv6_packet(clat_packet out, int pos, const uint8_t *packet, size_t len); // Deal with fragmented packets. size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, @@ -74,12 +74,13 @@ uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ // Translate ICMP packets. int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, - const char *payload, size_t payload_size); + const uint8_t *payload, size_t payload_size); int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, - const char *payload, size_t payload_size); + const uint8_t *payload, size_t payload_size); // Translate generic IP packets. -int generic_packet(clat_packet out, int pos, const char *payload, size_t len); +int generic_packet(clat_packet out, int pos, + const uint8_t *payload, size_t len); // Translate TCP and UDP packets. int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, @@ -88,8 +89,10 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, uint32_t new_sum, size_t len); int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); + uint32_t old_sum, uint32_t new_sum, + const uint8_t *payload, size_t payload_size); int udp_translate(clat_packet out, int pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); + uint32_t old_sum, uint32_t new_sum, + const uint8_t *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ From a4454bfda99803c287b78f8d1cd7bdc1b56065db Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 25 Feb 2014 09:27:31 +0900 Subject: [PATCH 038/261] Declare pos as clat_packet_index instead of int Bug: 11542311 Change-Id: Id5771c9685286b70a8ad283c259c7f83662e8b76 --- ipv4.c | 6 +++--- ipv6.c | 5 +++-- translate.c | 26 ++++++++++++-------------- translate.h | 31 +++++++++++++++---------------- 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/ipv4.c b/ipv4.c index 2695a860dd..4b0db3952c 100644 --- a/ipv4.c +++ b/ipv4.c @@ -31,8 +31,8 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, - size_t len) { +int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp, + uint32_t checksum, size_t len) { const uint8_t *payload; size_t payload_size; @@ -54,7 +54,7 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c * len - size of packet * returns: the highest position in the output clat_packet that's filled in */ -int ipv4_packet(clat_packet out, int pos, const uint8_t *packet, size_t len) { +int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { const struct iphdr *header = (struct iphdr *) packet; struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base; struct ip6_frag *frag_hdr; diff --git a/ipv6.c b/ipv6.c index d519e1bd69..b4853138dd 100644 --- a/ipv6.c +++ b/ipv6.c @@ -34,7 +34,8 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { +int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6, + size_t len) { const uint8_t *payload; size_t payload_size; @@ -74,7 +75,7 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i * len - size of packet * returns: the highest position in the output clat_packet that's filled in */ -int ipv6_packet(clat_packet out, int pos, const uint8_t *packet, size_t len) { +int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base; struct ip6_frag *frag_hdr = NULL; diff --git a/translate.c b/translate.c index b357057b56..e93a93a8af 100644 --- a/translate.c +++ b/translate.c @@ -33,7 +33,7 @@ * 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) { +uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos) { int i; for (i = pos; i < CLAT_POS_MAX; i++) { if (packet[i].iov_len > 0) { @@ -49,7 +49,7 @@ uint16_t packet_checksum(uint32_t checksum, clat_packet packet, int pos) { * 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) { +uint16_t packet_length(clat_packet packet, clat_packet_index pos) { size_t len = 0; int i; for (i = pos + 1; i < CLAT_POS_MAX; i++) { @@ -224,8 +224,8 @@ uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, - const uint8_t *payload, size_t payload_size) { +int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp, + uint32_t checksum, const uint8_t *payload, size_t payload_size) { struct icmp6_hdr *icmp6_targ = out[pos].iov_base; uint8_t icmp6_type; int clat_packet_len; @@ -278,7 +278,7 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, +int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6, const uint8_t *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; @@ -324,8 +324,7 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int generic_packet(clat_packet out, int pos, - const uint8_t *payload, size_t len) { +int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len) { out[pos].iov_len = 0; out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; out[CLAT_POS_PAYLOAD].iov_len = len; @@ -341,7 +340,7 @@ int generic_packet(clat_packet out, int pos, * new_sum - pseudo-header checksum of new header * len - size of ip payload */ -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, +int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum, uint32_t new_sum, size_t len) { const uint8_t *payload; size_t payload_size; @@ -365,7 +364,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, +int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum, uint32_t new_sum, size_t len) { const uint8_t *payload; size_t payload_size, header_size; @@ -402,9 +401,8 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, - const uint8_t *payload, size_t payload_size) { +int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); @@ -443,8 +441,8 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t old_sum, uint32_t new_sum, +int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, + size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; out[pos].iov_len = header_size; diff --git a/translate.h b/translate.h index ab20a55c18..421d2a5920 100644 --- a/translate.h +++ b/translate.h @@ -36,18 +36,18 @@ // 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 { +typedef enum { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, CLAT_POS_PAYLOAD, CLAT_POS_MAX -}; +} clat_packet_index; typedef struct iovec clat_packet[CLAT_POS_MAX]; // Calculates the checksum over all the packet components starting from pos. -uint16_t packet_checksum(uint32_t checksum, clat_packet packet, int pos); +uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos); // Returns the total length of the packet components after pos. -uint16_t packet_length(clat_packet packet, int pos); +uint16_t packet_length(clat_packet packet, clat_packet_index pos); // Returns true iff the given IPv6 address is in the plat subnet. int is_in_plat_subnet(const struct in6_addr *addr6); @@ -64,8 +64,8 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const uint8_t *packet, size_t packetsize); // Translate IPv4 and IPv6 packets. -int ipv4_packet(clat_packet out, int pos, const uint8_t *packet, size_t len); -int ipv6_packet(clat_packet out, int pos, const uint8_t *packet, size_t len); +int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len); +int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len); // Deal with fragmented packets. size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, @@ -73,25 +73,24 @@ size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_tar uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ); // Translate ICMP packets. -int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, - const uint8_t *payload, size_t payload_size); -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, +int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr *icmp, + uint32_t checksum, const uint8_t *payload, size_t payload_size); +int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6, const uint8_t *payload, size_t payload_size); // Translate generic IP packets. -int generic_packet(clat_packet out, int pos, - const uint8_t *payload, size_t len); +int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len); // Translate TCP and UDP packets. -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, +int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum, uint32_t new_sum, size_t len); -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, +int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum, uint32_t new_sum, size_t len); -int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t old_sum, uint32_t new_sum, +int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, + size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size); -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, +int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size); From d6c9bc21e673110aec61157419964b2e57c16078 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 27 Feb 2014 10:12:47 -0800 Subject: [PATCH 039/261] TARGET_SIMULATOR is long dead. Change-Id: I1ece12aac233888c3f0dec8f0beb5a20417a4286 --- Android.mk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Android.mk b/Android.mk index 886c0e08ef..a83cb5ea69 100644 --- a/Android.mk +++ b/Android.mk @@ -26,8 +26,6 @@ include $(BUILD_PREBUILT) # Unit tests. -ifneq ($(TARGET_SIMULATOR),true) - include $(CLEAR_VARS) LOCAL_MODULE := clatd_test @@ -38,5 +36,3 @@ LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_PATH := $(TARGET_OUT)/bin include $(BUILD_NATIVE_TEST) - -endif From ade83087bae9575d3c61f39fba1ffad6442f2d56 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 20 Dec 2013 14:51:26 -0800 Subject: [PATCH 040/261] Add ip6 dummy address to keep data usage stats consistent. Because of the way the tunnel pumps packets into the networking stack, the netfilter xt_qtaguid module can't track stats accurately: the totals don't add up. With "clat" having an ip address, qtaguid will track stats against it, which then can be deducted from the external iface. Bug: 11687690 Change-Id: I22ebf26dd9249e821da87665d2bfb0e54d3cdf64 --- clatd.c | 9 ++++++++- clatd.conf | 3 +++ config.c | 4 ++++ config.h | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 063026d667..26ecb57bd8 100644 --- a/clatd.c +++ b/clatd.c @@ -205,6 +205,13 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } + status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address, + 64, NULL); + if(status < 0) { + logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) 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); @@ -470,7 +477,7 @@ int main(int argc, char **argv) { // 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_open6 failed: %s", strerror(errno)); exit(1); } diff --git a/clatd.conf b/clatd.conf index 086d39ab24..578da5950e 100644 --- a/clatd.conf +++ b/clatd.conf @@ -5,6 +5,9 @@ ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 +# ipv6 extra link local address for the ip6 iface. +ipv6_local_address fe80::c000:0004 + # 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 diff --git a/config.c b/config.c index e7ec80e9ee..61a4ebb441 100644 --- a/config.c +++ b/config.c @@ -255,6 +255,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; + if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address)) + 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); @@ -295,6 +298,7 @@ void dump_config() { 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_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer))); 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))); diff --git a/config.h b/config.h index a83cbecab9..18760c3e5b 100644 --- a/config.h +++ b/config.h @@ -22,10 +22,13 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" +#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004" + #define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" struct clat_config { int16_t mtu, ipv4mtu; + struct in6_addr ipv6_local_address; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; From 86cb1ac3d69805bebcac7a18b408d656603028f4 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 13:33:30 +0900 Subject: [PATCH 041/261] DO NOT MERGE: Remove two almost-unused include files. This fixes a compiler warning. Change-Id: I14b5d3c871e9b2fdc8400d999e76e494ad510328 --- clatd.c | 2 -- ipv4.c | 1 - ipv4.h | 25 ------------------------- ipv6.c | 1 - ipv6.h | 25 ------------------------- translate.h | 4 ++++ 6 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 ipv4.h delete mode 100644 ipv6.h diff --git a/clatd.c b/clatd.c index 26ecb57bd8..1ca46a3252 100644 --- a/clatd.c +++ b/clatd.c @@ -47,8 +47,6 @@ #include -#include "ipv4.h" -#include "ipv6.h" #include "translate.h" #include "clatd.h" #include "config.h" diff --git a/ipv4.c b/ipv4.c index 89e47e475f..47f242279b 100644 --- a/ipv4.c +++ b/ipv4.c @@ -28,7 +28,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv4.h" #include "logging.h" #include "debug.h" #include "dump.h" diff --git a/ipv4.h b/ipv4.h deleted file mode 100644 index 00befd251f..0000000000 --- a/ipv4.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function - */ -#ifndef __IPV4_H__ -#define __IPV4_H__ - -#include "translate.h" - -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c index ef1e62f165..faf27addbb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -29,7 +29,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv6.h" #include "logging.h" #include "dump.h" #include "config.h" diff --git a/ipv6.h b/ipv6.h deleted file mode 100644 index 019de17c91..0000000000 --- a/ipv6.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function - */ -#ifndef __IPV6_H__ -#define __IPV6_H__ - -#include "translate.h" - -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV6_H__ */ diff --git a/translate.h b/translate.h index fded251433..356050f77a 100644 --- a/translate.h +++ b/translate.h @@ -47,6 +47,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate IPv4 and IPv6 packets. +int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); +int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); + // 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); From cb72f3643f475b70089e79108e16621787262e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Thu, 12 Dec 2013 10:28:16 +0100 Subject: [PATCH 042/261] DO NOT MERGE: Remove unused variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused variables -- they cause warnings (and errors with -Werror) when building with clang 3.4. Change-Id: I03fff9b1b9d620763f41c8b31a19c4724726739c Signed-off-by: Bernhard Rosenkränzer --- clatd.c | 2 -- config.c | 2 +- icmp.c | 2 +- ipv6.c | 1 - translate.c | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index 1ca46a3252..43fb5c6131 100644 --- a/clatd.c +++ b/clatd.c @@ -144,7 +144,6 @@ void deconfigure_tun_ipv6(const struct tun_data *tunnel) { * 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, @@ -377,7 +376,6 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { 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) { diff --git a/config.c b/config.c index 61a4ebb441..b82ac6c9d0 100644 --- a/config.c +++ b/config.c @@ -154,7 +154,7 @@ void free_config() { * 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; + int backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; diff --git a/icmp.c b/icmp.c index af96b83582..75a4a4d1c4 100644 --- a/icmp.c +++ b/icmp.c @@ -139,7 +139,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) { } // We don't understand this ICMP type. Return parameter problem so the caller will bail out. - logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type); + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code); return ICMP_PARAMETERPROB; } diff --git a/ipv6.c b/ipv6.c index faf27addbb..d9dcc091ab 100644 --- a/ipv6.c +++ b/ipv6.c @@ -89,7 +89,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { size_t len_left; uint32_t checksum; int iov_len; - int i; if(len < sizeof(struct ip6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); diff --git a/translate.c b/translate.c index fc70f3da0b..71263faf2b 100644 --- a/translate.c +++ b/translate.c @@ -245,7 +245,6 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint3 const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; - int ttl; int clat_packet_len; memset(icmp_targ, 0, sizeof(struct icmphdr)); From d0024fbd9285f473cd0053e4816393dbd0706b85 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 15:56:02 +0900 Subject: [PATCH 043/261] DO NOT MERGE: Fix compiler warnings and enable -Wall -Werror Change-Id: I24847fa35644d92bff0fe31ccfc5c4b3e8705779 --- Android.mk | 1 + clatd.c | 2 +- ipv6.c | 14 +++++++------- translate.c | 5 ++--- translate.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index b0d19d9626..240b25b8a9 100644 --- a/Android.mk +++ b/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c +LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index 43fb5c6131..a0ee07def2 100644 --- a/clatd.c +++ b/clatd.c @@ -82,7 +82,7 @@ void set_forwarding(int fd, const char *setting) { /* function: stop_loop * signal handler: stop the event loop */ -void stop_loop(int signal) { +void stop_loop() { running = 0; } diff --git a/ipv6.c b/ipv6.c index d9dcc091ab..4dff3bc55f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -42,8 +42,7 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, - size_t len) { +int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { const char *payload; size_t payload_size; @@ -55,7 +54,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32 payload = (const char *) (icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); - return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size); + return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); } /* function: log_bad_address @@ -63,16 +62,18 @@ 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 *src, const struct in6_addr *dst) { #if CLAT_DEBUG +void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr)); inet_ntop(AF_INET6, dst, dststr, sizeof(dststr)); logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr); -#endif } +#else +#define log_bad_address(fmt, src, dst) +#endif /* function: ipv6_packet * takes an ipv6 packet and hands it off to the layer 4 protocol function @@ -136,8 +137,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { // does not support IPv6 extension headers, this will drop any packet with them if(protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, checksum, - len_left); + iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if(ip6->ip6_nxt == IPPROTO_TCP) { iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); diff --git a/translate.c b/translate.c index 71263faf2b..599182d7ac 100644 --- a/translate.c +++ b/translate.c @@ -149,7 +149,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, // Third-party ICMPv6 message. This may have been originated by an native IPv6 address. // In that case, the source IPv6 address can't be translated and we need to make up an IPv4 // source address. For now, use 255.0.0., which at least looks useful in traceroute. - if (ip->saddr == (uint32_t) INADDR_NONE) { + if ((uint32_t) ip->saddr == INADDR_NONE) { ttl_guess = icmp_guess_ttl(old_header->ip6_hlim); ip->saddr = htonl((0xff << 24) + ttl_guess); } @@ -236,12 +236,11 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * translate ipv6 icmp to ipv4 icmp * out - output packet * icmp6 - source packet icmp6 header - * checksum - pseudo-header checksum (unused) * payload - icmp6 payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; diff --git a/translate.h b/translate.h index 356050f77a..c4d8ede85e 100644 --- a/translate.h +++ b/translate.h @@ -54,7 +54,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); // 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, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size); // Translate TCP and UDP packets. From f10644ce653e130280e93da8a1096454a4d8d61a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 12:59:44 +0900 Subject: [PATCH 044/261] DO NOT MERGE: Add generic IP packet code and use it for GRE. Bug: 11542311 Change-Id: I174e0b268869bc77927feeee57003580c47c30f2 --- ipv4.c | 8 +++++--- ipv6.c | 8 +++++--- translate.c | 16 ++++++++++++++++ translate.h | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ipv4.c b/ipv4.c index 47f242279b..b5cbf80661 100644 --- a/ipv4.c +++ b/ipv4.c @@ -123,12 +123,14 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { // Calculate the pseudo-header checksum. checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); - if(nxthdr == IPPROTO_ICMPV6) { + if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); - } else if(nxthdr == IPPROTO_TCP) { + } 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) { + } else if (nxthdr == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (nxthdr == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index 4dff3bc55f..79303ec7c9 100644 --- a/ipv6.c +++ b/ipv6.c @@ -136,14 +136,16 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them - if(protocol == IPPROTO_ICMP) { + if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if(ip6->ip6_nxt == IPPROTO_TCP) { + } 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) { + } else if (ip6->ip6_nxt == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (ip6->ip6_nxt == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index 599182d7ac..00ea0b99d8 100644 --- a/translate.c +++ b/translate.c @@ -278,6 +278,22 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, return clat_packet_len; } +/* function: generic_packet + * takes a generic IP packet and sets it up for translation + * out - output packet + * pos - position in the output packet of the transport header + * payload - pointer to IP payload + * len - size of ip payload + * returns: the highest position in the output clat_packet that's filled in + */ +int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { + out[pos].iov_len = 0; + out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_len = len; + + return CLAT_POS_PAYLOAD + 1; +} + /* function: udp_packet * takes a udp packet and sets it up for translation * out - output packet diff --git a/translate.h b/translate.h index c4d8ede85e..9f1ac15a3f 100644 --- a/translate.h +++ b/translate.h @@ -57,6 +57,9 @@ 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, const char *payload, size_t payload_size); +// Translate generic IP packets. +int generic_packet(clat_packet out, int pos, const char *payload, size_t len); + // 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); From 489e108988036facb25c59d59eb5250cf076fd3a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 10 Feb 2014 09:20:05 +0900 Subject: [PATCH 045/261] DO NOT MERGE: Fix up checksums instead of recalculating them. Currently the checksums of translated packets are calculated from scratch by checksumming the translated packet. This is slow and does not work in the case of fragments, because the whole packet is not available. Instead, calculate the checksum by adjusting the checksum of the original packet. Bug: 11542311 Bug: 12116252 Change-Id: I6b78a94ca5bd96b13ee2653b6200551193b3dcc1 --- checksum.c | 38 +++++++++++++++++++++++++++++++++----- checksum.h | 2 ++ ipv4.c | 13 ++++++++----- ipv6.c | 9 +++++---- translate.c | 49 ++++++++++++++++++++++++++++++++----------------- translate.h | 12 +++++++----- 6 files changed, 87 insertions(+), 36 deletions(-) diff --git a/checksum.c b/checksum.c index a4dc9b859c..099be6a0da 100644 --- a/checksum.c +++ b/checksum.c @@ -49,19 +49,27 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { return checksum; } -/* function: ip_checksum_finish - * close the checksum +/* function: ip_checksum_fold + * folds a 32-bit partial checksum into 16 bits * temp_sum - sum from ip_checksum_add + * returns: the folded checksum in network byte order */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { +uint16_t ip_checksum_fold(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_finish + * folds and closes the checksum + * temp_sum - sum from ip_checksum_add + * returns: a header checksum value in network byte order + */ +uint16_t ip_checksum_finish(uint32_t temp_sum) { + return ~ip_checksum_fold(temp_sum); +} + /* function: ip_checksum * combined ip_checksum_add and ip_checksum_finish * data - data to checksum @@ -113,3 +121,23 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u return current; } + +/* function: ip_checksum_adjust + * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums + * checksum - the header checksum in the original packet in network byte order + * old_hdr_sum - the pseudo-header checksum of the original packet + * new_hdr_sum - the pseudo-header checksum of the translated packet + * returns: the new header checksum in network byte order + */ +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { + // Algorithm suggested in RFC 1624. + // http://tools.ietf.org/html/rfc1624#section-3 + checksum = ~checksum; + uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); + uint16_t folded_old = ip_checksum_fold(old_hdr_sum); + if (folded_sum > folded_old) { + return ~(folded_sum - folded_old); + } else { + return ~(folded_sum - folded_old - 1); // end-around borrow + } +} diff --git a/checksum.h b/checksum.h index 473f5f52ed..44921f0d07 100644 --- a/checksum.h +++ b/checksum.h @@ -25,4 +25,6 @@ uint16_t ip_checksum(const void *data, int len); uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); + #endif /* __CHECKSUM_H__ */ diff --git a/ipv4.c b/ipv4.c index b5cbf80661..1d5b0b255c 100644 --- a/ipv4.c +++ b/ipv4.c @@ -70,7 +70,7 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t nxthdr; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct iphdr)) { @@ -121,14 +121,17 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(0, header, len_left); + new_sum = 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); + iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { diff --git a/ipv6.c b/ipv6.c index 79303ec7c9..e4a73fe7cb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -88,7 +88,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t protocol; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct ip6_hdr)) { @@ -133,16 +133,17 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); + new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); diff --git a/translate.c b/translate.c index 00ea0b99d8..9a0f1b5a0d 100644 --- a/translate.c +++ b/translate.c @@ -208,12 +208,10 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t // 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). + // an IPv6 header, which is 20 bytes longer. Fix it up here. // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum). - checksum = htonl(ntohl(checksum) + 20); + checksum = checksum + htons(20); } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) { // Ping packet. icmp6_targ->icmp6_id = icmp->un.echo.id; @@ -298,10 +296,12 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { * takes a udp packet and sets it up for translation * out - output packet * udp - pointer to udp header in packet - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * len - size of ip payload */ -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) { +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size; @@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec payload = (const char *) (udp + 1); payload_size = len - sizeof(struct udphdr); - return udp_translate(out, pos, udp, checksum, payload, payload_size); + return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); } /* function: tcp_packet @@ -324,7 +324,8 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) { +int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size, header_size; @@ -347,20 +348,21 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t chec payload = ((const char *) tcp) + header_size; payload_size = len - header_size; - return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); + return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); } /* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet * out - output packet * udp - udp header - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, - const char *payload, size_t payload_size) { +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, + uint32_t new_sum, const char *payload, size_t payload_size) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); @@ -369,8 +371,22 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c 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); + if (udp_targ->check) { + udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum); + } else { + // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that + // the transmitter generated no checksum (for debugging or for higher level protocols that + // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So + // for safety we recompute it. + udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + udp_targ->check = packet_checksum(new_sum, out, pos); + } + + // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent + // in one's complement arithmetic)." + if (!udp_targ->check) { + udp_targ->check = 0xffff; + } return CLAT_POS_PAYLOAD + 1; } @@ -389,7 +405,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t checksum, const char *payload, size_t payload_size) { + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; out[pos].iov_len = header_size; @@ -406,8 +422,7 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; - tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum. - tcp_targ->check = packet_checksum(checksum, out, pos); + tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); return CLAT_POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 9f1ac15a3f..cfb7bbbf3a 100644 --- a/translate.h +++ b/translate.h @@ -61,12 +61,14 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, int generic_packet(clat_packet out, int pos, const char *payload, size_t len); // 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_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len); +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, 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); + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ From 6c9862b607a198cfd1ee0141e4a19fcaafdff729 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 13 Feb 2014 12:53:35 +0900 Subject: [PATCH 046/261] DO NOT MERGE: Move translation entry point into translate.c. The entry point to the translation code is currently called packet_handler and lives in clatd.c. Move it into translate.c and rename it to translate_packet, since that's what it does. Also get rid of some redundant includes. Bug: 11542311 Change-Id: I8529fb87f3a86ee6724fad54787c33a5e86c56ab --- clatd.c | 66 +---------------------------------------------------- clatd.h | 6 +++++ ipv4.c | 9 -------- ipv6.c | 8 ------- translate.c | 61 +++++++++++++++++++++++++++++++++++++++++-------- translate.h | 14 ++++++++++++ 6 files changed, 73 insertions(+), 91 deletions(-) diff --git a/clatd.c b/clatd.c index a0ee07def2..3ab23711ef 100644 --- a/clatd.c +++ b/clatd.c @@ -29,15 +29,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -63,11 +54,6 @@ 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 */ @@ -305,56 +291,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, configure_tun_ip(tunnel); } -/* function: packet_handler - * takes a tun header and a packet and sends it down the stack - * tunnel - tun device data - * tun_header - tun header - * packet - packet - * packetsize - size of packet - */ -void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize) { - int fd; - int iov_len = 0; - - // Allocate buffers for all packet headers. - struct tun_pi tun_targ; - char iphdr[sizeof(struct ip6_hdr)]; - char transporthdr[MAX_TCP_HDR]; - char icmp_iphdr[sizeof(struct ip6_hdr)]; - char icmp_transporthdr[MAX_TCP_HDR]; - - // iovec of the packets we'll send. This gets passed down to the translation functions. - clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. - { iphdr, 0 }, // IP header. - { transporthdr, 0 }, // Transport layer header. - { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_transporthdr, 0 }, // ICMP error transport layer header. - { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. - }; - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); - iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else { - logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); - } - - if (iov_len > 0) { - writev(fd, out, iov_len); - } -} - /* function: read_packet * reads a packet from the tunnel fd and passes it down the stack * active_fd - tun file descriptor marked ready for reading @@ -383,7 +319,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); } } diff --git a/clatd.h b/clatd.h index 3459b09edb..44a655e0f8 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include #define MAXMTU 1500 @@ -30,4 +31,9 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 +struct tun_data { + char device6[IFNAMSIZ], device4[IFNAMSIZ]; + int fd6, fd4; +}; + #endif /* __CLATD_H__ */ diff --git a/ipv4.c b/ipv4.c index 1d5b0b255c..c828ffa1e0 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,15 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include - #include "translate.h" #include "checksum.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index e4a73fe7cb..371d9e180e 100644 --- a/ipv6.c +++ b/ipv6.c @@ -17,14 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include "translate.h" diff --git a/translate.c b/translate.c index 9a0f1b5a0d..f7f09cbb24 100644 --- a/translate.c +++ b/translate.c @@ -16,15 +16,7 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include - -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "icmp.h" #include "translate.h" @@ -175,6 +167,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } + /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp * out - output packet @@ -426,3 +419,53 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea return CLAT_POS_PAYLOAD + 1; } + +/* function: translate_packet + * 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 translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize) { + int fd; + int iov_len = 0; + + // Allocate buffers for all packet headers. + struct tun_pi tun_targ; + char iphdr[sizeof(struct ip6_hdr)]; + char transporthdr[MAX_TCP_HDR]; + char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_transporthdr[MAX_TCP_HDR]; + + // iovec of the packets we'll send. This gets passed down to the translation functions. + clat_packet out = { + { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { iphdr, 0 }, // IP header. + { transporthdr, 0 }, // Transport layer header. + { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_transporthdr, 0 }, // ICMP error transport layer header. + { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. + }; + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); + } + + if(ntohs(tun_header->proto) == ETH_P_IP) { + fd = tunnel->fd6; + fill_tun_header(&tun_targ, ETH_P_IPV6); + iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { + fd = tunnel->fd4; + fill_tun_header(&tun_targ, ETH_P_IP); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else { + logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); + } + + if (iov_len > 0) { + writev(fd, out, iov_len); + } +} diff --git a/translate.h b/translate.h index cfb7bbbf3a..3378254af8 100644 --- a/translate.h +++ b/translate.h @@ -18,8 +18,18 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ +#include +#include +#include +#include +#include +#include +#include +#include #include +#include "clatd.h" + #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. // A clat_packet is an array of iovec structures representing a packet that we are translating. @@ -47,6 +57,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate and send packets. +void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize); + // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); From fea4efa398ff9c86c300f0c1be92611d1d1fd5ba Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 20 Feb 2014 14:28:43 +0900 Subject: [PATCH 047/261] DO NOT MERGE: Modify the pseudo-header checksum functions. - Remove the initial checksum, which we don't use anywhere. - Add the upper-layer protocol to the IPv6 function. Currently this is always the same as the next header field in the IPv6 header, but technically it's the upper-layer header after all the extension headers. This is required to support fragments. Bug: 11542311 Change-Id: Ie1a20fa74ee5bc933c1014bab74ae2957979b2b8 --- checksum.c | 16 ++++++++-------- checksum.h | 4 ++-- dump.c | 8 ++++---- ipv4.c | 4 ++-- ipv6.c | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/checksum.c b/checksum.c index 099be6a0da..3dd1e00bef 100644 --- a/checksum.c +++ b/checksum.c @@ -84,16 +84,16 @@ 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 - the current checksum or 0 to start a new checksum - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) + * ip6 - the ipv6 header + * len - the transport length (transport header + payload) + * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments */ -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) { +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t) len); - checksum_next = htonl(ip6->ip6_nxt); + checksum_next = htonl(protocol); + uint32_t current = 0; current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); @@ -104,16 +104,16 @@ uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6 /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers - * current - the current checksum or 0 to start a new checksum * ip - the ipv4 header * len - the transport length (transport header + payload) */ -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) { +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { uint16_t temp_protocol, temp_length; temp_protocol = htons(ip->protocol); temp_length = htons(len); + uint32_t current = 0; current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); diff --git a/checksum.h b/checksum.h index 44921f0d07..619581011d 100644 --- a/checksum.h +++ b/checksum.h @@ -22,8 +22,8 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len); uint16_t ip_checksum_finish(uint32_t temp_sum); uint16_t ip_checksum(const void *data, int len); -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol); +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len); uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); diff --git a/dump.c b/dump.c index 856765551e..94e4796d49 100644 --- a/dump.c +++ b/dump.c @@ -147,14 +147,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, sizeof(*udp) + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print ipv6/udp header */ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size) { uint32_t temp_checksum; - temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); dump_udp_generic(udp, temp_checksum, payload, payload_size); } @@ -203,7 +203,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } @@ -211,7 +211,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } diff --git a/ipv4.c b/ipv4.c index c828ffa1e0..e2636b4b3a 100644 --- a/ipv4.c +++ b/ipv4.c @@ -112,8 +112,8 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - old_sum = ipv4_pseudo_header_checksum(0, header, len_left); - new_sum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(header, len_left); + new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); diff --git a/ipv6.c b/ipv6.c index 371d9e180e..d188e47550 100644 --- a/ipv6.c +++ b/ipv6.c @@ -125,8 +125,8 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); - new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); + new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { From 866b4af18d8e9ef1020adc9accbb9f9de4c873dc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sun, 9 Feb 2014 10:35:38 +0900 Subject: [PATCH 048/261] DO NOT MERGE: Support translating fragmented packets. Bug: 11542311 Change-Id: I14a20b9ac669cdb5927f6ac26147bb0109099497 --- clatd.c | 8 ++++++-- dump.c | 1 + ipv4.c | 33 +++++++++++++++++++------------- ipv6.c | 52 +++++++++++++++++++++++++++++++++++++-------------- translate.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++--- translate.h | 13 ++++++++++--- 6 files changed, 126 insertions(+), 35 deletions(-) diff --git a/clatd.c b/clatd.c index 3ab23711ef..a89c588780 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,9 @@ #define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" +/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ +#define MTU_DELTA 28 + int forwarding_fd = -1; volatile sig_atomic_t running = 1; @@ -271,8 +274,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, 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; + if(Global_Clatd_Config.ipv4mtu <= 0 || + Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { + Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } diff --git a/dump.c b/dump.c index 94e4796d49..ba5fa3e35a 100644 --- a/dump.c +++ b/dump.c @@ -220,6 +220,7 @@ void logcat_hexdump(const char *info, const char *data, size_t len) { char output[PACKETLEN*3+2]; size_t i; + output[0] = '\0'; for(i = 0; i < len && i < PACKETLEN; i++) { snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); } diff --git a/ipv4.c b/ipv4.c index e2636b4b3a..84dccd5bc7 100644 --- a/ipv4.c +++ b/ipv4.c @@ -57,7 +57,8 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c 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; + struct ip6_frag *frag_hdr; + size_t frag_hdr_len; uint8_t nxthdr; const char *next_header; size_t len_left; @@ -69,12 +70,6 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { return 0; } - frag_flags = ntohs(header->frag_off); - if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't - logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping"); - return 0; - } - if(header->ihl < 5) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl); return 0; @@ -111,20 +106,32 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip6_header(ip6_targ, 0, nxthdr, header); out[pos].iov_len = sizeof(struct ip6_hdr); - // Calculate the pseudo-header checksum. + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv4_pseudo_header_checksum(header, len_left); new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); - if (nxthdr == IPPROTO_ICMPV6) { - iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); + // If the IPv4 packet is fragmented, add a Fragment header. + frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base; + frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); + out[pos + 1].iov_len = frag_hdr_len; + + if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) { + // Non-first fragment. Copy the rest of the packet as is. + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (nxthdr == IPPROTO_ICMPV6) { + iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index d188e47550..2e83be137f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -77,6 +77,7 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i 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; + struct ip6_frag *frag_hdr = NULL; uint8_t protocol; const char *next_header; size_t len_left; @@ -112,10 +113,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { 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 @@ -124,21 +121,48 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip_header(ip_targ, 0, protocol, ip6); out[pos].iov_len = sizeof(struct iphdr); - // Calculate the pseudo-header checksum. + // If there's a Fragment header, parse it and decide what the next header is. + // Do this before calculating the pseudo-header checksum because it updates the next header value. + if (protocol == IPPROTO_FRAGMENT) { + frag_hdr = (struct ip6_frag *) next_header; + if (len_left < sizeof(*frag_hdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); + return 0; + } + + next_header += sizeof(*frag_hdr); + len_left -= sizeof(*frag_hdr); + + protocol = parse_frag_header(frag_hdr, ip_targ); + } + + // ICMP and ICMPv6 have different protocol numbers. + if (protocol == IPPROTO_ICMPV6) { + protocol = IPPROTO_ICMP; + ip_targ->protocol = IPPROTO_ICMP; + } + + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); - // does not support IPv6 extension headers, this will drop any packet with them - if (protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + // Does not support IPv6 extension headers except Fragment. + if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (protocol == IPPROTO_ICMP) { + iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left); + } else if (protocol == IPPROTO_TCP) { + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + } else if (protocol == IPPROTO_UDP) { + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + } else if (protocol == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index f7f09cbb24..758509226d 100644 --- a/translate.c +++ b/translate.c @@ -167,6 +167,53 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } +/* function: maybe_fill_frag_header + * fills a fragmentation header + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - target (ipv6) fragmentation header + * ip6_targ - target (ipv6) header + * old_header - (ipv4) source packet header + * returns: the length of the fragmentation header if present, or zero if not present + */ +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header) { + uint16_t frag_flags = ntohs(old_header->frag_off); + uint16_t frag_off = frag_flags & IP_OFFMASK; + if (frag_off == 0 && (frag_flags & IP_MF) == 0) { + // Not a fragment. + return 0; + } + + frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; + frag_hdr->ip6f_reserved = 0; + // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits. + frag_hdr->ip6f_offlg = htons(frag_off << 3); + if (frag_flags & IP_MF) { + frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG; + } + frag_hdr->ip6f_ident = htonl(ntohs(old_header->id)); + ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; + + return sizeof(*frag_hdr); +} + +/* function: parse_frag_header + * return the length of the fragmentation header if present, or zero if not present + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - (ipv6) fragmentation header + * ip_targ - target (ipv4) header + * returns: the next header value + */ +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) { + uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3); + if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) { + frag_off |= IP_MF; + } + ip_targ->frag_off = htons(frag_off); + ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); + ip_targ->protocol = frag_hdr->ip6f_nxt; + return frag_hdr->ip6f_nxt; +} /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp @@ -393,9 +440,6 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in - * - * TODO: mss rewrite - * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { @@ -435,16 +479,20 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, // Allocate buffers for all packet headers. struct tun_pi tun_targ; char iphdr[sizeof(struct ip6_hdr)]; + char fraghdr[sizeof(struct ip6_frag)]; char transporthdr[MAX_TCP_HDR]; char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_fraghdr[sizeof(struct ip6_frag)]; char icmp_transporthdr[MAX_TCP_HDR]; // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { { &tun_targ, sizeof(tun_targ) }, // Tunnel header. { iphdr, 0 }, // IP header. + { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_fraghdr, 0 }, // ICMP error fragmentation header. { icmp_transporthdr, 0 }, // ICMP error transport layer header. { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; diff --git a/translate.h b/translate.h index 3378254af8..5efa81725b 100644 --- a/translate.h +++ b/translate.h @@ -36,9 +36,11 @@ // The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain // specific parts of the packet. The packet_* functions operate on all the packet segments past a // given position. -enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX }; +enum clat_packet_index { + CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX +}; typedef struct iovec clat_packet[CLAT_POS_MAX]; // Calculates the checksum over all the packet components starting from pos. @@ -65,6 +67,11 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); +// Deal with fragmented packets. +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header); +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ); + // Translate ICMP packets. int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, const char *payload, size_t payload_size); From f085c7aa127ca770f8ee16d9714e7504cffe6499 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:29:32 +0000 Subject: [PATCH 049/261] Revert "DO NOT MERGE: Remove two almost-unused include files." This reverts commit 86cb1ac3d69805bebcac7a18b408d656603028f4. Change-Id: If592b80e8107f468c2fe1caf00fcff49f25a9441 --- clatd.c | 2 ++ ipv4.c | 1 + ipv4.h | 25 +++++++++++++++++++++++++ ipv6.c | 1 + ipv6.h | 25 +++++++++++++++++++++++++ translate.h | 4 ---- 6 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 ipv4.h create mode 100644 ipv6.h diff --git a/clatd.c b/clatd.c index 1ca46a3252..26ecb57bd8 100644 --- a/clatd.c +++ b/clatd.c @@ -47,6 +47,8 @@ #include +#include "ipv4.h" +#include "ipv6.h" #include "translate.h" #include "clatd.h" #include "config.h" diff --git a/ipv4.c b/ipv4.c index 47f242279b..89e47e475f 100644 --- a/ipv4.c +++ b/ipv4.c @@ -28,6 +28,7 @@ #include "translate.h" #include "checksum.h" +#include "ipv4.h" #include "logging.h" #include "debug.h" #include "dump.h" diff --git a/ipv4.h b/ipv4.h new file mode 100644 index 0000000000..00befd251f --- /dev/null +++ b/ipv4.h @@ -0,0 +1,25 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function + */ +#ifndef __IPV4_H__ +#define __IPV4_H__ + +#include "translate.h" + +int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); + +#endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c index faf27addbb..ef1e62f165 100644 --- a/ipv6.c +++ b/ipv6.c @@ -29,6 +29,7 @@ #include "translate.h" #include "checksum.h" +#include "ipv6.h" #include "logging.h" #include "dump.h" #include "config.h" diff --git a/ipv6.h b/ipv6.h new file mode 100644 index 0000000000..019de17c91 --- /dev/null +++ b/ipv6.h @@ -0,0 +1,25 @@ +/* + * Copyright 2011 Daniel Drown + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function + */ +#ifndef __IPV6_H__ +#define __IPV6_H__ + +#include "translate.h" + +int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); + +#endif /* __IPV6_H__ */ diff --git a/translate.h b/translate.h index 356050f77a..fded251433 100644 --- a/translate.h +++ b/translate.h @@ -47,10 +47,6 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); -// Translate IPv4 and IPv6 packets. -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); - // 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); From 88ee177670cc651784e1c58fac814202e63414ac Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:29:41 +0000 Subject: [PATCH 050/261] Revert "DO NOT MERGE: Remove unused variables" This reverts commit cb72f3643f475b70089e79108e16621787262e2b. Change-Id: I10196a15fe92e281d4922f384f373a964ff703ec --- clatd.c | 2 ++ config.c | 2 +- icmp.c | 2 +- ipv6.c | 1 + translate.c | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clatd.c b/clatd.c index 43fb5c6131..1ca46a3252 100644 --- a/clatd.c +++ b/clatd.c @@ -144,6 +144,7 @@ void deconfigure_tun_ipv6(const struct tun_data *tunnel) { * 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, @@ -376,6 +377,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { 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) { diff --git a/config.c b/config.c index b82ac6c9d0..61a4ebb441 100644 --- a/config.c +++ b/config.c @@ -154,7 +154,7 @@ void free_config() { * 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 backoff_sleep, status; + int i, backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; diff --git a/icmp.c b/icmp.c index 75a4a4d1c4..af96b83582 100644 --- a/icmp.c +++ b/icmp.c @@ -139,7 +139,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) { } // We don't understand this ICMP type. Return parameter problem so the caller will bail out. - logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code); + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type); return ICMP_PARAMETERPROB; } diff --git a/ipv6.c b/ipv6.c index d9dcc091ab..faf27addbb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -89,6 +89,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { size_t len_left; uint32_t checksum; int iov_len; + int i; if(len < sizeof(struct ip6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); diff --git a/translate.c b/translate.c index 71263faf2b..fc70f3da0b 100644 --- a/translate.c +++ b/translate.c @@ -245,6 +245,7 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint3 const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; + int ttl; int clat_packet_len; memset(icmp_targ, 0, sizeof(struct icmphdr)); From 5b29cefbea5b612b8cfff4b60e7481185133d005 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:29:48 +0000 Subject: [PATCH 051/261] Revert "DO NOT MERGE: Fix compiler warnings and enable -Wall -Werror" This reverts commit d0024fbd9285f473cd0053e4816393dbd0706b85. Change-Id: I59201406f1f404b97c48877863835bdaeefc33e2 --- Android.mk | 1 - clatd.c | 2 +- ipv6.c | 14 +++++++------- translate.c | 5 +++-- translate.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index 240b25b8a9..b0d19d9626 100644 --- a/Android.mk +++ b/Android.mk @@ -3,7 +3,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c -LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index a0ee07def2..43fb5c6131 100644 --- a/clatd.c +++ b/clatd.c @@ -82,7 +82,7 @@ void set_forwarding(int fd, const char *setting) { /* function: stop_loop * signal handler: stop the event loop */ -void stop_loop() { +void stop_loop(int signal) { running = 0; } diff --git a/ipv6.c b/ipv6.c index 4dff3bc55f..d9dcc091ab 100644 --- a/ipv6.c +++ b/ipv6.c @@ -42,7 +42,8 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { +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; @@ -54,7 +55,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t payload = (const char *) (icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); - return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); + return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size); } /* function: log_bad_address @@ -62,18 +63,16 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t * fmt - printf-style format, use %s to place the address * badaddr - the bad address in question */ -#if CLAT_DEBUG void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { +#if CLAT_DEBUG char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr)); inet_ntop(AF_INET6, dst, dststr, sizeof(dststr)); logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr); -} -#else -#define log_bad_address(fmt, src, dst) #endif +} /* function: ipv6_packet * takes an ipv6 packet and hands it off to the layer 4 protocol function @@ -137,7 +136,8 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { // does not support IPv6 extension headers, this will drop any packet with them if(protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); + 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); diff --git a/translate.c b/translate.c index 599182d7ac..71263faf2b 100644 --- a/translate.c +++ b/translate.c @@ -149,7 +149,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, // Third-party ICMPv6 message. This may have been originated by an native IPv6 address. // In that case, the source IPv6 address can't be translated and we need to make up an IPv4 // source address. For now, use 255.0.0., which at least looks useful in traceroute. - if ((uint32_t) ip->saddr == INADDR_NONE) { + if (ip->saddr == (uint32_t) INADDR_NONE) { ttl_guess = icmp_guess_ttl(old_header->ip6_hlim); ip->saddr = htonl((0xff << 24) + ttl_guess); } @@ -236,11 +236,12 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * translate ipv6 icmp to ipv4 icmp * out - output packet * icmp6 - source packet icmp6 header + * checksum - pseudo-header checksum (unused) * payload - icmp6 payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; diff --git a/translate.h b/translate.h index c4d8ede85e..356050f77a 100644 --- a/translate.h +++ b/translate.h @@ -54,7 +54,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); // 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, +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. From 5658b2563f0a939e424cd7ef34643cf5141a6bf1 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:29:52 +0000 Subject: [PATCH 052/261] Revert "DO NOT MERGE: Add generic IP packet code and use it for GRE." This reverts commit f10644ce653e130280e93da8a1096454a4d8d61a. Change-Id: Id70d629038e5269c64580b9a89f35179bc7c6926 --- ipv4.c | 8 +++----- ipv6.c | 8 +++----- translate.c | 16 ---------------- translate.h | 3 --- 4 files changed, 6 insertions(+), 29 deletions(-) diff --git a/ipv4.c b/ipv4.c index b5cbf80661..47f242279b 100644 --- a/ipv4.c +++ b/ipv4.c @@ -123,14 +123,12 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { // Calculate the pseudo-header checksum. checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); - if (nxthdr == IPPROTO_ICMPV6) { + if(nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); - } else if (nxthdr == IPPROTO_TCP) { + } 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) { + } else if(nxthdr == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); - } else if (nxthdr == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index 79303ec7c9..4dff3bc55f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -136,16 +136,14 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them - if (protocol == IPPROTO_ICMP) { + if(protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if (ip6->ip6_nxt == IPPROTO_TCP) { + } 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) { + } else if(ip6->ip6_nxt == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index 00ea0b99d8..599182d7ac 100644 --- a/translate.c +++ b/translate.c @@ -278,22 +278,6 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, return clat_packet_len; } -/* function: generic_packet - * takes a generic IP packet and sets it up for translation - * out - output packet - * pos - position in the output packet of the transport header - * payload - pointer to IP payload - * len - size of ip payload - * returns: the highest position in the output clat_packet that's filled in - */ -int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { - out[pos].iov_len = 0; - out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; - out[CLAT_POS_PAYLOAD].iov_len = len; - - return CLAT_POS_PAYLOAD + 1; -} - /* function: udp_packet * takes a udp packet and sets it up for translation * out - output packet diff --git a/translate.h b/translate.h index 9f1ac15a3f..c4d8ede85e 100644 --- a/translate.h +++ b/translate.h @@ -57,9 +57,6 @@ 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, const char *payload, size_t payload_size); -// Translate generic IP packets. -int generic_packet(clat_packet out, int pos, const char *payload, size_t len); - // 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); From adf87248150188a549dff15d843ce57ae7925b6f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:29:55 +0000 Subject: [PATCH 053/261] Revert "DO NOT MERGE: Fix up checksums instead of recalculating them." This reverts commit 489e108988036facb25c59d59eb5250cf076fd3a. Change-Id: I39e24afd8e9f1c862c0b7eea872c4fe31240aecf --- checksum.c | 38 +++++--------------------------------- checksum.h | 2 -- ipv4.c | 13 +++++-------- ipv6.c | 9 ++++----- translate.c | 49 +++++++++++++++++-------------------------------- translate.h | 12 +++++------- 6 files changed, 36 insertions(+), 87 deletions(-) diff --git a/checksum.c b/checksum.c index 099be6a0da..a4dc9b859c 100644 --- a/checksum.c +++ b/checksum.c @@ -49,25 +49,17 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { return checksum; } -/* function: ip_checksum_fold - * folds a 32-bit partial checksum into 16 bits +/* function: ip_checksum_finish + * close the checksum * temp_sum - sum from ip_checksum_add - * returns: the folded checksum in network byte order */ -uint16_t ip_checksum_fold(uint32_t temp_sum) { +uint16_t ip_checksum_finish(uint32_t temp_sum) { while(temp_sum > 0xffff) temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); - return temp_sum; -} + temp_sum = (~temp_sum) & 0xffff; -/* function: ip_checksum_finish - * folds and closes the checksum - * temp_sum - sum from ip_checksum_add - * returns: a header checksum value in network byte order - */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { - return ~ip_checksum_fold(temp_sum); + return temp_sum; } /* function: ip_checksum @@ -121,23 +113,3 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u return current; } - -/* function: ip_checksum_adjust - * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums - * checksum - the header checksum in the original packet in network byte order - * old_hdr_sum - the pseudo-header checksum of the original packet - * new_hdr_sum - the pseudo-header checksum of the translated packet - * returns: the new header checksum in network byte order - */ -uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { - // Algorithm suggested in RFC 1624. - // http://tools.ietf.org/html/rfc1624#section-3 - checksum = ~checksum; - uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); - uint16_t folded_old = ip_checksum_fold(old_hdr_sum); - if (folded_sum > folded_old) { - return ~(folded_sum - folded_old); - } else { - return ~(folded_sum - folded_old - 1); // end-around borrow - } -} diff --git a/checksum.h b/checksum.h index 44921f0d07..473f5f52ed 100644 --- a/checksum.h +++ b/checksum.h @@ -25,6 +25,4 @@ uint16_t ip_checksum(const void *data, int len); uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); -uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); - #endif /* __CHECKSUM_H__ */ diff --git a/ipv4.c b/ipv4.c index 1d5b0b255c..b5cbf80661 100644 --- a/ipv4.c +++ b/ipv4.c @@ -70,7 +70,7 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t nxthdr; const char *next_header; size_t len_left; - uint32_t old_sum, new_sum; + uint32_t checksum; int iov_len; if(len < sizeof(struct iphdr)) { @@ -121,17 +121,14 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - old_sum = ipv4_pseudo_header_checksum(0, header, len_left); - new_sum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + 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, new_sum, len_left); + 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, old_sum, new_sum, - len_left); + 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, old_sum, new_sum, - len_left); + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); } else if (nxthdr == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { diff --git a/ipv6.c b/ipv6.c index e4a73fe7cb..79303ec7c9 100644 --- a/ipv6.c +++ b/ipv6.c @@ -88,7 +88,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t protocol; const char *next_header; size_t len_left; - uint32_t old_sum, new_sum; + uint32_t checksum; int iov_len; if(len < sizeof(struct ip6_hdr)) { @@ -133,17 +133,16 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); - new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + 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, old_sum, new_sum, + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); } else if (ip6->ip6_nxt == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); diff --git a/translate.c b/translate.c index 9a0f1b5a0d..00ea0b99d8 100644 --- a/translate.c +++ b/translate.c @@ -208,10 +208,12 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t // 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. + // 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 = checksum + htons(20); + 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; @@ -296,12 +298,10 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { * takes a udp packet and sets it up for translation * out - output packet * udp - pointer to udp header in packet - * old_sum - pseudo-header checksum of old header - * new_sum - pseudo-header checksum of new header + * checksum - pseudo-header checksum * len - size of ip payload */ -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, size_t len) { +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; @@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, payload = (const char *) (udp + 1); payload_size = len - sizeof(struct udphdr); - return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); + return udp_translate(out, pos, udp, checksum, payload, payload_size); } /* function: tcp_packet @@ -324,8 +324,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, - uint32_t old_sum, uint32_t new_sum, size_t len) { +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; @@ -348,21 +347,20 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, payload = ((const char *) tcp) + header_size; payload_size = len - header_size; - return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); + return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); } /* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet * out - output packet * udp - udp header - * old_sum - pseudo-header checksum of old header - * new_sum - pseudo-header checksum of new header + * checksum - pseudo-header checksum * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, - uint32_t new_sum, 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) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); @@ -371,22 +369,8 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; - if (udp_targ->check) { - udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum); - } else { - // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that - // the transmitter generated no checksum (for debugging or for higher level protocols that - // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So - // for safety we recompute it. - udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. - udp_targ->check = packet_checksum(new_sum, out, pos); - } - - // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent - // in one's complement arithmetic)." - if (!udp_targ->check) { - udp_targ->check = 0xffff; - } + udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + udp_targ->check = packet_checksum(checksum, out, pos); return CLAT_POS_PAYLOAD + 1; } @@ -405,7 +389,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_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; @@ -422,7 +406,8 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; - tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); + tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + tcp_targ->check = packet_checksum(checksum, out, pos); return CLAT_POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index cfb7bbbf3a..9f1ac15a3f 100644 --- a/translate.h +++ b/translate.h @@ -61,14 +61,12 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, int generic_packet(clat_packet out, int pos, const char *payload, size_t len); // Translate TCP and UDP packets. -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, - uint32_t old_sum, uint32_t new_sum, size_t len); -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, size_t len); +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 old_sum, uint32_t new_sum, const char *payload, size_t payload_size); -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_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__ */ From 19b68f9d3969c17bdd3dfe608d4a89d6b201edef Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:30:00 +0000 Subject: [PATCH 054/261] Revert "DO NOT MERGE: Move translation entry point into translate.c." This reverts commit 6c9862b607a198cfd1ee0141e4a19fcaafdff729. Change-Id: I4e55606814e650116e62ecb1f1d9a5a09c7ba2e0 --- clatd.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++- clatd.h | 6 ----- ipv4.c | 9 ++++++++ ipv6.c | 8 +++++++ translate.c | 61 ++++++++----------------------------------------- translate.h | 14 ------------ 6 files changed, 91 insertions(+), 73 deletions(-) diff --git a/clatd.c b/clatd.c index 3ab23711ef..a0ee07def2 100644 --- a/clatd.c +++ b/clatd.c @@ -29,6 +29,15 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -54,6 +63,11 @@ 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 */ @@ -291,6 +305,56 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, configure_tun_ip(tunnel); } +/* function: packet_handler + * takes a tun header and a packet and sends it down the stack + * tunnel - tun device data + * tun_header - tun header + * packet - packet + * packetsize - size of packet + */ +void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize) { + int fd; + int iov_len = 0; + + // Allocate buffers for all packet headers. + struct tun_pi tun_targ; + char iphdr[sizeof(struct ip6_hdr)]; + char transporthdr[MAX_TCP_HDR]; + char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_transporthdr[MAX_TCP_HDR]; + + // iovec of the packets we'll send. This gets passed down to the translation functions. + clat_packet out = { + { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { iphdr, 0 }, // IP header. + { transporthdr, 0 }, // Transport layer header. + { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_transporthdr, 0 }, // ICMP error transport layer header. + { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. + }; + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags); + } + + if(ntohs(tun_header->proto) == ETH_P_IP) { + fd = tunnel->fd6; + fill_tun_header(&tun_targ, ETH_P_IPV6); + iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { + fd = tunnel->fd4; + fill_tun_header(&tun_targ, ETH_P_IP); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else { + logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); + } + + if (iov_len > 0) { + writev(fd, out, iov_len); + } +} + /* function: read_packet * reads a packet from the tunnel fd and passes it down the stack * active_fd - tun file descriptor marked ready for reading @@ -319,7 +383,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); } } diff --git a/clatd.h b/clatd.h index 44a655e0f8..3459b09edb 100644 --- a/clatd.h +++ b/clatd.h @@ -18,7 +18,6 @@ #ifndef __CLATD_H__ #define __CLATD_H__ -#include #include #define MAXMTU 1500 @@ -31,9 +30,4 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 -struct tun_data { - char device6[IFNAMSIZ], device4[IFNAMSIZ]; - int fd6, fd4; -}; - #endif /* __CLATD_H__ */ diff --git a/ipv4.c b/ipv4.c index c828ffa1e0..1d5b0b255c 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,6 +17,15 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include + #include "translate.h" #include "checksum.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index 371d9e180e..e4a73fe7cb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -17,6 +17,14 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "translate.h" diff --git a/translate.c b/translate.c index f7f09cbb24..9a0f1b5a0d 100644 --- a/translate.c +++ b/translate.c @@ -16,7 +16,15 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include #include "icmp.h" #include "translate.h" @@ -167,7 +175,6 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } - /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp * out - output packet @@ -419,53 +426,3 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea return CLAT_POS_PAYLOAD + 1; } - -/* function: translate_packet - * 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 translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize) { - int fd; - int iov_len = 0; - - // Allocate buffers for all packet headers. - struct tun_pi tun_targ; - char iphdr[sizeof(struct ip6_hdr)]; - char transporthdr[MAX_TCP_HDR]; - char icmp_iphdr[sizeof(struct ip6_hdr)]; - char icmp_transporthdr[MAX_TCP_HDR]; - - // iovec of the packets we'll send. This gets passed down to the translation functions. - clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. - { iphdr, 0 }, // IP header. - { transporthdr, 0 }, // Transport layer header. - { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_transporthdr, 0 }, // ICMP error transport layer header. - { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. - }; - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); - iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else { - logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); - } - - if (iov_len > 0) { - writev(fd, out, iov_len); - } -} diff --git a/translate.h b/translate.h index 3378254af8..cfb7bbbf3a 100644 --- a/translate.h +++ b/translate.h @@ -18,18 +18,8 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ -#include -#include -#include -#include -#include -#include -#include -#include #include -#include "clatd.h" - #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. // A clat_packet is an array of iovec structures representing a packet that we are translating. @@ -57,10 +47,6 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); -// Translate and send packets. -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize); - // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); From 09c026be0c23e3ab8086603a73c5ec5ff2af92b5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:30:03 +0000 Subject: [PATCH 055/261] Revert "DO NOT MERGE: Modify the pseudo-header checksum functions." This reverts commit fea4efa398ff9c86c300f0c1be92611d1d1fd5ba. Change-Id: If8199246b3f921e862a5e5ca587d5120906946e0 --- checksum.c | 18 +++++++++--------- checksum.h | 4 ++-- dump.c | 8 ++++---- ipv4.c | 4 ++-- ipv6.c | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/checksum.c b/checksum.c index 3dd1e00bef..099be6a0da 100644 --- a/checksum.c +++ b/checksum.c @@ -84,16 +84,16 @@ 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 - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) - * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments + * 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(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { +uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) { uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t) len); - checksum_next = htonl(protocol); - uint32_t current = 0; + checksum_len = htonl((uint32_t) len); + checksum_next = htonl(ip6->ip6_nxt); + current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); @@ -104,16 +104,16 @@ uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, ui /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers + * current - the current checksum or 0 to start a new checksum * ip - the ipv4 header * len - the transport length (transport header + payload) */ -uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { +uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) { uint16_t temp_protocol, temp_length; temp_protocol = htons(ip->protocol); temp_length = htons(len); - uint32_t current = 0; current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); diff --git a/checksum.h b/checksum.h index 619581011d..44921f0d07 100644 --- a/checksum.h +++ b/checksum.h @@ -22,8 +22,8 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len); uint16_t ip_checksum_finish(uint32_t temp_sum); uint16_t ip_checksum(const void *data, int len); -uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol); -uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len); +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); uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); diff --git a/dump.c b/dump.c index 94e4796d49..856765551e 100644 --- a/dump.c +++ b/dump.c @@ -147,14 +147,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(ip, sizeof(*udp) + payload_size); + 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(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); + temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } @@ -203,7 +203,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(ip, sizeof(*tcp) + options_size + payload_size); + 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); } @@ -211,7 +211,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(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); + 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/ipv4.c b/ipv4.c index e2636b4b3a..c828ffa1e0 100644 --- a/ipv4.c +++ b/ipv4.c @@ -112,8 +112,8 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - old_sum = ipv4_pseudo_header_checksum(header, len_left); - new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); + old_sum = ipv4_pseudo_header_checksum(0, header, len_left); + new_sum = 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, new_sum, len_left); diff --git a/ipv6.c b/ipv6.c index d188e47550..371d9e180e 100644 --- a/ipv6.c +++ b/ipv6.c @@ -125,8 +125,8 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); - new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); + new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { From 2636ea7d40cb9ac04571d580df62e2ebfe269d7d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 8 Mar 2014 20:30:06 +0000 Subject: [PATCH 056/261] Revert "DO NOT MERGE: Support translating fragmented packets." This reverts commit 866b4af18d8e9ef1020adc9accbb9f9de4c873dc. Change-Id: I864dc0cbf3ad0dc04b86ed4e84a46aaf02778429 --- clatd.c | 8 ++------ dump.c | 1 - ipv4.c | 33 +++++++++++++------------------- ipv6.c | 52 ++++++++++++++------------------------------------- translate.c | 54 +++-------------------------------------------------- translate.h | 13 +++---------- 6 files changed, 35 insertions(+), 126 deletions(-) diff --git a/clatd.c b/clatd.c index a89c588780..3ab23711ef 100644 --- a/clatd.c +++ b/clatd.c @@ -51,9 +51,6 @@ #define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" -/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ -#define MTU_DELTA 28 - int forwarding_fd = -1; volatile sig_atomic_t running = 1; @@ -274,9 +271,8 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, Global_Clatd_Config.mtu = 1280; } - if(Global_Clatd_Config.ipv4mtu <= 0 || - Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { - Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; + 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); } diff --git a/dump.c b/dump.c index ba5fa3e35a..94e4796d49 100644 --- a/dump.c +++ b/dump.c @@ -220,7 +220,6 @@ void logcat_hexdump(const char *info, const char *data, size_t len) { char output[PACKETLEN*3+2]; size_t i; - output[0] = '\0'; for(i = 0; i < len && i < PACKETLEN; i++) { snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); } diff --git a/ipv4.c b/ipv4.c index 84dccd5bc7..e2636b4b3a 100644 --- a/ipv4.c +++ b/ipv4.c @@ -57,8 +57,7 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c 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; - struct ip6_frag *frag_hdr; - size_t frag_hdr_len; + uint16_t frag_flags; uint8_t nxthdr; const char *next_header; size_t len_left; @@ -70,6 +69,12 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { return 0; } + frag_flags = ntohs(header->frag_off); + if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't + logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping"); + return 0; + } + if(header->ihl < 5) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl); return 0; @@ -106,32 +111,20 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip6_header(ip6_targ, 0, nxthdr, header); out[pos].iov_len = sizeof(struct ip6_hdr); - /* Calculate the pseudo-header checksum. - * Technically, the length that is used in the pseudo-header checksum is the transport layer - * length, which is not the same as len_left in the case of fragmented packets. But since - * translation does not change the transport layer length, the checksum is unaffected. - */ + // Calculate the pseudo-header checksum. old_sum = ipv4_pseudo_header_checksum(header, len_left); new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); - // If the IPv4 packet is fragmented, add a Fragment header. - frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base; - frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); - out[pos + 1].iov_len = frag_hdr_len; - - if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) { - // Non-first fragment. Copy the rest of the packet as is. - iov_len = generic_packet(out, pos + 2, next_header, len_left); - } else if (nxthdr == IPPROTO_ICMPV6) { - iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left); + if (nxthdr == IPPROTO_ICMPV6) { + iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 2, next_header, len_left); + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index 2e83be137f..d188e47550 100644 --- a/ipv6.c +++ b/ipv6.c @@ -77,7 +77,6 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i 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; - struct ip6_frag *frag_hdr = NULL; uint8_t protocol; const char *next_header; size_t len_left; @@ -113,6 +112,10 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { 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 @@ -121,48 +124,21 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip_header(ip_targ, 0, protocol, ip6); out[pos].iov_len = sizeof(struct iphdr); - // If there's a Fragment header, parse it and decide what the next header is. - // Do this before calculating the pseudo-header checksum because it updates the next header value. - if (protocol == IPPROTO_FRAGMENT) { - frag_hdr = (struct ip6_frag *) next_header; - if (len_left < sizeof(*frag_hdr)) { - logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); - return 0; - } - - next_header += sizeof(*frag_hdr); - len_left -= sizeof(*frag_hdr); - - protocol = parse_frag_header(frag_hdr, ip_targ); - } - - // ICMP and ICMPv6 have different protocol numbers. - if (protocol == IPPROTO_ICMPV6) { - protocol = IPPROTO_ICMP; - ip_targ->protocol = IPPROTO_ICMP; - } - - /* Calculate the pseudo-header checksum. - * Technically, the length that is used in the pseudo-header checksum is the transport layer - * length, which is not the same as len_left in the case of fragmented packets. But since - * translation does not change the transport layer length, the checksum is unaffected. - */ + // Calculate the pseudo-header checksum. old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); - // Does not support IPv6 extension headers except Fragment. - if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { - iov_len = generic_packet(out, pos + 2, next_header, len_left); - } else if (protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left); - } else if (protocol == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, + // does not support IPv6 extension headers, this will drop any packet with them + if (protocol == IPPROTO_ICMP) { + iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); + } else if (ip6->ip6_nxt == IPPROTO_TCP) { + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); - } else if (protocol == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, + } else if (ip6->ip6_nxt == IPPROTO_UDP) { + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, len_left); - } else if (protocol == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (ip6->ip6_nxt == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index 758509226d..f7f09cbb24 100644 --- a/translate.c +++ b/translate.c @@ -167,53 +167,6 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } -/* function: maybe_fill_frag_header - * fills a fragmentation header - * generate an ipv6 fragment header from an ipv4 header - * frag_hdr - target (ipv6) fragmentation header - * ip6_targ - target (ipv6) header - * old_header - (ipv4) source packet header - * returns: the length of the fragmentation header if present, or zero if not present - */ -size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, - const struct iphdr *old_header) { - uint16_t frag_flags = ntohs(old_header->frag_off); - uint16_t frag_off = frag_flags & IP_OFFMASK; - if (frag_off == 0 && (frag_flags & IP_MF) == 0) { - // Not a fragment. - return 0; - } - - frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; - frag_hdr->ip6f_reserved = 0; - // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits. - frag_hdr->ip6f_offlg = htons(frag_off << 3); - if (frag_flags & IP_MF) { - frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG; - } - frag_hdr->ip6f_ident = htonl(ntohs(old_header->id)); - ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; - - return sizeof(*frag_hdr); -} - -/* function: parse_frag_header - * return the length of the fragmentation header if present, or zero if not present - * generate an ipv6 fragment header from an ipv4 header - * frag_hdr - (ipv6) fragmentation header - * ip_targ - target (ipv4) header - * returns: the next header value - */ -uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) { - uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3); - if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) { - frag_off |= IP_MF; - } - ip_targ->frag_off = htons(frag_off); - ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); - ip_targ->protocol = frag_hdr->ip6f_nxt; - return frag_hdr->ip6f_nxt; -} /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp @@ -440,6 +393,9 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in + * + * TODO: mss rewrite + * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { @@ -479,20 +435,16 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, // Allocate buffers for all packet headers. struct tun_pi tun_targ; char iphdr[sizeof(struct ip6_hdr)]; - char fraghdr[sizeof(struct ip6_frag)]; char transporthdr[MAX_TCP_HDR]; char icmp_iphdr[sizeof(struct ip6_hdr)]; - char icmp_fraghdr[sizeof(struct ip6_frag)]; char icmp_transporthdr[MAX_TCP_HDR]; // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { { &tun_targ, sizeof(tun_targ) }, // Tunnel header. { iphdr, 0 }, // IP header. - { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_fraghdr, 0 }, // ICMP error fragmentation header. { icmp_transporthdr, 0 }, // ICMP error transport layer header. { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; diff --git a/translate.h b/translate.h index 5efa81725b..3378254af8 100644 --- a/translate.h +++ b/translate.h @@ -36,11 +36,9 @@ // 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_FRAGHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX -}; +enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX }; typedef struct iovec clat_packet[CLAT_POS_MAX]; // Calculates the checksum over all the packet components starting from pos. @@ -67,11 +65,6 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); -// Deal with fragmented packets. -size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, - const struct iphdr *old_header); -uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ); - // Translate ICMP packets. int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, const char *payload, size_t payload_size); From 56ec161d47856212008f47676577882f30853312 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 10 Mar 2014 16:33:22 +0900 Subject: [PATCH 057/261] Mark unused parameters and set -Wunused-parameter This allows us to compile with -Wall -Werror in build environments where -Wall also warns about unused parameters. Also explicitly set -Wunused-parameter so unused parameters will cause the build to fail in environments where -Wall does not warn about them. Change-Id: Icccf2121d2a9df77b1c224c4976cb9aec56496b3 --- Android.mk | 4 ++-- getaddr.c | 4 +++- getroute.c | 3 ++- logging.c | 6 ++++-- netlink_callbacks.c | 5 +++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Android.mk b/Android.mk index a83cb5ea69..35bc49853d 100644 --- a/Android.mk +++ b/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c -LOCAL_CFLAGS := -Wall -Werror +LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog @@ -29,7 +29,7 @@ include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := clatd_test -LOCAL_CFLAGS := -Wall -Werror +LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog diff --git a/getaddr.c b/getaddr.c index fb761f086a..5cae78b7ff 100644 --- a/getaddr.c +++ b/getaddr.c @@ -85,7 +85,9 @@ static int getaddr_cb(struct nl_msg *msg, void *data) { * 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) { +static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, + __attribute__((unused)) struct nlmsgerr *err, + __attribute__((unused)) void *arg) { return NL_OK; } diff --git a/getroute.c b/getroute.c index 5f9475e9ec..a615a4f60c 100644 --- a/getroute.c +++ b/getroute.c @@ -90,7 +90,8 @@ static int get_default_route_cb(struct nl_msg *msg, void *data) { * err - netlink message * arg - (int *) storage for the error number */ -static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { +static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, + struct nlmsgerr *err, void *arg) { int *retval = arg; if(err->error < 0) { // error_handler called even on no error (NLMSG_ERROR reply type used) *retval = err->error; diff --git a/logging.c b/logging.c index f2f3d501f8..c90b1cfc3f 100644 --- a/logging.c +++ b/logging.c @@ -42,12 +42,14 @@ void logmsg(int prio, const char *fmt, ...) { * fmt - printf format specifier * ... - printf format arguments */ -void logmsg_dbg(int prio, const char *fmt, ...) { #if CLAT_DEBUG +void logmsg_dbg(int prio, const char *fmt, ...) { va_list ap; va_start(ap, fmt); __android_log_vprint(prio, "clatd", fmt, ap); va_end(ap); -#endif } +#else +void logmsg_dbg(__attribute__((unused)) int prio, __attribute__((unused)) const char *fmt, ...) {} +#endif diff --git a/netlink_callbacks.c b/netlink_callbacks.c index 5e0f34edaf..a79aa7615a 100644 --- a/netlink_callbacks.c +++ b/netlink_callbacks.c @@ -27,7 +27,7 @@ * msg - netlink message * data - pointer to an int, stores the success code */ -static int ack_handler(struct nl_msg *msg, void *data) { +static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) { int *retval = data; *retval = 0; return NL_OK; @@ -39,7 +39,8 @@ static int ack_handler(struct nl_msg *msg, void *data) { * 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) { +static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, + struct nlmsgerr *err, void *arg) { int *retval = arg; if(err->error < 0) { *retval = err->error; From 82ab730688b8aa5290bdb3d4287e51d3c04f5c95 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 13:33:30 +0900 Subject: [PATCH 058/261] DO NOT MERGE: Remove two almost-unused include files. This fixes a compiler warning. Change-Id: I7f4a866fd5a65a64861fe22e9d32a1a8b8e9758d --- clatd.c | 2 -- ipv4.c | 1 - ipv4.h | 25 ------------------------- ipv6.c | 1 - ipv6.h | 25 ------------------------- translate.h | 4 ++++ 6 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 ipv4.h delete mode 100644 ipv6.h diff --git a/clatd.c b/clatd.c index 26ecb57bd8..1ca46a3252 100644 --- a/clatd.c +++ b/clatd.c @@ -47,8 +47,6 @@ #include -#include "ipv4.h" -#include "ipv6.h" #include "translate.h" #include "clatd.h" #include "config.h" diff --git a/ipv4.c b/ipv4.c index 89e47e475f..47f242279b 100644 --- a/ipv4.c +++ b/ipv4.c @@ -28,7 +28,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv4.h" #include "logging.h" #include "debug.h" #include "dump.h" diff --git a/ipv4.h b/ipv4.h deleted file mode 100644 index 00befd251f..0000000000 --- a/ipv4.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function - */ -#ifndef __IPV4_H__ -#define __IPV4_H__ - -#include "translate.h" - -int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV4_H__ */ diff --git a/ipv6.c b/ipv6.c index ef1e62f165..faf27addbb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -29,7 +29,6 @@ #include "translate.h" #include "checksum.h" -#include "ipv6.h" #include "logging.h" #include "dump.h" #include "config.h" diff --git a/ipv6.h b/ipv6.h deleted file mode 100644 index 019de17c91..0000000000 --- a/ipv6.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function - */ -#ifndef __IPV6_H__ -#define __IPV6_H__ - -#include "translate.h" - -int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); - -#endif /* __IPV6_H__ */ diff --git a/translate.h b/translate.h index fded251433..356050f77a 100644 --- a/translate.h +++ b/translate.h @@ -47,6 +47,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate IPv4 and IPv6 packets. +int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); +int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); + // 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); From 72f3126f6499cd9017b367f5c7e61cab1d99d080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Thu, 12 Dec 2013 10:28:16 +0100 Subject: [PATCH 059/261] DO NOT MERGE: Remove unused variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused variables -- they cause warnings (and errors with -Werror) when building with clang 3.4. Change-Id: I6ef10841bc1384bf256d73874ae1e709caa6b132 Signed-off-by: Bernhard Rosenkränzer --- clatd.c | 2 -- config.c | 2 +- icmp.c | 2 +- ipv6.c | 1 - translate.c | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index 1ca46a3252..43fb5c6131 100644 --- a/clatd.c +++ b/clatd.c @@ -144,7 +144,6 @@ void deconfigure_tun_ipv6(const struct tun_data *tunnel) { * 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, @@ -377,7 +376,6 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { 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) { diff --git a/config.c b/config.c index 61a4ebb441..b82ac6c9d0 100644 --- a/config.c +++ b/config.c @@ -154,7 +154,7 @@ void free_config() { * 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; + int backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; diff --git a/icmp.c b/icmp.c index af96b83582..75a4a4d1c4 100644 --- a/icmp.c +++ b/icmp.c @@ -139,7 +139,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) { } // We don't understand this ICMP type. Return parameter problem so the caller will bail out. - logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type); + logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code); return ICMP_PARAMETERPROB; } diff --git a/ipv6.c b/ipv6.c index faf27addbb..d9dcc091ab 100644 --- a/ipv6.c +++ b/ipv6.c @@ -89,7 +89,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { size_t len_left; uint32_t checksum; int iov_len; - int i; if(len < sizeof(struct ip6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); diff --git a/translate.c b/translate.c index fc70f3da0b..71263faf2b 100644 --- a/translate.c +++ b/translate.c @@ -245,7 +245,6 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint3 const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; - int ttl; int clat_packet_len; memset(icmp_targ, 0, sizeof(struct icmphdr)); From c2549b4b4e2437aca29497c29ebe0b17b6214549 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 15:56:02 +0900 Subject: [PATCH 060/261] DO NOT MERGE: Fix compiler warnings and enable -Wall -Werror Change-Id: I4eb9eb407500893a27745ed2df6da9a378ddb159 --- Android.mk | 1 + clatd.c | 2 +- ipv6.c | 14 +++++++------- translate.c | 5 ++--- translate.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Android.mk b/Android.mk index b0d19d9626..ec1eeb78f5 100644 --- a/Android.mk +++ b/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c +LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter LOCAL_C_INCLUDES := external/libnl-headers LOCAL_STATIC_LIBRARIES := libnl_2 LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index 43fb5c6131..a0ee07def2 100644 --- a/clatd.c +++ b/clatd.c @@ -82,7 +82,7 @@ void set_forwarding(int fd, const char *setting) { /* function: stop_loop * signal handler: stop the event loop */ -void stop_loop(int signal) { +void stop_loop() { running = 0; } diff --git a/ipv6.c b/ipv6.c index d9dcc091ab..4dff3bc55f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -42,8 +42,7 @@ * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, - size_t len) { +int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) { const char *payload; size_t payload_size; @@ -55,7 +54,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32 payload = (const char *) (icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); - return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size); + return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); } /* function: log_bad_address @@ -63,16 +62,18 @@ 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 *src, const struct in6_addr *dst) { #if CLAT_DEBUG +void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr)); inet_ntop(AF_INET6, dst, dststr, sizeof(dststr)); logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr); -#endif } +#else +#define log_bad_address(fmt, src, dst) +#endif /* function: ipv6_packet * takes an ipv6 packet and hands it off to the layer 4 protocol function @@ -136,8 +137,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { // does not support IPv6 extension headers, this will drop any packet with them if(protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, checksum, - len_left); + iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if(ip6->ip6_nxt == IPPROTO_TCP) { iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); diff --git a/translate.c b/translate.c index 71263faf2b..599182d7ac 100644 --- a/translate.c +++ b/translate.c @@ -149,7 +149,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, // Third-party ICMPv6 message. This may have been originated by an native IPv6 address. // In that case, the source IPv6 address can't be translated and we need to make up an IPv4 // source address. For now, use 255.0.0., which at least looks useful in traceroute. - if (ip->saddr == (uint32_t) INADDR_NONE) { + if ((uint32_t) ip->saddr == INADDR_NONE) { ttl_guess = icmp_guess_ttl(old_header->ip6_hlim); ip->saddr = htonl((0xff << 24) + ttl_guess); } @@ -236,12 +236,11 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t * translate ipv6 icmp to ipv4 icmp * out - output packet * icmp6 - source packet icmp6 header - * checksum - pseudo-header checksum (unused) * payload - icmp6 payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { struct icmphdr *icmp_targ = out[pos].iov_base; uint8_t icmp_type; diff --git a/translate.h b/translate.h index 356050f77a..c4d8ede85e 100644 --- a/translate.h +++ b/translate.h @@ -54,7 +54,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); // 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, +int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size); // Translate TCP and UDP packets. From 57bcefd5920c330f03aa76329b9f97f3f1e841ac Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Nov 2013 12:59:44 +0900 Subject: [PATCH 061/261] DO NOT MERGE: Add generic IP packet code and use it for GRE. Bug: 11542311 Change-Id: I91040f35814bd7b23288228a7fdf80f1be2f5a04 --- ipv4.c | 8 +++++--- ipv6.c | 8 +++++--- translate.c | 16 ++++++++++++++++ translate.h | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ipv4.c b/ipv4.c index 47f242279b..b5cbf80661 100644 --- a/ipv4.c +++ b/ipv4.c @@ -123,12 +123,14 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { // Calculate the pseudo-header checksum. checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); - if(nxthdr == IPPROTO_ICMPV6) { + if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); - } else if(nxthdr == IPPROTO_TCP) { + } 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) { + } else if (nxthdr == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (nxthdr == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index 4dff3bc55f..79303ec7c9 100644 --- a/ipv6.c +++ b/ipv6.c @@ -136,14 +136,16 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them - if(protocol == IPPROTO_ICMP) { + if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if(ip6->ip6_nxt == IPPROTO_TCP) { + } 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) { + } else if (ip6->ip6_nxt == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + } else if (ip6->ip6_nxt == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index 599182d7ac..00ea0b99d8 100644 --- a/translate.c +++ b/translate.c @@ -278,6 +278,22 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, return clat_packet_len; } +/* function: generic_packet + * takes a generic IP packet and sets it up for translation + * out - output packet + * pos - position in the output packet of the transport header + * payload - pointer to IP payload + * len - size of ip payload + * returns: the highest position in the output clat_packet that's filled in + */ +int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { + out[pos].iov_len = 0; + out[CLAT_POS_PAYLOAD].iov_base = (char *) payload; + out[CLAT_POS_PAYLOAD].iov_len = len; + + return CLAT_POS_PAYLOAD + 1; +} + /* function: udp_packet * takes a udp packet and sets it up for translation * out - output packet diff --git a/translate.h b/translate.h index c4d8ede85e..9f1ac15a3f 100644 --- a/translate.h +++ b/translate.h @@ -57,6 +57,9 @@ 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, const char *payload, size_t payload_size); +// Translate generic IP packets. +int generic_packet(clat_packet out, int pos, const char *payload, size_t len); + // 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); From c1be4799c39775fecb004d92687fe95298cac380 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 10 Feb 2014 09:20:05 +0900 Subject: [PATCH 062/261] DO NOT MERGE: Fix up checksums instead of recalculating them. Currently the checksums of translated packets are calculated from scratch by checksumming the translated packet. This is slow and does not work in the case of fragments, because the whole packet is not available. Instead, calculate the checksum by adjusting the checksum of the original packet. Bug: 11542311 Bug: 12116252 Change-Id: I457347753d1bb5b23e3ae759bf2e4388102772d7 --- checksum.c | 38 +++++++++++++++++++++++++++++++++----- checksum.h | 2 ++ ipv4.c | 13 ++++++++----- ipv6.c | 9 +++++---- translate.c | 49 ++++++++++++++++++++++++++++++++----------------- translate.h | 12 +++++++----- 6 files changed, 87 insertions(+), 36 deletions(-) diff --git a/checksum.c b/checksum.c index a4dc9b859c..099be6a0da 100644 --- a/checksum.c +++ b/checksum.c @@ -49,19 +49,27 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { return checksum; } -/* function: ip_checksum_finish - * close the checksum +/* function: ip_checksum_fold + * folds a 32-bit partial checksum into 16 bits * temp_sum - sum from ip_checksum_add + * returns: the folded checksum in network byte order */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { +uint16_t ip_checksum_fold(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_finish + * folds and closes the checksum + * temp_sum - sum from ip_checksum_add + * returns: a header checksum value in network byte order + */ +uint16_t ip_checksum_finish(uint32_t temp_sum) { + return ~ip_checksum_fold(temp_sum); +} + /* function: ip_checksum * combined ip_checksum_add and ip_checksum_finish * data - data to checksum @@ -113,3 +121,23 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u return current; } + +/* function: ip_checksum_adjust + * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums + * checksum - the header checksum in the original packet in network byte order + * old_hdr_sum - the pseudo-header checksum of the original packet + * new_hdr_sum - the pseudo-header checksum of the translated packet + * returns: the new header checksum in network byte order + */ +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { + // Algorithm suggested in RFC 1624. + // http://tools.ietf.org/html/rfc1624#section-3 + checksum = ~checksum; + uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); + uint16_t folded_old = ip_checksum_fold(old_hdr_sum); + if (folded_sum > folded_old) { + return ~(folded_sum - folded_old); + } else { + return ~(folded_sum - folded_old - 1); // end-around borrow + } +} diff --git a/checksum.h b/checksum.h index 473f5f52ed..44921f0d07 100644 --- a/checksum.h +++ b/checksum.h @@ -25,4 +25,6 @@ uint16_t ip_checksum(const void *data, int len); uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); + #endif /* __CHECKSUM_H__ */ diff --git a/ipv4.c b/ipv4.c index b5cbf80661..1d5b0b255c 100644 --- a/ipv4.c +++ b/ipv4.c @@ -70,7 +70,7 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t nxthdr; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct iphdr)) { @@ -121,14 +121,17 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(0, header, len_left); + new_sum = 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); + iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + len_left); } else if (nxthdr == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); } else { diff --git a/ipv6.c b/ipv6.c index 79303ec7c9..e4a73fe7cb 100644 --- a/ipv6.c +++ b/ipv6.c @@ -88,7 +88,7 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { uint8_t protocol; const char *next_header; size_t len_left; - uint32_t checksum; + uint32_t old_sum, new_sum; int iov_len; if(len < sizeof(struct ip6_hdr)) { @@ -133,16 +133,17 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); + new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, + iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, + iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (ip6->ip6_nxt == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 1, next_header, len_left); diff --git a/translate.c b/translate.c index 00ea0b99d8..9a0f1b5a0d 100644 --- a/translate.c +++ b/translate.c @@ -208,12 +208,10 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t // 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). + // an IPv6 header, which is 20 bytes longer. Fix it up here. // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum). - checksum = htonl(ntohl(checksum) + 20); + checksum = checksum + htons(20); } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) { // Ping packet. icmp6_targ->icmp6_id = icmp->un.echo.id; @@ -298,10 +296,12 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) { * takes a udp packet and sets it up for translation * out - output packet * udp - pointer to udp header in packet - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * len - size of ip payload */ -int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) { +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size; @@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec payload = (const char *) (udp + 1); payload_size = len - sizeof(struct udphdr); - return udp_translate(out, pos, udp, checksum, payload, payload_size); + return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); } /* function: tcp_packet @@ -324,7 +324,8 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) { +int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len) { const char *payload; size_t payload_size, header_size; @@ -347,20 +348,21 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t chec payload = ((const char *) tcp) + header_size; payload_size = len - header_size; - return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size); + return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); } /* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet * out - output packet * udp - udp header - * checksum - pseudo-header checksum + * old_sum - pseudo-header checksum of old header + * new_sum - pseudo-header checksum of new header * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in */ -int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, - const char *payload, size_t payload_size) { +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum, + uint32_t new_sum, const char *payload, size_t payload_size) { struct udphdr *udp_targ = out[pos].iov_base; memcpy(udp_targ, udp, sizeof(struct udphdr)); @@ -369,8 +371,22 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c 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); + if (udp_targ->check) { + udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum); + } else { + // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that + // the transmitter generated no checksum (for debugging or for higher level protocols that + // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So + // for safety we recompute it. + udp_targ->check = 0; // Checksum field must be 0 when calculating checksum. + udp_targ->check = packet_checksum(new_sum, out, pos); + } + + // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent + // in one's complement arithmetic)." + if (!udp_targ->check) { + udp_targ->check = 0xffff; + } return CLAT_POS_PAYLOAD + 1; } @@ -389,7 +405,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, - uint32_t checksum, const char *payload, size_t payload_size) { + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; out[pos].iov_len = header_size; @@ -406,8 +422,7 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea out[CLAT_POS_PAYLOAD].iov_base = (char *)payload; out[CLAT_POS_PAYLOAD].iov_len = payload_size; - tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum. - tcp_targ->check = packet_checksum(checksum, out, pos); + tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); return CLAT_POS_PAYLOAD + 1; } diff --git a/translate.h b/translate.h index 9f1ac15a3f..cfb7bbbf3a 100644 --- a/translate.h +++ b/translate.h @@ -61,12 +61,14 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, int generic_packet(clat_packet out, int pos, const char *payload, size_t len); // 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_packet(clat_packet out, int pos, const struct tcphdr *tcp, + uint32_t old_sum, uint32_t new_sum, size_t len); +int udp_packet(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, 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); + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); +int udp_translate(clat_packet out, int pos, const struct udphdr *udp, + uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ From f0029d7d06cc8401686ddf379a609b29f511f1a5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 13 Feb 2014 12:53:35 +0900 Subject: [PATCH 063/261] DO NOT MERGE: Move translation entry point into translate.c. The entry point to the translation code is currently called packet_handler and lives in clatd.c. Move it into translate.c and rename it to translate_packet, since that's what it does. Also get rid of some redundant includes. Bug: 11542311 Change-Id: I821bb9590b9105ea1d425c3bbe0e634c4ce667b5 --- clatd.c | 66 +---------------------------------------------------- clatd.h | 6 +++++ ipv4.c | 9 -------- ipv6.c | 8 ------- translate.c | 61 +++++++++++++++++++++++++++++++++++++++++-------- translate.h | 14 ++++++++++++ 6 files changed, 73 insertions(+), 91 deletions(-) diff --git a/clatd.c b/clatd.c index a0ee07def2..3ab23711ef 100644 --- a/clatd.c +++ b/clatd.c @@ -29,15 +29,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -63,11 +54,6 @@ 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 */ @@ -305,56 +291,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, configure_tun_ip(tunnel); } -/* function: packet_handler - * takes a tun header and a packet and sends it down the stack - * tunnel - tun device data - * tun_header - tun header - * packet - packet - * packetsize - size of packet - */ -void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, - size_t packetsize) { - int fd; - int iov_len = 0; - - // Allocate buffers for all packet headers. - struct tun_pi tun_targ; - char iphdr[sizeof(struct ip6_hdr)]; - char transporthdr[MAX_TCP_HDR]; - char icmp_iphdr[sizeof(struct ip6_hdr)]; - char icmp_transporthdr[MAX_TCP_HDR]; - - // iovec of the packets we'll send. This gets passed down to the translation functions. - clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. - { iphdr, 0 }, // IP header. - { transporthdr, 0 }, // Transport layer header. - { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_transporthdr, 0 }, // ICMP error transport layer header. - { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. - }; - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); - iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else { - logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto); - } - - if (iov_len > 0) { - writev(fd, out, iov_len); - } -} - /* function: read_packet * reads a packet from the tunnel fd and passes it down the stack * active_fd - tun file descriptor marked ready for reading @@ -383,7 +319,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); } } diff --git a/clatd.h b/clatd.h index 3459b09edb..44a655e0f8 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include #define MAXMTU 1500 @@ -30,4 +31,9 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 +struct tun_data { + char device6[IFNAMSIZ], device4[IFNAMSIZ]; + int fd6, fd4; +}; + #endif /* __CLATD_H__ */ diff --git a/ipv4.c b/ipv4.c index 1d5b0b255c..c828ffa1e0 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,15 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include - #include "translate.h" #include "checksum.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index e4a73fe7cb..371d9e180e 100644 --- a/ipv6.c +++ b/ipv6.c @@ -17,14 +17,6 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include "translate.h" diff --git a/translate.c b/translate.c index 9a0f1b5a0d..f7f09cbb24 100644 --- a/translate.c +++ b/translate.c @@ -16,15 +16,7 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include - -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "icmp.h" #include "translate.h" @@ -175,6 +167,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } + /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp * out - output packet @@ -426,3 +419,53 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea return CLAT_POS_PAYLOAD + 1; } + +/* function: translate_packet + * 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 translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize) { + int fd; + int iov_len = 0; + + // Allocate buffers for all packet headers. + struct tun_pi tun_targ; + char iphdr[sizeof(struct ip6_hdr)]; + char transporthdr[MAX_TCP_HDR]; + char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_transporthdr[MAX_TCP_HDR]; + + // iovec of the packets we'll send. This gets passed down to the translation functions. + clat_packet out = { + { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { iphdr, 0 }, // IP header. + { transporthdr, 0 }, // Transport layer header. + { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_transporthdr, 0 }, // ICMP error transport layer header. + { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. + }; + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); + } + + if(ntohs(tun_header->proto) == ETH_P_IP) { + fd = tunnel->fd6; + fill_tun_header(&tun_targ, ETH_P_IPV6); + iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { + fd = tunnel->fd4; + fill_tun_header(&tun_targ, ETH_P_IP); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); + } else { + logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); + } + + if (iov_len > 0) { + writev(fd, out, iov_len); + } +} diff --git a/translate.h b/translate.h index cfb7bbbf3a..3378254af8 100644 --- a/translate.h +++ b/translate.h @@ -18,8 +18,18 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ +#include +#include +#include +#include +#include +#include +#include +#include #include +#include "clatd.h" + #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. // A clat_packet is an array of iovec structures representing a packet that we are translating. @@ -47,6 +57,10 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header); +// Translate and send packets. +void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, + size_t packetsize); + // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); From 13a58c4859164d9f16e124b0f85c35a061dec76d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 20 Feb 2014 14:28:43 +0900 Subject: [PATCH 064/261] DO NOT MERGE: Modify the pseudo-header checksum functions. - Remove the initial checksum, which we don't use anywhere. - Add the upper-layer protocol to the IPv6 function. Currently this is always the same as the next header field in the IPv6 header, but technically it's the upper-layer header after all the extension headers. This is required to support fragments. Bug: 11542311 Change-Id: Ifa8af1bfda182711a0bba5c6452f44045fbd1a7f --- checksum.c | 16 ++++++++-------- checksum.h | 4 ++-- dump.c | 8 ++++---- ipv4.c | 4 ++-- ipv6.c | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/checksum.c b/checksum.c index 099be6a0da..3dd1e00bef 100644 --- a/checksum.c +++ b/checksum.c @@ -84,16 +84,16 @@ 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 - the current checksum or 0 to start a new checksum - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) + * ip6 - the ipv6 header + * len - the transport length (transport header + payload) + * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments */ -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) { +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t) len); - checksum_next = htonl(ip6->ip6_nxt); + checksum_next = htonl(protocol); + uint32_t current = 0; current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); @@ -104,16 +104,16 @@ uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6 /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers - * current - the current checksum or 0 to start a new checksum * ip - the ipv4 header * len - the transport length (transport header + payload) */ -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) { +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { uint16_t temp_protocol, temp_length; temp_protocol = htons(ip->protocol); temp_length = htons(len); + uint32_t current = 0; current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); diff --git a/checksum.h b/checksum.h index 44921f0d07..619581011d 100644 --- a/checksum.h +++ b/checksum.h @@ -22,8 +22,8 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len); uint16_t ip_checksum_finish(uint32_t temp_sum); uint16_t ip_checksum(const void *data, int len); -uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len); -uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len); +uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol); +uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len); uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); diff --git a/dump.c b/dump.c index 856765551e..94e4796d49 100644 --- a/dump.c +++ b/dump.c @@ -147,14 +147,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, sizeof(*udp) + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print ipv6/udp header */ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size) { uint32_t temp_checksum; - temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); dump_udp_generic(udp, temp_checksum, payload, payload_size); } @@ -203,7 +203,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } @@ -211,7 +211,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, sizeof(*tcp) + options_size + payload_size); + temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size); } diff --git a/ipv4.c b/ipv4.c index c828ffa1e0..e2636b4b3a 100644 --- a/ipv4.c +++ b/ipv4.c @@ -112,8 +112,8 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct ip6_hdr); // Calculate the pseudo-header checksum. - old_sum = ipv4_pseudo_header_checksum(0, header, len_left); - new_sum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); + old_sum = ipv4_pseudo_header_checksum(header, len_left); + new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); if (nxthdr == IPPROTO_ICMPV6) { iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); diff --git a/ipv6.c b/ipv6.c index 371d9e180e..d188e47550 100644 --- a/ipv6.c +++ b/ipv6.c @@ -125,8 +125,8 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { out[pos].iov_len = sizeof(struct iphdr); // Calculate the pseudo-header checksum. - old_sum = ipv6_pseudo_header_checksum(0, ip6, len_left); - new_sum = ipv4_pseudo_header_checksum(0, ip_targ, len_left); + old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); + new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); // does not support IPv6 extension headers, this will drop any packet with them if (protocol == IPPROTO_ICMP) { From 2b4cc7393268622ac3de071435a6e4ab857342df Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sun, 9 Feb 2014 10:35:38 +0900 Subject: [PATCH 065/261] DO NOT MERGE: Support translating fragmented packets. Bug: 11542311 Change-Id: I5dd29805e12b919ae3105b6128aaedefd7e78b48 --- clatd.c | 8 ++++++-- dump.c | 1 + ipv4.c | 33 +++++++++++++++++++------------- ipv6.c | 52 +++++++++++++++++++++++++++++++++++++-------------- translate.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++--- translate.h | 13 ++++++++++--- 6 files changed, 126 insertions(+), 35 deletions(-) diff --git a/clatd.c b/clatd.c index 3ab23711ef..a89c588780 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,9 @@ #define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" +/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ +#define MTU_DELTA 28 + int forwarding_fd = -1; volatile sig_atomic_t running = 1; @@ -271,8 +274,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, 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; + if(Global_Clatd_Config.ipv4mtu <= 0 || + Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { + Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } diff --git a/dump.c b/dump.c index 94e4796d49..ba5fa3e35a 100644 --- a/dump.c +++ b/dump.c @@ -220,6 +220,7 @@ void logcat_hexdump(const char *info, const char *data, size_t len) { char output[PACKETLEN*3+2]; size_t i; + output[0] = '\0'; for(i = 0; i < len && i < PACKETLEN; i++) { snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]); } diff --git a/ipv4.c b/ipv4.c index e2636b4b3a..84dccd5bc7 100644 --- a/ipv4.c +++ b/ipv4.c @@ -57,7 +57,8 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c 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; + struct ip6_frag *frag_hdr; + size_t frag_hdr_len; uint8_t nxthdr; const char *next_header; size_t len_left; @@ -69,12 +70,6 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { return 0; } - frag_flags = ntohs(header->frag_off); - if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't - logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping"); - return 0; - } - if(header->ihl < 5) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl); return 0; @@ -111,20 +106,32 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip6_header(ip6_targ, 0, nxthdr, header); out[pos].iov_len = sizeof(struct ip6_hdr); - // Calculate the pseudo-header checksum. + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv4_pseudo_header_checksum(header, len_left); new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); - if (nxthdr == IPPROTO_ICMPV6) { - iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, new_sum, len_left); + // If the IPv4 packet is fragmented, add a Fragment header. + frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base; + frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); + out[pos + 1].iov_len = frag_hdr_len; + + if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) { + // Non-first fragment. Copy the rest of the packet as is. + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (nxthdr == IPPROTO_ICMPV6) { + iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); diff --git a/ipv6.c b/ipv6.c index d188e47550..2e83be137f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -77,6 +77,7 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i 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; + struct ip6_frag *frag_hdr = NULL; uint8_t protocol; const char *next_header; size_t len_left; @@ -112,10 +113,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { 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 @@ -124,21 +121,48 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) { fill_ip_header(ip_targ, 0, protocol, ip6); out[pos].iov_len = sizeof(struct iphdr); - // Calculate the pseudo-header checksum. + // If there's a Fragment header, parse it and decide what the next header is. + // Do this before calculating the pseudo-header checksum because it updates the next header value. + if (protocol == IPPROTO_FRAGMENT) { + frag_hdr = (struct ip6_frag *) next_header; + if (len_left < sizeof(*frag_hdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); + return 0; + } + + next_header += sizeof(*frag_hdr); + len_left -= sizeof(*frag_hdr); + + protocol = parse_frag_header(frag_hdr, ip_targ); + } + + // ICMP and ICMPv6 have different protocol numbers. + if (protocol == IPPROTO_ICMPV6) { + protocol = IPPROTO_ICMP; + ip_targ->protocol = IPPROTO_ICMP; + } + + /* Calculate the pseudo-header checksum. + * Technically, the length that is used in the pseudo-header checksum is the transport layer + * length, which is not the same as len_left in the case of fragmented packets. But since + * translation does not change the transport layer length, the checksum is unaffected. + */ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); - // does not support IPv6 extension headers, this will drop any packet with them - if (protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, len_left); - } else if (ip6->ip6_nxt == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, old_sum, new_sum, + // Does not support IPv6 extension headers except Fragment. + if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); + } else if (protocol == IPPROTO_ICMP) { + iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left); + } else if (protocol == IPPROTO_TCP) { + iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, old_sum, new_sum, + } else if (protocol == IPPROTO_UDP) { + iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, len_left); - } else if (ip6->ip6_nxt == IPPROTO_GRE) { - iov_len = generic_packet(out, pos + 1, next_header, len_left); + } else if (protocol == IPPROTO_GRE) { + iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); diff --git a/translate.c b/translate.c index f7f09cbb24..758509226d 100644 --- a/translate.c +++ b/translate.c @@ -167,6 +167,53 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr); } +/* function: maybe_fill_frag_header + * fills a fragmentation header + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - target (ipv6) fragmentation header + * ip6_targ - target (ipv6) header + * old_header - (ipv4) source packet header + * returns: the length of the fragmentation header if present, or zero if not present + */ +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header) { + uint16_t frag_flags = ntohs(old_header->frag_off); + uint16_t frag_off = frag_flags & IP_OFFMASK; + if (frag_off == 0 && (frag_flags & IP_MF) == 0) { + // Not a fragment. + return 0; + } + + frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; + frag_hdr->ip6f_reserved = 0; + // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits. + frag_hdr->ip6f_offlg = htons(frag_off << 3); + if (frag_flags & IP_MF) { + frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG; + } + frag_hdr->ip6f_ident = htonl(ntohs(old_header->id)); + ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; + + return sizeof(*frag_hdr); +} + +/* function: parse_frag_header + * return the length of the fragmentation header if present, or zero if not present + * generate an ipv6 fragment header from an ipv4 header + * frag_hdr - (ipv6) fragmentation header + * ip_targ - target (ipv4) header + * returns: the next header value + */ +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) { + uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3); + if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) { + frag_off |= IP_MF; + } + ip_targ->frag_off = htons(frag_off); + ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); + ip_targ->protocol = frag_hdr->ip6f_nxt; + return frag_hdr->ip6f_nxt; +} /* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp @@ -393,9 +440,6 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o * payload - tcp payload * payload_size - size of payload * returns: the highest position in the output clat_packet that's filled in - * - * TODO: mss rewrite - * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size, uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) { @@ -435,16 +479,20 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, // Allocate buffers for all packet headers. struct tun_pi tun_targ; char iphdr[sizeof(struct ip6_hdr)]; + char fraghdr[sizeof(struct ip6_frag)]; char transporthdr[MAX_TCP_HDR]; char icmp_iphdr[sizeof(struct ip6_hdr)]; + char icmp_fraghdr[sizeof(struct ip6_frag)]; char icmp_transporthdr[MAX_TCP_HDR]; // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { { &tun_targ, sizeof(tun_targ) }, // Tunnel header. { iphdr, 0 }, // IP header. + { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_fraghdr, 0 }, // ICMP error fragmentation header. { icmp_transporthdr, 0 }, // ICMP error transport layer header. { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; diff --git a/translate.h b/translate.h index 3378254af8..5efa81725b 100644 --- a/translate.h +++ b/translate.h @@ -36,9 +36,11 @@ // The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain // specific parts of the packet. The packet_* functions operate on all the packet segments past a // given position. -enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX }; +enum clat_packet_index { + CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX +}; typedef struct iovec clat_packet[CLAT_POS_MAX]; // Calculates the checksum over all the packet components starting from pos. @@ -65,6 +67,11 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len); int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len); +// Deal with fragmented packets. +size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, + const struct iphdr *old_header); +uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ); + // Translate ICMP packets. int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, const char *payload, size_t payload_size); From 4d4784214bf7f03fc53c167777be87e512c6ed54 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Feb 2014 16:33:24 +0900 Subject: [PATCH 066/261] Put the native test in the proper location. Change-Id: Ieaf90050ccc80a8d04976dd96ac66d8853a48f0a --- Android.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/Android.mk b/Android.mk index 35bc49853d..72fc0f9565 100644 --- a/Android.mk +++ b/Android.mk @@ -33,6 +33,5 @@ LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog -LOCAL_MODULE_PATH := $(TARGET_OUT)/bin include $(BUILD_NATIVE_TEST) From cd254c95a7cb5961bd53d16737f06bf261426e38 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 17 Mar 2014 11:31:17 +0900 Subject: [PATCH 067/261] Update BUGS and bump version to 1.2 Change-Id: Ib8dea3efa57ac2f2e509527fe3da7eea31432d43 --- BUGS | 8 ++------ clatd.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/BUGS b/BUGS index 5494d6bf21..70aeb9f0f4 100644 --- a/BUGS +++ b/BUGS @@ -1,9 +1,5 @@ -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 + - does not handle protocols other than ICMP, UDP, TCP and GRE - 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 /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/clatd.h b/clatd.h index 44a655e0f8..29c9acebb0 100644 --- a/clatd.h +++ b/clatd.h @@ -23,7 +23,7 @@ #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) -#define CLATD_VERSION "1.1" +#define CLATD_VERSION "1.2" // how frequently (in seconds) to poll for an address change while traffic is passing #define INTERFACE_POLL_FREQUENCY 30 From 932614ee5e740704d7ef951dff6daede4fe12904 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Wed, 2 Apr 2014 17:00:26 -0700 Subject: [PATCH 068/261] Minor code clean-up for GCC 4.9. - Tidy up "signed vs unsigned" comparison - Unconditionally initialize the "protocol" variable. This is a false-positive warning as protocol is indeed initialized when used, but GCC failed to detect the dependency between protocol and ip6. Change-Id: I8146898d91d45531c3de73403e0df3330c0a2043 --- clatd_test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index ba7889891c..5172d3fb4d 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -286,8 +286,8 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu uint8_t *reassembled, size_t *reassembled_len, const char *msg) { struct iphdr *ip = NULL; struct ip6_hdr *ip6 = NULL; - int total_length, pos = 0; - uint8_t protocol; + size_t total_length, pos = 0; + uint8_t protocol = 0; uint8_t version = ip_version(fragments[0]); for (int i = 0; i < numpackets; i++) { @@ -345,7 +345,7 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu // Copy the payload. int payload_length = len - payload_offset; total_length += payload_length; - ASSERT_LT(total_length, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + ASSERT_LT(total_length, *reassembled_len) << msg << ": Reassembly buffer too small\n"; memcpy(reassembled + pos, packet + payload_offset, payload_length); pos += payload_length; } @@ -596,8 +596,8 @@ TEST_F(ClatdTest, TransportChecksum) { uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN, IPPROTO_UDP); - EXPECT_EQ(0x3ad0, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; - EXPECT_EQ(0x2644b, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; + EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; + EXPECT_EQ(0x2644bU, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; EXPECT_EQ( kUdpV4Checksum, ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) From 079fd2305ef90d95a1bfdb200e2975e9db86bf43 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Mon, 28 Apr 2014 10:25:47 -0400 Subject: [PATCH 069/261] Place clatd ip routing rule into local table rather than main table as the main table is not used for routing when per-network tables are being used and clatd's rule is for locally destined packets. Change-Id: I243397b57c946f4af4c2439242b90208b01e84f0 --- setroute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setroute.c b/setroute.c index cffee9fb99..f9dc365687 100644 --- a/setroute.c +++ b/setroute.c @@ -61,7 +61,7 @@ int if_route(const char *ifname, int family, const void *destination, int prefix memset(&rt, 0, sizeof(rt)); rt.rtm_family = family; - rt.rtm_table = RT_TABLE_MAIN; + rt.rtm_table = RT_TABLE_LOCAL; rt.rtm_dst_len = prefixlen; switch(change_type) { case ROUTE_DELETE: From a1c871c8efad6c0b69e27d3b85e82a27e282b8be Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 30 May 2014 13:19:10 -0400 Subject: [PATCH 070/261] Make DNS request for plat prefix detection network specific. When plat prefix detection is done on a non-default network it must use the network specific version of getaddrinfo() so the plat prefix corresponds to the particular network. The network is specified to clatd via a NetID command line argument. Change-Id: I77a47b24e68c7786f790974f05787a40a4934af5 --- Android.mk | 2 +- clatd.c | 25 ++++++++++++++++++++----- config.c | 11 +++++++---- config.h | 3 ++- dns64.c | 10 ++++++---- dns64.h | 2 +- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Android.mk b/Android.mk index 72fc0f9565..f94acdf205 100644 --- a/Android.mk +++ b/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_C_INCLUDES := external/libnl/include +LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index 63c4d6d6b9..7b8e43fec8 100644 --- a/clatd.c +++ b/clatd.c @@ -42,6 +42,7 @@ #include "clatd.h" #include "config.h" #include "logging.h" +#include "resolv_netid.h" #include "setif.h" #include "setroute.h" #include "mtu.h" @@ -252,11 +253,12 @@ void drop_root() { * uplink_interface - network interface to use to reach the ipv6 internet * plat_prefix - PLAT prefix to use * tunnel - tun device data + * net_id - NetID to use, NETID_UNSET indicates use of default network */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel) { +void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel, unsigned net_id) { int error; - if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix)) { + if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { logmsg(ANDROID_LOG_FATAL,"read_config failed"); exit(1); } @@ -374,6 +376,7 @@ void print_help() { printf("android-clat arguments:\n"); printf("-i [uplink interface]\n"); printf("-p [plat prefix]\n"); + printf("-n [NetId]\n"); } /* function: main @@ -382,12 +385,13 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; + unsigned net_id = NETID_UNSET; strcpy(tunnel.device6, DEVICENAME6); strcpy(tunnel.device4, DEVICENAME4); - while((opt = getopt(argc, argv, "i:p:h")) != -1) { + while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { switch(opt) { case 'i': uplink_interface = optarg; @@ -395,6 +399,9 @@ int main(int argc, char **argv) { case 'p': plat_prefix = optarg; break; + case 'n': + net_id_str = optarg; + break; case 'h': default: print_help(); @@ -408,6 +415,14 @@ int main(int argc, char **argv) { printf("I need an interface\n"); exit(1); } + if (net_id_str != NULL) { + char *end_ptr; + net_id = strtoul(net_id_str, &end_ptr, 0); + if (net_id == ULONG_MAX || *net_id_str == 0 || *end_ptr != 0) { + logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); + exit(1); + } + } logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); // open the tunnel device before dropping privs @@ -438,7 +453,7 @@ int main(int argc, char **argv) { // "local", but that only works for the netd process itself. unsetenv("ANDROID_DNS_MODE"); - configure_interface(uplink_interface, plat_prefix, &tunnel); + configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); set_forwarding(forwarding_fd,"1\n"); diff --git a/config.c b/config.c index b82ac6c9d0..3d39ff071f 100644 --- a/config.c +++ b/config.c @@ -152,15 +152,16 @@ void free_config() { /* function: dns64_detection * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -void dns64_detection() { +void dns64_detection(unsigned net_id) { int backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; while(1) { - status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,&tmp_ptr); + status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,net_id,&tmp_ptr); if(status > 0) { memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); return; @@ -223,8 +224,10 @@ int subnet_from_interface(cnode *root, const char *interface) { * file - filename to parse * uplink_interface - interface to use to reach the internet and supplier of address space * plat_prefix - (optional) plat prefix to use, otherwise follow config file + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix) { +int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, + unsigned net_id) { cnode *root = config_node("", ""); void *tmp_ptr = NULL; @@ -277,7 +280,7 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!(Global_Clatd_Config.plat_from_dns64_hostname = config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME))) goto failed; - dns64_detection(); + dns64_detection(net_id); } } diff --git a/config.h b/config.h index 18760c3e5b..fa47152055 100644 --- a/config.h +++ b/config.h @@ -39,7 +39,8 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix); +int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, + unsigned net_id); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); #endif /* __CONFIG_H__ */ diff --git a/dns64.c b/dns64.c index b139fe4ee0..ce8539d404 100644 --- a/dns64.c +++ b/dns64.c @@ -27,13 +27,15 @@ #include "dns64.h" #include "logging.h" +#include "resolv_netid.h" /* function: plat_prefix * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on temporary failure, -1 on permanent failure - * ipv4_name - name to lookup - * prefix - the plat /96 prefix + * ipv4_name - name to lookup + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network + * prefix - the plat /96 prefix */ -int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { +int plat_prefix(const char *ipv4_name, unsigned net_id, 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; @@ -48,7 +50,7 @@ int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { bzero(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; - status = getaddrinfo(ipv4_name, NULL, &hints, &result); + status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); if(status != 0) { logmsg(ANDROID_LOG_ERROR,"plat_prefix/dns(%s) status = %d/%s\n", ipv4_name, status, gai_strerror(status)); return 0; diff --git a/dns64.h b/dns64.h index f295e5097b..f5eaea8bc5 100644 --- a/dns64.h +++ b/dns64.h @@ -18,6 +18,6 @@ #ifndef __DNS64_H__ #define __DNS64_H__ -int plat_prefix(const char *ipv4_name, struct in6_addr *prefix); +int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix); #endif From fc0f94a75c7a6d16ea9e327092f132a0d2c9175e Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 3 Jun 2014 14:41:57 -0400 Subject: [PATCH 071/261] Fix build. Remove superfluous check triggering compiler error. Change-Id: I408b54938acfa475cf8734a50a5459218a4b325a --- clatd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 7b8e43fec8..d8a9e72fe6 100644 --- a/clatd.c +++ b/clatd.c @@ -418,7 +418,7 @@ int main(int argc, char **argv) { if (net_id_str != NULL) { char *end_ptr; net_id = strtoul(net_id_str, &end_ptr, 0); - if (net_id == ULONG_MAX || *net_id_str == 0 || *end_ptr != 0) { + if (*net_id_str == 0 || *end_ptr != 0) { logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); exit(1); } From 91d0f1bc6dd24e54ed3caef9b08525b332ab0adf Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 2 Jun 2014 15:49:36 +0900 Subject: [PATCH 072/261] Make translate_packet take a fd instead of a tun header. This will make it easier to use separate fds for reading and writing in a future change. Bug: 15340961 Change-Id: I5b081b05765cae0488ac599be5738ce9737cae41 --- clatd.c | 21 +++++++++++++++++++-- clatd_test.cpp | 10 ++++------ translate.c | 25 +++++++------------------ translate.h | 3 +-- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/clatd.c b/clatd.c index d8a9e72fe6..af54ff26c4 100644 --- a/clatd.c +++ b/clatd.c @@ -306,7 +306,8 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { ssize_t readlen; uint8_t packet[PACKETLEN]; - // in case something ignores the packet length + // In case something ignores the packet length. + // TODO: remove it. memset(packet, 0, PACKETLEN); readlen = read(active_fd,packet,PACKETLEN); @@ -325,7 +326,23 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + struct tun_pi *tun_header = (struct tun_pi *) packet; + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + int fd; + uint16_t proto = ntohs(tun_header->proto); + if (proto == ETH_P_IP) { + fd = tunnel->fd6; + } else if (proto == ETH_P_IPV6) { + fd = tunnel->fd4; + } else { + logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); + return; + } + + translate_packet(fd, (proto == ETH_P_IP), packet + header_size, readlen - header_size); } } diff --git a/clatd_test.cpp b/clatd_test.cpp index 5172d3fb4d..bc32a84d94 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -424,17 +424,13 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { abort(); } - struct tun_data tunnel = { - "clat", "clat4", - fds[0], fds[1] - }; struct tun_pi tun_header = { 0, 0 }; char foo[512]; snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); check_packet(original, original_len, foo); - int read_fd; + int read_fd, write_fd; uint16_t expected_proto; int version = ip_version(original); switch (version) { @@ -442,18 +438,20 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * tun_header.proto = htons(ETH_P_IP); expected_proto = htons(ETH_P_IPV6); read_fd = fds[1]; + write_fd = fds[0]; break; case 6: tun_header.proto = htons(ETH_P_IPV6); expected_proto = htons(ETH_P_IP); read_fd = fds[0]; + write_fd = fds[1]; break; default: FAIL() << msg << ": Unsupported IP version " << version << "\n"; break; } - translate_packet(&tunnel, &tun_header, original, original_len); + translate_packet(write_fd, (version == 4), original, original_len); struct tun_pi new_tun_header; struct iovec iov[] = { diff --git a/translate.c b/translate.c index e93a93a8af..958ce2f2d7 100644 --- a/translate.c +++ b/translate.c @@ -466,15 +466,13 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t } /* function: translate_packet - * takes a tun header and a packet and sends it down the stack - * tunnel - tun device data - * tun_header - tun header + * takes a packet, translates it, and writes it to fd + * fd - fd to write translated packet to + * to_ipv6 - true if translating to ipv6, false if translating to ipv4 * packet - packet * packetsize - size of packet */ -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, - const uint8_t *packet, size_t packetsize) { - int fd; +void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize) { int iov_len = 0; // Allocate buffers for all packet headers. @@ -498,23 +496,14 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); + if (to_ipv6) { iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); } else { - logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); } if (iov_len > 0) { + fill_tun_header(&tun_targ, to_ipv6 ? ETH_P_IPV6 : ETH_P_IP); writev(fd, out, iov_len); } } diff --git a/translate.h b/translate.h index 6d4f126910..46e178b21b 100644 --- a/translate.h +++ b/translate.h @@ -60,8 +60,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol const struct iphdr *old_header); // Translate and send packets. -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, - const uint8_t *packet, size_t packetsize); +void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize); // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len); From 10e8827d636a72a7bcdfd52d15bad9342ae2a0a6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 2 Jun 2014 21:20:40 +0900 Subject: [PATCH 073/261] Use a raw socket to send IPv6 packets instead of a tun. This will allow us to bind the socket to a particular network. Bug: 15340961 Change-Id: I50857d372955f2b6f7035157c2968cda72c32585 --- clatd.c | 30 +++++++++++++++++++++++++----- clatd.h | 2 +- clatd_test.cpp | 37 +++++++++++++++++++++++++------------ translate.c | 39 +++++++++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/clatd.c b/clatd.c index af54ff26c4..be7d0c64e4 100644 --- a/clatd.c +++ b/clatd.c @@ -248,6 +248,24 @@ void drop_root() { } } +/* function: open_raw_socket + * opens the raw socket for sending IPv6 packets + */ +void open_raw_socket(struct tun_data *tunnel) { + int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (rawsock < 0) { + logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); + exit(1); + } + + int off = 0; + if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { + logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); + } + + tunnel->write_fd6 = rawsock; +} + /* function: configure_interface * reads the configuration and applies it to the interface * uplink_interface - network interface to use to reach the ipv6 internet @@ -282,7 +300,7 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device6, tunnel->fd6); + error = tun_alloc(tunnel->device6, tunnel->read_fd6); if(error < 0) { logmsg(ANDROID_LOG_FATAL,"tun_alloc failed: %s",strerror(errno)); exit(1); @@ -334,7 +352,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { int fd; uint16_t proto = ntohs(tun_header->proto); if (proto == ETH_P_IP) { - fd = tunnel->fd6; + fd = tunnel->write_fd6; } else if (proto == ETH_P_IPV6) { fd = tunnel->fd4; } else { @@ -357,7 +375,7 @@ void event_loop(const struct tun_data *tunnel) { // start the poll timer last_interface_poll = time(NULL); - wait_fd[0].fd = tunnel->fd6; + wait_fd[0].fd = tunnel->read_fd6; wait_fd[0].events = POLLIN; wait_fd[0].revents = 0; wait_fd[1].fd = tunnel->fd4; @@ -443,8 +461,8 @@ int main(int argc, char **argv) { 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) { + tunnel.read_fd6 = tun_open(); + if(tunnel.read_fd6 < 0) { logmsg(ANDROID_LOG_FATAL, "tun_open6 failed: %s", strerror(errno)); exit(1); } @@ -463,6 +481,8 @@ int main(int argc, char **argv) { exit(1); } + open_raw_socket(&tunnel); + // run under a regular user drop_root(); diff --git a/clatd.h b/clatd.h index 29c9acebb0..0a48013ac7 100644 --- a/clatd.h +++ b/clatd.h @@ -33,7 +33,7 @@ struct tun_data { char device6[IFNAMSIZ], device4[IFNAMSIZ]; - int fd6, fd4; + int read_fd6, write_fd6, fd4; }; #endif /* __CLATD_H__ */ diff --git a/clatd_test.cpp b/clatd_test.cpp index bc32a84d94..b35bf70abe 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -418,6 +418,13 @@ void fix_udp_checksum(uint8_t* packet) { udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); } +// Testing stub for send_rawv6. The real version uses sendmsg() with a +// destination IPv6 address, and attempting to call that on our test socketpair +// fd results in EINVAL. +extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} + void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen, const char *msg) { int fds[2]; @@ -453,19 +460,25 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * translate_packet(write_fd, (version == 4), original, original_len); - struct tun_pi new_tun_header; - struct iovec iov[] = { - { &new_tun_header, sizeof(new_tun_header) }, - { out, *outlen } - }; - int len = readv(read_fd, iov, 2); - if (len > (int) sizeof(new_tun_header)) { - ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; - EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; - *outlen = len - sizeof(new_tun_header); + if (version == 6) { + // Translating to IPv4. Expect a tun header. + struct tun_pi new_tun_header; + struct iovec iov[] = { + { &new_tun_header, sizeof(new_tun_header) }, + { out, *outlen } + }; + int len = readv(read_fd, iov, 2); + if (len > (int) sizeof(new_tun_header)) { + ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; + EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; + *outlen = len - sizeof(new_tun_header); + } else { + FAIL() << msg << ": Packet was not translated"; + *outlen = 0; + } } else { - FAIL() << msg << ": Packet was not translated"; - *outlen = 0; + // Translating to IPv6. Expect raw packet. + *outlen = read(read_fd, out, *outlen); } } diff --git a/translate.c b/translate.c index 958ce2f2d7..487468b9cb 100644 --- a/translate.c +++ b/translate.c @@ -465,6 +465,30 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t return CLAT_POS_PAYLOAD + 1; } +void send_tun(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} + +// Weak symbol so we can override it in the unit test. +void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak)); + +void send_rawv6(int fd, clat_packet out, int iov_len) { + // A send on a raw socket requires a destination address to be specified even if the socket's + // protocol is IPPROTO_RAW. This is the address that will be used in routing lookups; the + // destination address in the packet header only affects what appears on the wire, not where the + // packet is sent to. + static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 }; + static struct msghdr msg = { + .msg_name = &sin6, + .msg_namelen = sizeof(sin6), + }; + + msg.msg_iov = out, + msg.msg_iovlen = iov_len, + sin6.sin6_addr = ((struct ip6_hdr *) out[CLAT_POS_IPHDR].iov_base)->ip6_dst; + sendmsg(fd, &msg, 0); +} + /* function: translate_packet * takes a packet, translates it, and writes it to fd * fd - fd to write translated packet to @@ -486,7 +510,7 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { &tun_targ, 0 }, // Tunnel header. { iphdr, 0 }, // IP header. { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. @@ -498,12 +522,15 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets if (to_ipv6) { iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + if (iov_len > 0) { + send_rawv6(fd, out, iov_len); + } } else { iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } - - if (iov_len > 0) { - fill_tun_header(&tun_targ, to_ipv6 ? ETH_P_IPV6 : ETH_P_IP); - writev(fd, out, iov_len); + if (iov_len > 0) { + fill_tun_header(&tun_targ, ETH_P_IP); + out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ); + send_tun(fd, out, iov_len); + } } } From d44fac888a441cf21111358f43384f3942399d5d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 3 Jun 2014 12:56:38 +0900 Subject: [PATCH 074/261] Use a packet socket to receive packets. Combined with the previous change to send on a raw socket instead of a tunnel interface, this allows us to get rid of the clat interface and remove all the routing configuration code. Bug: 15340961 Change-Id: I9d9b90f9b4f58dcc3c102abcdc32a2c7ee86b01d --- Android.mk | 2 +- clatd.c | 197 +++++++++++++++++++++++++---------------------------- clatd.h | 4 +- config.c | 1 - dump.c | 6 +- getroute.c | 144 --------------------------------------- getroute.h | 35 ---------- setroute.c | 141 -------------------------------------- setroute.h | 27 -------- 9 files changed, 98 insertions(+), 459 deletions(-) delete mode 100644 getroute.c delete mode 100644 getroute.h delete mode 100644 setroute.c delete mode 100644 setroute.h diff --git a/Android.mk b/Android.mk index f94acdf205..a640e06df8 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 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_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index be7d0c64e4..001c4cb957 100644 --- a/clatd.c +++ b/clatd.c @@ -32,9 +32,12 @@ #include #include #include +#include #include #include #include +#include +#include #include @@ -44,31 +47,17 @@ #include "logging.h" #include "resolv_netid.h" #include "setif.h" -#include "setroute.h" #include "mtu.h" #include "getaddr.h" #include "dump.h" -#define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 -int forwarding_fd = -1; volatile sig_atomic_t running = 1; -/* function: set_forwarding - * enables/disables ipv6 forwarding - */ -void set_forwarding(int fd, const char *setting) { - /* we have to forward packets from the WAN to the tun interface */ - if(write(fd, setting, strlen(setting)) < 0) { - logmsg(ANDROID_LOG_FATAL,"set_forwarding(%s) failed: %s", setting, strerror(errno)); - exit(1); - } -} - /* function: stop_loop * signal handler: stop the event loop */ @@ -114,34 +103,51 @@ int tun_alloc(char *dev, int fd) { return 0; } -/* function: deconfigure_tun_ipv6 - * removes the ipv6 route - * tunnel - tun device data +/* function: configure_packet_socket + * Binds the packet socket and attaches the receive filter to it. + * sock - the socket to configure */ -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)); +int configure_packet_socket(int sock) { + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IPV6), + .sll_ifindex = if_nametoindex((char *) &Global_Clatd_Config.default_pdp_interface), + .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. + }; + if (bind(sock, (struct sockaddr *) &sll, sizeof(sll))) { + logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); + return 0; } -} -/* 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) { - int status; + uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; + struct sock_filter filter_code[] = { + // Load the first four bytes of the IPv6 destination address (starts 24 bytes in). + // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads + // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it + // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other + // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet). + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 24), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[0]), 0, 7), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 28), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[1]), 0, 5), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 32), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[2]), 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1), + BPF_STMT(BPF_RET | BPF_K, PACKETLEN), + BPF_STMT(BPF_RET | BPF_K, 0) + }; + struct sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code + }; - 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); + if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) { + logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno)); + return 0; } + + return 1; } /* function: interface_poll @@ -153,7 +159,8 @@ void interface_poll(const struct tun_data *tunnel) { 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); + logmsg(ANDROID_LOG_WARN,"unable to find an ipv6 ip on interface %s", + Global_Clatd_Config.default_pdp_interface); return; } @@ -165,12 +172,15 @@ void interface_poll(const struct tun_data *tunnel) { 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 + // 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); + + // Update our packet socket filter to reflect the new 464xlat IP address. + if (!configure_packet_socket(tunnel->read_fd6)) { + // Things aren't going to work. Bail out and hope we have better luck next time. + // We don't log an error here because attach_filter has already done so. + exit(1); + } } free(interface_ip); @@ -192,24 +202,10 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } - status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address, - 64, NULL); - if(status < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) failed: %s",strerror(-status)); - exit(1); - } - - if((status = if_up(tunnel->device6, Global_Clatd_Config.mtu)) < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(6) failed: %s",strerror(-status)); - exit(1); - } - if((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) { logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(4) failed: %s",strerror(-status)); exit(1); } - - configure_tun_ipv6(tunnel); } /* function: drop_root @@ -248,10 +244,10 @@ void drop_root() { } } -/* function: open_raw_socket - * opens the raw socket for sending IPv6 packets +/* function: open_socket + * opens raw and packet sockets */ -void open_raw_socket(struct tun_data *tunnel) { +void open_sockets(struct tun_data *tunnel) { int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); @@ -264,6 +260,14 @@ void open_raw_socket(struct tun_data *tunnel) { } tunnel->write_fd6 = rawsock; + + int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + if (packetsock < 0) { + logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + exit(1); + } + + tunnel->read_fd6 = packetsock; } /* function: configure_interface @@ -300,12 +304,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device6, tunnel->read_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)); @@ -322,13 +320,10 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, */ void read_packet(int active_fd, const struct tun_data *tunnel) { ssize_t readlen; - uint8_t packet[PACKETLEN]; + uint8_t buf[PACKETLEN], *packet; + int fd; - // In case something ignores the packet length. - // TODO: remove it. - memset(packet, 0, PACKETLEN); - - readlen = read(active_fd,packet,PACKETLEN); + readlen = read(active_fd, buf, PACKETLEN); if(readlen < 0) { logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); @@ -336,32 +331,37 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { } else if(readlen == 0) { logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed"); running = 0; - } else { + return; + } + + if (active_fd == tunnel->fd4) { ssize_t header_size = sizeof(struct tun_pi); - if(readlen < header_size) { + if (readlen < header_size) { logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen); return; } - struct tun_pi *tun_header = (struct tun_pi *) packet; - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); - } - - int fd; + struct tun_pi *tun_header = (struct tun_pi *) buf; uint16_t proto = ntohs(tun_header->proto); - if (proto == ETH_P_IP) { - fd = tunnel->write_fd6; - } else if (proto == ETH_P_IPV6) { - fd = tunnel->fd4; - } else { + if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); return; } - translate_packet(fd, (proto == ETH_P_IP), packet + header_size, readlen - header_size); + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + fd = tunnel->write_fd6; + packet = buf + header_size; + readlen -= header_size; + } else { + fd = tunnel->fd4; + packet = buf; } + + translate_packet(fd, (fd == tunnel->write_fd6), packet, readlen); } /* function: event_loop @@ -423,7 +423,6 @@ int main(int argc, char **argv) { char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; unsigned net_id = NETID_UNSET; - strcpy(tunnel.device6, DEVICENAME6); strcpy(tunnel.device4, DEVICENAME4); while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { @@ -460,28 +459,14 @@ int main(int argc, char **argv) { } logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); - // open the tunnel device before dropping privs - tunnel.read_fd6 = tun_open(); - if(tunnel.read_fd6 < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_open6 failed: %s", strerror(errno)); - exit(1); - } - + // open the tunnel device and our raw sockets before dropping privs 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); - } - - open_raw_socket(&tunnel); + open_sockets(&tunnel); // run under a regular user drop_root(); @@ -492,7 +477,10 @@ int main(int argc, char **argv) { configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); - set_forwarding(forwarding_fd,"1\n"); + if (!configure_packet_socket(tunnel.read_fd6)) { + // We've already logged an error. + exit(1); + } // Loop until someone sends us a signal or brings down the tun interface. if(signal(SIGTERM, stop_loop) == SIG_ERR) { @@ -501,7 +489,6 @@ int main(int argc, char **argv) { } event_loop(&tunnel); - set_forwarding(forwarding_fd,"0\n"); logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface); return 0; diff --git a/clatd.h b/clatd.h index 0a48013ac7..0f4809d321 100644 --- a/clatd.h +++ b/clatd.h @@ -23,7 +23,7 @@ #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) -#define CLATD_VERSION "1.2" +#define CLATD_VERSION "1.3" // how frequently (in seconds) to poll for an address change while traffic is passing #define INTERFACE_POLL_FREQUENCY 30 @@ -32,7 +32,7 @@ #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 struct tun_data { - char device6[IFNAMSIZ], device4[IFNAMSIZ]; + char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; }; diff --git a/config.c b/config.c index 3d39ff071f..d8bec80910 100644 --- a/config.c +++ b/config.c @@ -31,7 +31,6 @@ #include "logging.h" #include "getaddr.h" #include "clatd.h" -#include "setroute.h" struct clat_config Global_Clatd_Config; diff --git a/dump.c b/dump.c index 0fda4e7b0d..27b75d1e69 100644 --- a/dump.c +++ b/dump.c @@ -162,7 +162,7 @@ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, } /* 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) { +void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr)); @@ -205,7 +205,7 @@ void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t opti /* print ipv4/tcp header */ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size) { + const uint8_t *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); @@ -215,7 +215,7 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, /* print ipv6/tcp header */ void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size) { + const uint8_t *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); diff --git a/getroute.c b/getroute.c deleted file mode 100644 index a615a4f60c..0000000000 --- a/getroute.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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(__attribute__((unused)) 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 deleted file mode 100644 index e7b8670341..0000000000 --- a/getroute.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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/setroute.c b/setroute.c deleted file mode 100644 index f9dc365687..0000000000 --- a/setroute.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setroute.c - network route configuration - */ -#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_LOCAL; - rt.rtm_dst_len = prefixlen; - switch(change_type) { - case ROUTE_DELETE: - rt.rtm_scope = RT_SCOPE_NOWHERE; - type = RTM_DELROUTE; - break; - - case ROUTE_REPLACE: - flags = NLM_F_REPLACE; - case ROUTE_CREATE: - type = RTM_NEWROUTE; - flags |= NLM_F_CREATE; - if(gateway == NULL) { - rt.rtm_scope = RT_SCOPE_LINK; - } else { - rt.rtm_scope = RT_SCOPE_UNIVERSE; - } - rt.rtm_type = RTN_UNICAST; - //RTPROT_STATIC = from administrator's configuration - //RTPROT_BOOT = from an automatic process - rt.rtm_protocol = RTPROT_BOOT; - break; - - default: - retval = -EINVAL; - goto cleanup; - } - - flags |= NLM_F_REQUEST | NLM_F_ACK; - - msg = nlmsg_alloc_rtmsg(type, flags, &rt); - if(!msg) { - retval = -ENOMEM; - goto cleanup; - } - - if(nla_put(msg, RTA_DST, addr_size, destination) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if(gateway != NULL) - if(nla_put(msg, RTA_GATEWAY, addr_size, gateway) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if(nla_put(msg, RTA_OIF, 4, &ifindex) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if(nla_put(msg, RTA_PRIORITY, 4, &metric) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if(mtu > 0 && change_type != ROUTE_DELETE) { - // MTU is inside an RTA_METRICS nested message - struct nlattr *metrics = nla_nest_start(msg, RTA_METRICS); - if(metrics == NULL) { - retval = -ENOMEM; - goto cleanup; - } - - if(nla_put(msg, RTAX_MTU, 4, &mtu) < 0) { - retval = -ENOMEM; - goto cleanup; - } - - nla_nest_end(msg, metrics); - } - - retval = netlink_sendrecv(msg); - -cleanup: - if(msg) - nlmsg_free(msg); - - return retval; -} diff --git a/setroute.h b/setroute.h deleted file mode 100644 index 58f61cf3af..0000000000 --- a/setroute.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setroute.h - network route configuration - */ - -#ifndef __SETROUTE_H__ -#define __SETROUTE_H__ - -#define ROUTE_DELETE 0 -#define ROUTE_REPLACE 1 -#define ROUTE_CREATE 2 -int if_route(const char *ifname, int family, const void *destination, int cidr, const void *gateway, int metric, int mtu, int change_type); - -#endif From 10c1adce5e85b429bf45bf7f0208c6c37245fbfb Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 9 Jun 2014 13:55:50 +0900 Subject: [PATCH 075/261] Mark the raw socket used by clatd. This is required to use clatd on a non-default network. Bug: 15340961 Change-Id: I289e5600da31e963a13c00679fa816f576678d61 --- clatd.c | 56 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/clatd.c b/clatd.c index 001c4cb957..0ee3831ae3 100644 --- a/clatd.c +++ b/clatd.c @@ -178,7 +178,7 @@ void interface_poll(const struct tun_data *tunnel) { // Update our packet socket filter to reflect the new 464xlat IP address. if (!configure_packet_socket(tunnel->read_fd6)) { // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because attach_filter has already done so. + // We don't log an error here because configure_packet_socket has already done so. exit(1); } } @@ -244,10 +244,12 @@ void drop_root() { } } -/* function: open_socket - * opens raw and packet sockets +/* function: open_sockets + * opens a packet socket to receive IPv6 packets and a raw socket to send them + * tunnel - tun device data + * mark - the socket mark to use for the sending raw socket */ -void open_sockets(struct tun_data *tunnel) { +void open_sockets(struct tun_data *tunnel, uint32_t mark) { int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); @@ -258,6 +260,9 @@ void open_sockets(struct tun_data *tunnel) { if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); } + if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { + logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); + } tunnel->write_fd6 = rawsock; @@ -412,6 +417,18 @@ void print_help() { printf("-i [uplink interface]\n"); printf("-p [plat prefix]\n"); printf("-n [NetId]\n"); + printf("-m [socket mark]\n"); +} + +/* function: parse_unsigned + * parses a string as a decimal/hex/octal unsigned integer + * str - the string to parse + * out - the unsigned integer to write to, gets clobbered on failure + */ +int parse_unsigned(const char *str, unsigned *out) { + char *end_ptr; + *out = strtoul(str, &end_ptr, 0); + return *str && !*end_ptr; } /* function: main @@ -420,12 +437,13 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; unsigned net_id = NETID_UNSET; + uint32_t mark = MARK_UNSET; strcpy(tunnel.device4, DEVICENAME4); - while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { + while((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { switch(opt) { case 'i': uplink_interface = optarg; @@ -436,6 +454,9 @@ int main(int argc, char **argv) { case 'n': net_id_str = optarg; break; + case 'm': + mark_str = optarg; + break; case 'h': default: print_help(); @@ -446,18 +467,21 @@ int main(int argc, char **argv) { if(uplink_interface == NULL) { logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); - printf("I need an interface\n"); exit(1); } - if (net_id_str != NULL) { - char *end_ptr; - net_id = strtoul(net_id_str, &end_ptr, 0); - if (*net_id_str == 0 || *end_ptr != 0) { - logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); - exit(1); - } + + if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { + logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); + exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); + + if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { + logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); + exit(1); + } + + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, + uplink_interface, net_id_str, mark_str); // open the tunnel device and our raw sockets before dropping privs tunnel.fd4 = tun_open(); @@ -466,7 +490,7 @@ int main(int argc, char **argv) { exit(1); } - open_sockets(&tunnel); + open_sockets(&tunnel, mark); // run under a regular user drop_root(); From 3e7b1d8f783320d38382089a3c9a790ba13772dc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 9 Jun 2014 19:39:01 +0900 Subject: [PATCH 076/261] Log unexpected arguments. Bug: 15340961 Change-Id: Icde62d209e236d0156bf643bab694b92792be913 --- clatd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index 0ee3831ae3..6871129820 100644 --- a/clatd.c +++ b/clatd.c @@ -458,10 +458,11 @@ int main(int argc, char **argv) { mark_str = optarg; break; case 'h': - default: print_help(); + exit(0); + default: + logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char) optopt); exit(1); - break; } } @@ -480,8 +481,10 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, - uplink_interface, net_id_str, mark_str); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", + CLATD_VERSION, uplink_interface, + net_id_str ? net_id_str : "(none)", + mark_str ? mark_str : "(none)"); // open the tunnel device and our raw sockets before dropping privs tunnel.fd4 = tun_open(); From 247dd7199b4e89c6a974794056b468bfac5f0e51 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 30 May 2014 13:19:10 -0400 Subject: [PATCH 077/261] Make DNS request for plat prefix detection network specific. When plat prefix detection is done on a non-default network it must use the network specific version of getaddrinfo() so the plat prefix corresponds to the particular network. The network is specified to clatd via a NetID command line argument. (cherry picked from commit a1c871c8efad6c0b69e27d3b85e82a27e282b8be) Change-Id: I270c5afc4b445cf20e95fd3e58eada6bc24b16ef --- Android.mk | 2 +- clatd.c | 25 ++++++++++++++++++++----- config.c | 11 +++++++---- config.h | 3 ++- dns64.c | 10 ++++++---- dns64.h | 2 +- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Android.mk b/Android.mk index 72fc0f9565..f94acdf205 100644 --- a/Android.mk +++ b/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_C_INCLUDES := external/libnl/include +LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/clatd.c b/clatd.c index 63c4d6d6b9..7b8e43fec8 100644 --- a/clatd.c +++ b/clatd.c @@ -42,6 +42,7 @@ #include "clatd.h" #include "config.h" #include "logging.h" +#include "resolv_netid.h" #include "setif.h" #include "setroute.h" #include "mtu.h" @@ -252,11 +253,12 @@ void drop_root() { * uplink_interface - network interface to use to reach the ipv6 internet * plat_prefix - PLAT prefix to use * tunnel - tun device data + * net_id - NetID to use, NETID_UNSET indicates use of default network */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel) { +void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel, unsigned net_id) { int error; - if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix)) { + if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { logmsg(ANDROID_LOG_FATAL,"read_config failed"); exit(1); } @@ -374,6 +376,7 @@ void print_help() { printf("android-clat arguments:\n"); printf("-i [uplink interface]\n"); printf("-p [plat prefix]\n"); + printf("-n [NetId]\n"); } /* function: main @@ -382,12 +385,13 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; + unsigned net_id = NETID_UNSET; strcpy(tunnel.device6, DEVICENAME6); strcpy(tunnel.device4, DEVICENAME4); - while((opt = getopt(argc, argv, "i:p:h")) != -1) { + while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { switch(opt) { case 'i': uplink_interface = optarg; @@ -395,6 +399,9 @@ int main(int argc, char **argv) { case 'p': plat_prefix = optarg; break; + case 'n': + net_id_str = optarg; + break; case 'h': default: print_help(); @@ -408,6 +415,14 @@ int main(int argc, char **argv) { printf("I need an interface\n"); exit(1); } + if (net_id_str != NULL) { + char *end_ptr; + net_id = strtoul(net_id_str, &end_ptr, 0); + if (net_id == ULONG_MAX || *net_id_str == 0 || *end_ptr != 0) { + logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); + exit(1); + } + } logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); // open the tunnel device before dropping privs @@ -438,7 +453,7 @@ int main(int argc, char **argv) { // "local", but that only works for the netd process itself. unsetenv("ANDROID_DNS_MODE"); - configure_interface(uplink_interface, plat_prefix, &tunnel); + configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); set_forwarding(forwarding_fd,"1\n"); diff --git a/config.c b/config.c index b82ac6c9d0..3d39ff071f 100644 --- a/config.c +++ b/config.c @@ -152,15 +152,16 @@ void free_config() { /* function: dns64_detection * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -void dns64_detection() { +void dns64_detection(unsigned net_id) { int backoff_sleep, status; struct in6_addr tmp_ptr; backoff_sleep = 1; while(1) { - status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,&tmp_ptr); + status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,net_id,&tmp_ptr); if(status > 0) { memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); return; @@ -223,8 +224,10 @@ int subnet_from_interface(cnode *root, const char *interface) { * file - filename to parse * uplink_interface - interface to use to reach the internet and supplier of address space * plat_prefix - (optional) plat prefix to use, otherwise follow config file + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix) { +int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, + unsigned net_id) { cnode *root = config_node("", ""); void *tmp_ptr = NULL; @@ -277,7 +280,7 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!(Global_Clatd_Config.plat_from_dns64_hostname = config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME))) goto failed; - dns64_detection(); + dns64_detection(net_id); } } diff --git a/config.h b/config.h index 18760c3e5b..fa47152055 100644 --- a/config.h +++ b/config.h @@ -39,7 +39,8 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix); +int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, + unsigned net_id); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); #endif /* __CONFIG_H__ */ diff --git a/dns64.c b/dns64.c index b139fe4ee0..ce8539d404 100644 --- a/dns64.c +++ b/dns64.c @@ -27,13 +27,15 @@ #include "dns64.h" #include "logging.h" +#include "resolv_netid.h" /* function: plat_prefix * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on temporary failure, -1 on permanent failure - * ipv4_name - name to lookup - * prefix - the plat /96 prefix + * ipv4_name - name to lookup + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network + * prefix - the plat /96 prefix */ -int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { +int plat_prefix(const char *ipv4_name, unsigned net_id, 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; @@ -48,7 +50,7 @@ int plat_prefix(const char *ipv4_name, struct in6_addr *prefix) { bzero(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; - status = getaddrinfo(ipv4_name, NULL, &hints, &result); + status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); if(status != 0) { logmsg(ANDROID_LOG_ERROR,"plat_prefix/dns(%s) status = %d/%s\n", ipv4_name, status, gai_strerror(status)); return 0; diff --git a/dns64.h b/dns64.h index f295e5097b..f5eaea8bc5 100644 --- a/dns64.h +++ b/dns64.h @@ -18,6 +18,6 @@ #ifndef __DNS64_H__ #define __DNS64_H__ -int plat_prefix(const char *ipv4_name, struct in6_addr *prefix); +int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix); #endif From 464fb26dd770b52b44721d46ef00985dc8fc88ee Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 3 Jun 2014 14:41:57 -0400 Subject: [PATCH 078/261] Fix build. Remove superfluous check triggering compiler error. (cherry picked from commit fc0f94a75c7a6d16ea9e327092f132a0d2c9175e) Change-Id: I0366eb3882948d77a8be2d5d32169a13f412ad4e --- clatd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 7b8e43fec8..d8a9e72fe6 100644 --- a/clatd.c +++ b/clatd.c @@ -418,7 +418,7 @@ int main(int argc, char **argv) { if (net_id_str != NULL) { char *end_ptr; net_id = strtoul(net_id_str, &end_ptr, 0); - if (net_id == ULONG_MAX || *net_id_str == 0 || *end_ptr != 0) { + if (*net_id_str == 0 || *end_ptr != 0) { logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); exit(1); } From e24982ea3a4480d9d62c75b4d9a416b9479f7f0b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 2 Jun 2014 15:49:36 +0900 Subject: [PATCH 079/261] Make translate_packet take a fd instead of a tun header. This will make it easier to use separate fds for reading and writing in a future change. (cherry picked from commit 91d0f1bc6dd24e54ed3caef9b08525b332ab0adf) Bug: 15340961 Change-Id: I374b85f28ae570dc82d21e1ea82a2f072fd7fba3 --- clatd.c | 21 +++++++++++++++++++-- clatd_test.cpp | 10 ++++------ translate.c | 25 +++++++------------------ translate.h | 3 +-- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/clatd.c b/clatd.c index d8a9e72fe6..af54ff26c4 100644 --- a/clatd.c +++ b/clatd.c @@ -306,7 +306,8 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { ssize_t readlen; uint8_t packet[PACKETLEN]; - // in case something ignores the packet length + // In case something ignores the packet length. + // TODO: remove it. memset(packet, 0, PACKETLEN); readlen = read(active_fd,packet,PACKETLEN); @@ -325,7 +326,23 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size); + struct tun_pi *tun_header = (struct tun_pi *) packet; + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + int fd; + uint16_t proto = ntohs(tun_header->proto); + if (proto == ETH_P_IP) { + fd = tunnel->fd6; + } else if (proto == ETH_P_IPV6) { + fd = tunnel->fd4; + } else { + logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); + return; + } + + translate_packet(fd, (proto == ETH_P_IP), packet + header_size, readlen - header_size); } } diff --git a/clatd_test.cpp b/clatd_test.cpp index 5172d3fb4d..bc32a84d94 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -424,17 +424,13 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { abort(); } - struct tun_data tunnel = { - "clat", "clat4", - fds[0], fds[1] - }; struct tun_pi tun_header = { 0, 0 }; char foo[512]; snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); check_packet(original, original_len, foo); - int read_fd; + int read_fd, write_fd; uint16_t expected_proto; int version = ip_version(original); switch (version) { @@ -442,18 +438,20 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * tun_header.proto = htons(ETH_P_IP); expected_proto = htons(ETH_P_IPV6); read_fd = fds[1]; + write_fd = fds[0]; break; case 6: tun_header.proto = htons(ETH_P_IPV6); expected_proto = htons(ETH_P_IP); read_fd = fds[0]; + write_fd = fds[1]; break; default: FAIL() << msg << ": Unsupported IP version " << version << "\n"; break; } - translate_packet(&tunnel, &tun_header, original, original_len); + translate_packet(write_fd, (version == 4), original, original_len); struct tun_pi new_tun_header; struct iovec iov[] = { diff --git a/translate.c b/translate.c index e93a93a8af..958ce2f2d7 100644 --- a/translate.c +++ b/translate.c @@ -466,15 +466,13 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t } /* function: translate_packet - * takes a tun header and a packet and sends it down the stack - * tunnel - tun device data - * tun_header - tun header + * takes a packet, translates it, and writes it to fd + * fd - fd to write translated packet to + * to_ipv6 - true if translating to ipv6, false if translating to ipv4 * packet - packet * packetsize - size of packet */ -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, - const uint8_t *packet, size_t packetsize) { - int fd; +void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize) { int iov_len = 0; // Allocate buffers for all packet headers. @@ -498,23 +496,14 @@ void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "translate_packet: unexpected flags = %d", tun_header->flags); - } - - if(ntohs(tun_header->proto) == ETH_P_IP) { - fd = tunnel->fd6; - fill_tun_header(&tun_targ, ETH_P_IPV6); + if (to_ipv6) { iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } else if(ntohs(tun_header->proto) == ETH_P_IPV6) { - fd = tunnel->fd4; - fill_tun_header(&tun_targ, ETH_P_IP); - iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); } else { - logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto); + iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); } if (iov_len > 0) { + fill_tun_header(&tun_targ, to_ipv6 ? ETH_P_IPV6 : ETH_P_IP); writev(fd, out, iov_len); } } diff --git a/translate.h b/translate.h index 6d4f126910..46e178b21b 100644 --- a/translate.h +++ b/translate.h @@ -60,8 +60,7 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol const struct iphdr *old_header); // Translate and send packets. -void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, - const uint8_t *packet, size_t packetsize); +void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packetsize); // Translate IPv4 and IPv6 packets. int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len); From ce14088b80c2d22add83a616e567f9025d252ef7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 2 Jun 2014 21:20:40 +0900 Subject: [PATCH 080/261] Use a raw socket to send IPv6 packets instead of a tun. This will allow us to bind the socket to a particular network. (cherry picked from commit 10e8827d636a72a7bcdfd52d15bad9342ae2a0a6) Bug: 15340961 Change-Id: I0b62ef96364a90b9c0a9e3ac3ba97b5c19c89b69 --- clatd.c | 30 +++++++++++++++++++++++++----- clatd.h | 2 +- clatd_test.cpp | 37 +++++++++++++++++++++++++------------ translate.c | 39 +++++++++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/clatd.c b/clatd.c index af54ff26c4..be7d0c64e4 100644 --- a/clatd.c +++ b/clatd.c @@ -248,6 +248,24 @@ void drop_root() { } } +/* function: open_raw_socket + * opens the raw socket for sending IPv6 packets + */ +void open_raw_socket(struct tun_data *tunnel) { + int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (rawsock < 0) { + logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); + exit(1); + } + + int off = 0; + if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { + logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); + } + + tunnel->write_fd6 = rawsock; +} + /* function: configure_interface * reads the configuration and applies it to the interface * uplink_interface - network interface to use to reach the ipv6 internet @@ -282,7 +300,7 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device6, tunnel->fd6); + error = tun_alloc(tunnel->device6, tunnel->read_fd6); if(error < 0) { logmsg(ANDROID_LOG_FATAL,"tun_alloc failed: %s",strerror(errno)); exit(1); @@ -334,7 +352,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { int fd; uint16_t proto = ntohs(tun_header->proto); if (proto == ETH_P_IP) { - fd = tunnel->fd6; + fd = tunnel->write_fd6; } else if (proto == ETH_P_IPV6) { fd = tunnel->fd4; } else { @@ -357,7 +375,7 @@ void event_loop(const struct tun_data *tunnel) { // start the poll timer last_interface_poll = time(NULL); - wait_fd[0].fd = tunnel->fd6; + wait_fd[0].fd = tunnel->read_fd6; wait_fd[0].events = POLLIN; wait_fd[0].revents = 0; wait_fd[1].fd = tunnel->fd4; @@ -443,8 +461,8 @@ int main(int argc, char **argv) { 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) { + tunnel.read_fd6 = tun_open(); + if(tunnel.read_fd6 < 0) { logmsg(ANDROID_LOG_FATAL, "tun_open6 failed: %s", strerror(errno)); exit(1); } @@ -463,6 +481,8 @@ int main(int argc, char **argv) { exit(1); } + open_raw_socket(&tunnel); + // run under a regular user drop_root(); diff --git a/clatd.h b/clatd.h index 29c9acebb0..0a48013ac7 100644 --- a/clatd.h +++ b/clatd.h @@ -33,7 +33,7 @@ struct tun_data { char device6[IFNAMSIZ], device4[IFNAMSIZ]; - int fd6, fd4; + int read_fd6, write_fd6, fd4; }; #endif /* __CLATD_H__ */ diff --git a/clatd_test.cpp b/clatd_test.cpp index bc32a84d94..b35bf70abe 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -418,6 +418,13 @@ void fix_udp_checksum(uint8_t* packet) { udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); } +// Testing stub for send_rawv6. The real version uses sendmsg() with a +// destination IPv6 address, and attempting to call that on our test socketpair +// fd results in EINVAL. +extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} + void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen, const char *msg) { int fds[2]; @@ -453,19 +460,25 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * translate_packet(write_fd, (version == 4), original, original_len); - struct tun_pi new_tun_header; - struct iovec iov[] = { - { &new_tun_header, sizeof(new_tun_header) }, - { out, *outlen } - }; - int len = readv(read_fd, iov, 2); - if (len > (int) sizeof(new_tun_header)) { - ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; - EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; - *outlen = len - sizeof(new_tun_header); + if (version == 6) { + // Translating to IPv4. Expect a tun header. + struct tun_pi new_tun_header; + struct iovec iov[] = { + { &new_tun_header, sizeof(new_tun_header) }, + { out, *outlen } + }; + int len = readv(read_fd, iov, 2); + if (len > (int) sizeof(new_tun_header)) { + ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; + EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; + *outlen = len - sizeof(new_tun_header); + } else { + FAIL() << msg << ": Packet was not translated"; + *outlen = 0; + } } else { - FAIL() << msg << ": Packet was not translated"; - *outlen = 0; + // Translating to IPv6. Expect raw packet. + *outlen = read(read_fd, out, *outlen); } } diff --git a/translate.c b/translate.c index 958ce2f2d7..487468b9cb 100644 --- a/translate.c +++ b/translate.c @@ -465,6 +465,30 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t return CLAT_POS_PAYLOAD + 1; } +void send_tun(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} + +// Weak symbol so we can override it in the unit test. +void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak)); + +void send_rawv6(int fd, clat_packet out, int iov_len) { + // A send on a raw socket requires a destination address to be specified even if the socket's + // protocol is IPPROTO_RAW. This is the address that will be used in routing lookups; the + // destination address in the packet header only affects what appears on the wire, not where the + // packet is sent to. + static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 }; + static struct msghdr msg = { + .msg_name = &sin6, + .msg_namelen = sizeof(sin6), + }; + + msg.msg_iov = out, + msg.msg_iovlen = iov_len, + sin6.sin6_addr = ((struct ip6_hdr *) out[CLAT_POS_IPHDR].iov_base)->ip6_dst; + sendmsg(fd, &msg, 0); +} + /* function: translate_packet * takes a packet, translates it, and writes it to fd * fd - fd to write translated packet to @@ -486,7 +510,7 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { - { &tun_targ, sizeof(tun_targ) }, // Tunnel header. + { &tun_targ, 0 }, // Tunnel header. { iphdr, 0 }, // IP header. { fraghdr, 0 }, // Fragment header. { transporthdr, 0 }, // Transport layer header. @@ -498,12 +522,15 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets if (to_ipv6) { iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize); + if (iov_len > 0) { + send_rawv6(fd, out, iov_len); + } } else { iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize); - } - - if (iov_len > 0) { - fill_tun_header(&tun_targ, to_ipv6 ? ETH_P_IPV6 : ETH_P_IP); - writev(fd, out, iov_len); + if (iov_len > 0) { + fill_tun_header(&tun_targ, ETH_P_IP); + out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ); + send_tun(fd, out, iov_len); + } } } From f08c5aa666d170c17abb784081897ea169c1b67f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 3 Jun 2014 12:56:38 +0900 Subject: [PATCH 081/261] Use a packet socket to receive packets. Combined with the previous change to send on a raw socket instead of a tunnel interface, this allows us to get rid of the clat interface and remove all the routing configuration code. (cherry picked from commit d44fac888a441cf21111358f43384f3942399d5d) Bug: 15340961 Change-Id: I2147dbfc07321bbd64d3fb9762b1120487712133 --- Android.mk | 2 +- clatd.c | 197 +++++++++++++++++++++++++---------------------------- clatd.h | 4 +- config.c | 1 - dump.c | 6 +- getroute.c | 144 --------------------------------------- getroute.h | 35 ---------- setroute.c | 141 -------------------------------------- setroute.h | 27 -------- 9 files changed, 98 insertions(+), 459 deletions(-) delete mode 100644 getroute.c delete mode 100644 getroute.h delete mode 100644 setroute.c delete mode 100644 setroute.h diff --git a/Android.mk b/Android.mk index f94acdf205..a640e06df8 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 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_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index be7d0c64e4..001c4cb957 100644 --- a/clatd.c +++ b/clatd.c @@ -32,9 +32,12 @@ #include #include #include +#include #include #include #include +#include +#include #include @@ -44,31 +47,17 @@ #include "logging.h" #include "resolv_netid.h" #include "setif.h" -#include "setroute.h" #include "mtu.h" #include "getaddr.h" #include "dump.h" -#define DEVICENAME6 "clat" #define DEVICENAME4 "clat4" /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 -int forwarding_fd = -1; volatile sig_atomic_t running = 1; -/* function: set_forwarding - * enables/disables ipv6 forwarding - */ -void set_forwarding(int fd, const char *setting) { - /* we have to forward packets from the WAN to the tun interface */ - if(write(fd, setting, strlen(setting)) < 0) { - logmsg(ANDROID_LOG_FATAL,"set_forwarding(%s) failed: %s", setting, strerror(errno)); - exit(1); - } -} - /* function: stop_loop * signal handler: stop the event loop */ @@ -114,34 +103,51 @@ int tun_alloc(char *dev, int fd) { return 0; } -/* function: deconfigure_tun_ipv6 - * removes the ipv6 route - * tunnel - tun device data +/* function: configure_packet_socket + * Binds the packet socket and attaches the receive filter to it. + * sock - the socket to configure */ -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)); +int configure_packet_socket(int sock) { + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IPV6), + .sll_ifindex = if_nametoindex((char *) &Global_Clatd_Config.default_pdp_interface), + .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. + }; + if (bind(sock, (struct sockaddr *) &sll, sizeof(sll))) { + logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); + return 0; } -} -/* 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) { - int status; + uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; + struct sock_filter filter_code[] = { + // Load the first four bytes of the IPv6 destination address (starts 24 bytes in). + // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads + // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it + // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other + // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet). + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 24), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[0]), 0, 7), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 28), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[1]), 0, 5), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 32), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[2]), 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1), + BPF_STMT(BPF_RET | BPF_K, PACKETLEN), + BPF_STMT(BPF_RET | BPF_K, 0) + }; + struct sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code + }; - 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); + if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) { + logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno)); + return 0; } + + return 1; } /* function: interface_poll @@ -153,7 +159,8 @@ void interface_poll(const struct tun_data *tunnel) { 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); + logmsg(ANDROID_LOG_WARN,"unable to find an ipv6 ip on interface %s", + Global_Clatd_Config.default_pdp_interface); return; } @@ -165,12 +172,15 @@ void interface_poll(const struct tun_data *tunnel) { 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 + // 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); + + // Update our packet socket filter to reflect the new 464xlat IP address. + if (!configure_packet_socket(tunnel->read_fd6)) { + // Things aren't going to work. Bail out and hope we have better luck next time. + // We don't log an error here because attach_filter has already done so. + exit(1); + } } free(interface_ip); @@ -192,24 +202,10 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } - status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address, - 64, NULL); - if(status < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) failed: %s",strerror(-status)); - exit(1); - } - - if((status = if_up(tunnel->device6, Global_Clatd_Config.mtu)) < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(6) failed: %s",strerror(-status)); - exit(1); - } - if((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) { logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(4) failed: %s",strerror(-status)); exit(1); } - - configure_tun_ipv6(tunnel); } /* function: drop_root @@ -248,10 +244,10 @@ void drop_root() { } } -/* function: open_raw_socket - * opens the raw socket for sending IPv6 packets +/* function: open_socket + * opens raw and packet sockets */ -void open_raw_socket(struct tun_data *tunnel) { +void open_sockets(struct tun_data *tunnel) { int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); @@ -264,6 +260,14 @@ void open_raw_socket(struct tun_data *tunnel) { } tunnel->write_fd6 = rawsock; + + int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + if (packetsock < 0) { + logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + exit(1); + } + + tunnel->read_fd6 = packetsock; } /* function: configure_interface @@ -300,12 +304,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device6, tunnel->read_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)); @@ -322,13 +320,10 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, */ void read_packet(int active_fd, const struct tun_data *tunnel) { ssize_t readlen; - uint8_t packet[PACKETLEN]; + uint8_t buf[PACKETLEN], *packet; + int fd; - // In case something ignores the packet length. - // TODO: remove it. - memset(packet, 0, PACKETLEN); - - readlen = read(active_fd,packet,PACKETLEN); + readlen = read(active_fd, buf, PACKETLEN); if(readlen < 0) { logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); @@ -336,32 +331,37 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { } else if(readlen == 0) { logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed"); running = 0; - } else { + return; + } + + if (active_fd == tunnel->fd4) { ssize_t header_size = sizeof(struct tun_pi); - if(readlen < header_size) { + if (readlen < header_size) { logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen); return; } - struct tun_pi *tun_header = (struct tun_pi *) packet; - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); - } - - int fd; + struct tun_pi *tun_header = (struct tun_pi *) buf; uint16_t proto = ntohs(tun_header->proto); - if (proto == ETH_P_IP) { - fd = tunnel->write_fd6; - } else if (proto == ETH_P_IPV6) { - fd = tunnel->fd4; - } else { + if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); return; } - translate_packet(fd, (proto == ETH_P_IP), packet + header_size, readlen - header_size); + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + fd = tunnel->write_fd6; + packet = buf + header_size; + readlen -= header_size; + } else { + fd = tunnel->fd4; + packet = buf; } + + translate_packet(fd, (fd == tunnel->write_fd6), packet, readlen); } /* function: event_loop @@ -423,7 +423,6 @@ int main(int argc, char **argv) { char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; unsigned net_id = NETID_UNSET; - strcpy(tunnel.device6, DEVICENAME6); strcpy(tunnel.device4, DEVICENAME4); while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { @@ -460,28 +459,14 @@ int main(int argc, char **argv) { } logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); - // open the tunnel device before dropping privs - tunnel.read_fd6 = tun_open(); - if(tunnel.read_fd6 < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_open6 failed: %s", strerror(errno)); - exit(1); - } - + // open the tunnel device and our raw sockets before dropping privs 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); - } - - open_raw_socket(&tunnel); + open_sockets(&tunnel); // run under a regular user drop_root(); @@ -492,7 +477,10 @@ int main(int argc, char **argv) { configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); - set_forwarding(forwarding_fd,"1\n"); + if (!configure_packet_socket(tunnel.read_fd6)) { + // We've already logged an error. + exit(1); + } // Loop until someone sends us a signal or brings down the tun interface. if(signal(SIGTERM, stop_loop) == SIG_ERR) { @@ -501,7 +489,6 @@ int main(int argc, char **argv) { } event_loop(&tunnel); - set_forwarding(forwarding_fd,"0\n"); logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface); return 0; diff --git a/clatd.h b/clatd.h index 0a48013ac7..0f4809d321 100644 --- a/clatd.h +++ b/clatd.h @@ -23,7 +23,7 @@ #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) -#define CLATD_VERSION "1.2" +#define CLATD_VERSION "1.3" // how frequently (in seconds) to poll for an address change while traffic is passing #define INTERFACE_POLL_FREQUENCY 30 @@ -32,7 +32,7 @@ #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 struct tun_data { - char device6[IFNAMSIZ], device4[IFNAMSIZ]; + char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; }; diff --git a/config.c b/config.c index 3d39ff071f..d8bec80910 100644 --- a/config.c +++ b/config.c @@ -31,7 +31,6 @@ #include "logging.h" #include "getaddr.h" #include "clatd.h" -#include "setroute.h" struct clat_config Global_Clatd_Config; diff --git a/dump.c b/dump.c index 0fda4e7b0d..27b75d1e69 100644 --- a/dump.c +++ b/dump.c @@ -162,7 +162,7 @@ void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, } /* 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) { +void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr)); @@ -205,7 +205,7 @@ void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t opti /* print ipv4/tcp header */ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size) { + const uint8_t *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size); @@ -215,7 +215,7 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, /* print ipv6/tcp header */ void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size) { + const uint8_t *options, size_t options_size) { uint32_t temp_checksum; temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP); diff --git a/getroute.c b/getroute.c deleted file mode 100644 index a615a4f60c..0000000000 --- a/getroute.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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(__attribute__((unused)) 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 deleted file mode 100644 index e7b8670341..0000000000 --- a/getroute.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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/setroute.c b/setroute.c deleted file mode 100644 index cffee9fb99..0000000000 --- a/setroute.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setroute.c - network route configuration - */ -#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; -} diff --git a/setroute.h b/setroute.h deleted file mode 100644 index 58f61cf3af..0000000000 --- a/setroute.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setroute.h - network route configuration - */ - -#ifndef __SETROUTE_H__ -#define __SETROUTE_H__ - -#define ROUTE_DELETE 0 -#define ROUTE_REPLACE 1 -#define ROUTE_CREATE 2 -int if_route(const char *ifname, int family, const void *destination, int cidr, const void *gateway, int metric, int mtu, int change_type); - -#endif From 7564761e2d98ce52a128b2338ec5edcd13aaed01 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 9 Jun 2014 13:55:50 +0900 Subject: [PATCH 082/261] Mark the raw socket used by clatd. This is required to use clatd on a non-default network. (cherry picked from commit 10c1adce5e85b429bf45bf7f0208c6c37245fbfb) Bug: 15340961 Change-Id: I779c83644727227a109bfeec1565364336b5ac2b --- clatd.c | 56 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/clatd.c b/clatd.c index 001c4cb957..0ee3831ae3 100644 --- a/clatd.c +++ b/clatd.c @@ -178,7 +178,7 @@ void interface_poll(const struct tun_data *tunnel) { // Update our packet socket filter to reflect the new 464xlat IP address. if (!configure_packet_socket(tunnel->read_fd6)) { // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because attach_filter has already done so. + // We don't log an error here because configure_packet_socket has already done so. exit(1); } } @@ -244,10 +244,12 @@ void drop_root() { } } -/* function: open_socket - * opens raw and packet sockets +/* function: open_sockets + * opens a packet socket to receive IPv6 packets and a raw socket to send them + * tunnel - tun device data + * mark - the socket mark to use for the sending raw socket */ -void open_sockets(struct tun_data *tunnel) { +void open_sockets(struct tun_data *tunnel, uint32_t mark) { int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); @@ -258,6 +260,9 @@ void open_sockets(struct tun_data *tunnel) { if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); } + if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { + logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); + } tunnel->write_fd6 = rawsock; @@ -412,6 +417,18 @@ void print_help() { printf("-i [uplink interface]\n"); printf("-p [plat prefix]\n"); printf("-n [NetId]\n"); + printf("-m [socket mark]\n"); +} + +/* function: parse_unsigned + * parses a string as a decimal/hex/octal unsigned integer + * str - the string to parse + * out - the unsigned integer to write to, gets clobbered on failure + */ +int parse_unsigned(const char *str, unsigned *out) { + char *end_ptr; + *out = strtoul(str, &end_ptr, 0); + return *str && !*end_ptr; } /* function: main @@ -420,12 +437,13 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; unsigned net_id = NETID_UNSET; + uint32_t mark = MARK_UNSET; strcpy(tunnel.device4, DEVICENAME4); - while((opt = getopt(argc, argv, "i:p:n:h")) != -1) { + while((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { switch(opt) { case 'i': uplink_interface = optarg; @@ -436,6 +454,9 @@ int main(int argc, char **argv) { case 'n': net_id_str = optarg; break; + case 'm': + mark_str = optarg; + break; case 'h': default: print_help(); @@ -446,18 +467,21 @@ int main(int argc, char **argv) { if(uplink_interface == NULL) { logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); - printf("I need an interface\n"); exit(1); } - if (net_id_str != NULL) { - char *end_ptr; - net_id = strtoul(net_id_str, &end_ptr, 0); - if (*net_id_str == 0 || *end_ptr != 0) { - logmsg(ANDROID_LOG_FATAL, "clatd called with invalid NetID %s", net_id_str); - exit(1); - } + + if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { + logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); + exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface); + + if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { + logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); + exit(1); + } + + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, + uplink_interface, net_id_str, mark_str); // open the tunnel device and our raw sockets before dropping privs tunnel.fd4 = tun_open(); @@ -466,7 +490,7 @@ int main(int argc, char **argv) { exit(1); } - open_sockets(&tunnel); + open_sockets(&tunnel, mark); // run under a regular user drop_root(); From b9b471e1e3788a398adeb0a04bfc52d6ba7c3cf6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 9 Jun 2014 19:39:01 +0900 Subject: [PATCH 083/261] Log unexpected arguments. (cherry picked from commit 3e7b1d8f783320d38382089a3c9a790ba13772dc) Bug: 15340961 Change-Id: Iff172b066aa468f9ee36aab661a8b51cb03db95b --- clatd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index 0ee3831ae3..6871129820 100644 --- a/clatd.c +++ b/clatd.c @@ -458,10 +458,11 @@ int main(int argc, char **argv) { mark_str = optarg; break; case 'h': - default: print_help(); + exit(0); + default: + logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char) optopt); exit(1); - break; } } @@ -480,8 +481,10 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, - uplink_interface, net_id_str, mark_str); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", + CLATD_VERSION, uplink_interface, + net_id_str ? net_id_str : "(none)", + mark_str ? mark_str : "(none)"); // open the tunnel device and our raw sockets before dropping privs tunnel.fd4 = tun_open(); From 7045e27d4946330f3152218fdaa81b37d02b6d6c Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 13 Jun 2014 21:36:01 +0900 Subject: [PATCH 084/261] Create the tun device after dropping root privileges. Currently, clatd creates the clat4 interface as root using root's dac_override abilities. Instead, change the drop_root code to acquire membership in the AID_VPN group, and use that membership to create the interface. This removes the need for dac_override. Change-Id: I1f824254f52a441f21c5b7963d9993be88cea2db --- clatd.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/clatd.c b/clatd.c index 6871129820..1333e303a6 100644 --- a/clatd.c +++ b/clatd.c @@ -212,7 +212,7 @@ void configure_tun_ip(const struct tun_data *tunnel) { * drops root privs but keeps the needed capability */ void drop_root() { - gid_t groups[] = { AID_INET }; + gid_t groups[] = { AID_INET, AID_VPN }; if(setgroups(sizeof(groups)/sizeof(groups[0]), groups) < 0) { logmsg(ANDROID_LOG_FATAL,"drop_root/setgroups failed: %s",strerror(errno)); exit(1); @@ -486,18 +486,19 @@ int main(int argc, char **argv) { net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); - // open the tunnel device and our raw sockets before dropping privs + // open our raw sockets before dropping privs + open_sockets(&tunnel, mark); + + // run under a regular user + drop_root(); + + // we can create tun devices as non-root because we're in the VPN group. tunnel.fd4 = tun_open(); if(tunnel.fd4 < 0) { logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); exit(1); } - open_sockets(&tunnel, mark); - - // run under a regular user - drop_root(); - // When run from netd, the environment variable ANDROID_DNS_MODE is set to // "local", but that only works for the netd process itself. unsetenv("ANDROID_DNS_MODE"); From 787ea96922dc3942946fd4ad1e0495d4e06e0c2f Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 18 Jul 2014 17:25:26 -0700 Subject: [PATCH 085/261] Fix implicit declaration of function 'prctl' in clatd. Change-Id: I55b46a2b65e4168f1af74a6220dbccc4b1147aa6 --- clatd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clatd.c b/clatd.c index 1333e303a6..c2199ac33d 100644 --- a/clatd.c +++ b/clatd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include From 3afe9ae11d9268037ca7b56c0aba4ef9ae477fd7 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 18 Jul 2014 17:25:26 -0700 Subject: [PATCH 086/261] Fix implicit declaration of function 'prctl' in clatd. (cherry picked from commit 787ea96922dc3942946fd4ad1e0495d4e06e0c2f) Change-Id: I4582222c8576353b800cc7d30f16c5dbe5f2e8a9 --- clatd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clatd.c b/clatd.c index 1333e303a6..c2199ac33d 100644 --- a/clatd.c +++ b/clatd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include From 18921713780edb45ceef327d5fcf3387818300f3 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 18 Jul 2014 17:42:51 -0700 Subject: [PATCH 087/261] Remove unused #include. Change-Id: I713af06725f457ba355a5419f7fb7c3c53534131 --- clatd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clatd.c b/clatd.c index c2199ac33d..56eb66a06f 100644 --- a/clatd.c +++ b/clatd.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include From 545bdca4e73b5aa56b81a735ead316eb6d33c326 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 18 Jul 2014 17:42:51 -0700 Subject: [PATCH 088/261] Remove unused #include. (cherry picked from commit 18921713780edb45ceef327d5fcf3387818300f3) Change-Id: I132f7a4ea1a73073fe22ca5f3d89644f2a302db3 --- clatd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clatd.c b/clatd.c index c2199ac33d..56eb66a06f 100644 --- a/clatd.c +++ b/clatd.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include From dce3ddf54083ccd0e3752c4c08013688f79baa7a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 25 Aug 2014 16:07:12 -0700 Subject: [PATCH 089/261] Call read on any event, not just on POLLIN. The main clatd event loop calls poll() in a loop, but reads only from FDs that have POLLIN set. This causes it to get into an infinite loop if one of the fds has POLLERR set. Instead, read from all fds that have reported events. The read causes the kernel to return the error to userspace and clear the socket error flag, and poll starts working correctly after that. Bug: 17183471 Bug: 17186694 Change-Id: Ie25853e0d60c077d2478b3e5154946e201f96dca --- clatd.c | 23 ++++++++++++----------- clatd.h | 2 ++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clatd.c b/clatd.c index 56eb66a06f..dbf725b99c 100644 --- a/clatd.c +++ b/clatd.c @@ -375,27 +375,28 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { */ void event_loop(const struct tun_data *tunnel) { time_t last_interface_poll; - struct pollfd wait_fd[2]; + struct pollfd wait_fd[] = { + { tunnel->read_fd6, POLLIN, 0 }, + { tunnel->fd4, POLLIN, 0 }, + }; // start the poll timer last_interface_poll = time(NULL); - wait_fd[0].fd = tunnel->read_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) { + size_t i; + for(i = 0; i < ARRAY_SIZE(wait_fd); i++) { + // Call read_packet if the socket has data to be read, but also if an + // error is waiting. If we don't call read() after getting POLLERR, a + // subsequent poll() will return immediately with POLLERR again, + // causing this code to spin in a loop. Calling read() will clear the + // socket error flag instead. + if(wait_fd[i].revents != 0) { read_packet(wait_fd[i].fd,tunnel); } } diff --git a/clatd.h b/clatd.h index 0f4809d321..ca7369b27c 100644 --- a/clatd.h +++ b/clatd.h @@ -25,6 +25,8 @@ #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) #define CLATD_VERSION "1.3" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + // how frequently (in seconds) to poll for an address change while traffic is passing #define INTERFACE_POLL_FREQUENCY 30 From 05ff508a793c2113ca2a06942f0cec1aec220799 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Tue, 26 Aug 2014 10:46:26 -0700 Subject: [PATCH 090/261] Remove unused variables. BUG: 17281763 Change-Id: Ia9cbda2897e46ca757910d59d0cbb6de07d55482 --- clatd_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index b35bf70abe..9a09b48a15 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -639,7 +639,7 @@ TEST_F(ClatdTest, AdjustChecksum) { { 0x1215, 0x5560, 0x15560 + 20, 0x1200 }, { 0xd0c7, 0x3ad0, 0x2644b, 0xa74a }, }; - unsigned i, failed = 0; + unsigned i = 0; for (i = 0; i < ARRAYSIZE(DATA); i++) { struct checksum_data *data = DATA + i; @@ -672,7 +672,6 @@ TEST_F(ClatdTest, Translate) { } TEST_F(ClatdTest, Fragmentation) { - int len, i; check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation"); From 89f49ae6d7ddde7a765cce7ecd5006ecf97e8754 Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Mon, 22 Sep 2014 13:32:27 +0900 Subject: [PATCH 091/261] Simplify and always retry DNS64 prefix discovery. - Only request AAAAs for the IPv4-only hostname, since that's all the information being used. Perhaps at some point in the future when _validation_ of the IPv4 address(es)'s presence within the IPv6 address(es) is being done this should be revisited. - Only use the first AAAA returned to find the DNS64 prefix. It's not clear how to properly resolve any potential DNS64 prefix conflicts anyway. - Re-try DNS64 prefix discovery on any type of error. If clatd was started (presumably an IPv6-only network), retry DNS64 prefix discovery with exponential backoff. Some "permanent" errors can be indistinguishable from transient errors (e.g. receiving an erroneous SERVFAIL from a DNS resolver, ...). - Fix-up back-off logic to not sleep 128 then 120, 120, ... :-) Bug: 17569702 Change-Id: I226ea7772cd7c88d2b60153ef76e5435400e11aa --- config.c | 9 ++----- dns64.c | 71 ++++++++++++++++++-------------------------------------- 2 files changed, 25 insertions(+), 55 deletions(-) diff --git a/config.c b/config.c index d8bec80910..13889a0f6c 100644 --- a/config.c +++ b/config.c @@ -165,16 +165,11 @@ void dns64_detection(unsigned net_id) { 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); + logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); sleep(backoff_sleep); + backoff_sleep *= 2; if(backoff_sleep >= 120) { backoff_sleep = 120; - } else { - backoff_sleep *= 2; } } } diff --git a/dns64.c b/dns64.c index ce8539d404..4e9252dc2e 100644 --- a/dns64.c +++ b/dns64.c @@ -30,69 +30,44 @@ #include "resolv_netid.h" /* function: plat_prefix - * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on temporary failure, -1 on permanent failure + * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on failure * ipv4_name - name to lookup * net_id - (optional) netId to use, NETID_UNSET indicates use of default network * prefix - the plat /96 prefix */ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) { - 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; + const struct addrinfo hints = { + .ai_family = AF_INET6, + }; + int status; + struct addrinfo *result = NULL; + struct in6_addr plat_addr; char plat_addr_str[INET6_ADDRSTRLEN]; logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); - result = NULL; - plat_addr_set = 0; - ipv4_records = ipv6_records = 0; - - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); - if(status != 0) { - logmsg(ANDROID_LOG_ERROR,"plat_prefix/dns(%s) status = %d/%s\n", ipv4_name, status, gai_strerror(status)); + if (status != 0 || result == NULL) { + logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", + 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; + // Use only the first result. If other records are present, possibly with + // differing DNS64 prefixes they are ignored (there is very little sensible + // that could be done with them at this time anyway). - if(!plat_addr_set) { - plat_addr = this_plat_addr; - plat_addr_set = 1; - continue; - } + if (result->ai_family != AF_INET6) { + logmsg(ANDROID_LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family); + return 0; + } + plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr; + // Only /96 DNS64 prefixes are supported at this time. + plat_addr.s6_addr32[3] = 0; + freeaddrinfo(result); - inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)); - if(!IN6_ARE_ADDR_EQUAL(&plat_addr, &this_plat_addr)) { - char this_plat_addr_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &this_plat_addr, this_plat_addr_str, sizeof(this_plat_addr_str)); - logmsg(ANDROID_LOG_ERROR,"plat_prefix/two different plat addrs = %s,%s", - plat_addr_str,this_plat_addr_str); - } - } - if(result != NULL) { - freeaddrinfo(result); - } - if(ipv4_records > 0 && ipv6_records == 0) { - logmsg(ANDROID_LOG_WARN,"plat_prefix/no dns64 detected\n"); - return -1; - } - - logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", plat_addr_str); + logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", + inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str))); *prefix = plat_addr; return 1; } From 0ec5dfa1da6a97532dc8cca825bae4edfb7d3dce Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Mon, 22 Sep 2014 13:32:27 +0900 Subject: [PATCH 092/261] Simplify and always retry DNS64 prefix discovery. - Only request AAAAs for the IPv4-only hostname, since that's all the information being used. Perhaps at some point in the future when _validation_ of the IPv4 address(es)'s presence within the IPv6 address(es) is being done this should be revisited. - Only use the first AAAA returned to find the DNS64 prefix. It's not clear how to properly resolve any potential DNS64 prefix conflicts anyway. - Re-try DNS64 prefix discovery on any type of error. If clatd was started (presumably an IPv6-only network), retry DNS64 prefix discovery with exponential backoff. Some "permanent" errors can be indistinguishable from transient errors (e.g. receiving an erroneous SERVFAIL from a DNS resolver, ...). - Fix-up back-off logic to not sleep 128 then 120, 120, ... :-) Bug: 17569702 Change-Id: I226ea7772cd7c88d2b60153ef76e5435400e11aa --- config.c | 9 ++----- dns64.c | 71 ++++++++++++++++++-------------------------------------- 2 files changed, 25 insertions(+), 55 deletions(-) diff --git a/config.c b/config.c index d8bec80910..13889a0f6c 100644 --- a/config.c +++ b/config.c @@ -165,16 +165,11 @@ void dns64_detection(unsigned net_id) { 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); + logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); sleep(backoff_sleep); + backoff_sleep *= 2; if(backoff_sleep >= 120) { backoff_sleep = 120; - } else { - backoff_sleep *= 2; } } } diff --git a/dns64.c b/dns64.c index ce8539d404..4e9252dc2e 100644 --- a/dns64.c +++ b/dns64.c @@ -30,69 +30,44 @@ #include "resolv_netid.h" /* function: plat_prefix - * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on temporary failure, -1 on permanent failure + * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on failure * ipv4_name - name to lookup * net_id - (optional) netId to use, NETID_UNSET indicates use of default network * prefix - the plat /96 prefix */ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) { - 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; + const struct addrinfo hints = { + .ai_family = AF_INET6, + }; + int status; + struct addrinfo *result = NULL; + struct in6_addr plat_addr; char plat_addr_str[INET6_ADDRSTRLEN]; logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); - result = NULL; - plat_addr_set = 0; - ipv4_records = ipv6_records = 0; - - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); - if(status != 0) { - logmsg(ANDROID_LOG_ERROR,"plat_prefix/dns(%s) status = %d/%s\n", ipv4_name, status, gai_strerror(status)); + if (status != 0 || result == NULL) { + logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", + 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; + // Use only the first result. If other records are present, possibly with + // differing DNS64 prefixes they are ignored (there is very little sensible + // that could be done with them at this time anyway). - if(!plat_addr_set) { - plat_addr = this_plat_addr; - plat_addr_set = 1; - continue; - } + if (result->ai_family != AF_INET6) { + logmsg(ANDROID_LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family); + return 0; + } + plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr; + // Only /96 DNS64 prefixes are supported at this time. + plat_addr.s6_addr32[3] = 0; + freeaddrinfo(result); - inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str)); - if(!IN6_ARE_ADDR_EQUAL(&plat_addr, &this_plat_addr)) { - char this_plat_addr_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &this_plat_addr, this_plat_addr_str, sizeof(this_plat_addr_str)); - logmsg(ANDROID_LOG_ERROR,"plat_prefix/two different plat addrs = %s,%s", - plat_addr_str,this_plat_addr_str); - } - } - if(result != NULL) { - freeaddrinfo(result); - } - if(ipv4_records > 0 && ipv6_records == 0) { - logmsg(ANDROID_LOG_WARN,"plat_prefix/no dns64 detected\n"); - return -1; - } - - logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", plat_addr_str); + logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", + inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str))); *prefix = plat_addr; return 1; } From 74151e9d0cda83f77d9dbce62bf8a1340cb08386 Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Thu, 23 Oct 2014 09:38:19 -0700 Subject: [PATCH 093/261] Cleanup: Remove unused "ipv6_local_address". This was originally used on the "clat" interface. That interface is not created anymore (since the switch to using a raw socket). Note that the v6 address used on the raw socket is a combination of the plat subnet plus a host ID, and not this link-local address. Also fixup default values to match clatd.conf values. Change-Id: I3470a02e6798b65cae0f8d7f074ce51fb0647e6a --- clatd.conf | 3 --- config.c | 6 +----- config.h | 6 ++---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/clatd.conf b/clatd.conf index 0d4b79ef4a..b17b862227 100644 --- a/clatd.conf +++ b/clatd.conf @@ -5,9 +5,6 @@ ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 -# ipv6 extra link local address for the ip6 iface. -ipv6_local_address fe80::c000:0004 - # 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 diff --git a/config.c b/config.c index 13889a0f6c..623a1b0eaa 100644 --- a/config.c +++ b/config.c @@ -196,7 +196,7 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { 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)) + if(!config_item_ip6(root, "ipv6_host_id", "::464", &Global_Clatd_Config.ipv6_host_id)) return 0; interface_ip = getinterface_ip(interface, AF_INET6); @@ -252,9 +252,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address)) - 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); @@ -295,7 +292,6 @@ void dump_config() { 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_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer))); 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))); diff --git a/config.h b/config.h index fa47152055..da7446efdc 100644 --- a/config.h +++ b/config.h @@ -21,14 +21,12 @@ #include #include -#define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" -#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004" +#define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" -#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" +#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { int16_t mtu, ipv4mtu; - struct in6_addr ipv6_local_address; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; From 9808952bdf2fa6ce4cbd790d1967691bb95bf327 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 9 Oct 2014 22:29:45 +0900 Subject: [PATCH 094/261] Generate a random IID if one is not specified. - Add code to generate a random IPv6 address that's checksum-neutral with the NAT64 prefix and clat IPv4 address. - Only calculate the IP address after the NAT64 prefix is known. - Because the clat IPv6 address is no longer determinisitic, modify interface_poll so it checks whether the prefix has changed instead of checking whether the IPv6 address has changed. - Add/update unit tests. Change-Id: Ia53716ca5315ebdd0eaa3ad3a07552bf18e9dd5c --- Android.mk | 2 +- checksum.h | 4 + clatd.c | 8 +- clatd.conf | 7 +- clatd_test.cpp | 210 +++++++++++++++++++++++++++++++++++++++++++++++-- config.c | 58 ++++++++++++-- config.h | 1 + 7 files changed, 270 insertions(+), 20 deletions(-) diff --git a/Android.mk b/Android.mk index a640e06df8..b3b9fa3f0c 100644 --- a/Android.mk +++ b/Android.mk @@ -30,7 +30,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c +LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog diff --git a/checksum.h b/checksum.h index 619581011d..d0af88ed33 100644 --- a/checksum.h +++ b/checksum.h @@ -18,6 +18,10 @@ #ifndef __CHECKSUM_H__ #define __CHECKSUM_H__ +#include +#include +#include + 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); diff --git a/clatd.c b/clatd.c index dbf725b99c..4b6b8ccf73 100644 --- a/clatd.c +++ b/clatd.c @@ -164,16 +164,16 @@ void interface_poll(const struct tun_data *tunnel) { return; } - config_generate_local_ipv6_subnet(&interface_ip->ip6); + if(!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { + 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); + logmsg(ANDROID_LOG_WARN, "clat IPv6 address changed from %s to %s", from_addr, to_addr); // Start translating packets to the new prefix. - memcpy(&Global_Clatd_Config.ipv6_local_subnet, &interface_ip->ip6, sizeof(struct in6_addr)); + Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; // Update our packet socket filter to reflect the new 464xlat IP address. if (!configure_packet_socket(tunnel->read_fd6)) { diff --git a/clatd.conf b/clatd.conf index b17b862227..3805c6df94 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1,6 +1,7 @@ -# host ID to use as the source of CLAT traffic -# this is a /128 taken out of the /64 routed to the phone -ipv6_host_id ::464 +# Host IID to use as the source of CLAT traffic. +# This is a /128 taken out of the /64 on the parent interface. +# A host IID of :: means to generate a checksum-neutral, random IID. +ipv6_host_id :: # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 diff --git a/clatd_test.cpp b/clatd_test.cpp index b35bf70abe..171aecb3b7 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -370,7 +371,7 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu *reassembled_len = total_length; } -void check_data_matches(const uint8_t *expected, const uint8_t *actual, size_t len, const char *msg) { +void check_data_matches(const void *expected, const void *actual, size_t len, const char *msg) { if (memcmp(expected, actual, len)) { // Hex dump, 20 bytes per line, one space between bytes (1 byte = 3 chars), indented by 4. int hexdump_len = len * 3 + (len / 20 + 1) * 5; @@ -382,11 +383,11 @@ void check_data_matches(const uint8_t *expected, const uint8_t *actual, size_t l sprintf(actual_hexdump + pos, "\n "); pos += 4; } - sprintf(expected_hexdump + pos, " %02x", expected[i]); - sprintf(actual_hexdump + pos, " %02x", actual[i]); + sprintf(expected_hexdump + pos, " %02x", ((uint8_t *) expected)[i]); + sprintf(actual_hexdump + pos, " %02x", ((uint8_t *) actual)[i]); pos += 3; } - FAIL() << msg << ": Translated packet doesn't match" + FAIL() << msg << ": Data doesn't match" << "\n Expected:" << (char *) expected_hexdump << "\n Actual:" << (char *) actual_hexdump << "\n"; } @@ -460,6 +461,7 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * translate_packet(write_fd, (version == 4), original, original_len); + snprintf(foo, sizeof(foo), "%s: Invalid translated packet", msg); if (version == 6) { // Translating to IPv4. Expect a tun header. struct tun_pi new_tun_header; @@ -472,13 +474,15 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; *outlen = len - sizeof(new_tun_header); + check_packet(out, *outlen, msg); } else { - FAIL() << msg << ": Packet was not translated"; + FAIL() << msg << ": Packet was not translated: len=" << len; *outlen = 0; } } else { // Translating to IPv6. Expect raw packet. *outlen = read(read_fd, out, *outlen); + check_packet(out, *outlen, msg); } } @@ -514,6 +518,44 @@ void check_fragment_translation(const uint8_t *original[], const size_t original check_packet(translated, translated_len, msg); } +int get_transport_checksum(const uint8_t *packet) { + struct iphdr *ip; + struct ip6_hdr *ip6; + uint8_t protocol; + const void *payload; + + int version = ip_version(packet); + switch (version) { + case 4: + ip = (struct iphdr *) packet; + if (is_ipv4_fragment(ip)) { + return -1; + } + protocol = ip->protocol; + payload = ip + 1; + break; + case 6: + ip6 = (struct ip6_hdr *) packet; + protocol = ip6->ip6_nxt; + payload = ip6 + 1; + break; + default: + return -1; + } + + switch (protocol) { + case IPPROTO_UDP: + return ((struct udphdr *) payload)->check; + + case IPPROTO_TCP: + return ((struct tcphdr *) payload)->check; + + case IPPROTO_FRAGMENT: + default: + return -1; + } +} + struct clat_config Global_Clatd_Config; class ClatdTest : public ::testing::Test { @@ -522,10 +564,126 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + Global_Clatd_Config.ipv6_host_id = in6addr_any; } }; -TEST_F(ClatdTest, Sanitycheck) { +void expect_ipv6_addr_equal(struct in6_addr *expected, struct in6_addr *actual) { + if (!IN6_ARE_ADDR_EQUAL(expected, actual)) { + char expected_str[INET6_ADDRSTRLEN], actual_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, expected, expected_str, sizeof(expected_str)); + inet_ntop(AF_INET6, actual, actual_str, sizeof(actual_str)); + FAIL() + << "Unexpected IPv6 address:: " + << "\n Expected: " << expected_str + << "\n Actual: " << actual_str + << "\n"; + } +} + +TEST_F(ClatdTest, TestIPv6PrefixEqual) { + EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet, + &Global_Clatd_Config.plat_subnet)); + EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.plat_subnet, + &Global_Clatd_Config.ipv6_local_subnet)); + + struct in6_addr subnet2 = Global_Clatd_Config.ipv6_local_subnet; + EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2)); + EXPECT_TRUE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); + + subnet2.s6_addr[6] = 0xff; + EXPECT_FALSE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &subnet2)); + EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); +} + +int count_onebits(const void *data, size_t size) { + int onebits = 0; + for (size_t pos = 0; pos < size; pos++) { + uint8_t *byte = ((uint8_t*) data) + pos; + for (int shift = 0; shift < 8; shift++) { + onebits += (*byte >> shift) & 1; + } + } + return onebits; +} + +TEST_F(ClatdTest, TestCountOnebits) { + uint64_t i; + i = 1; + ASSERT_EQ(1, count_onebits(&i, sizeof(i))); + i <<= 61; + ASSERT_EQ(1, count_onebits(&i, sizeof(i))); + i |= ((uint64_t) 1 << 33); + ASSERT_EQ(2, count_onebits(&i, sizeof(i))); + i = 0xf1000202020000f0; + ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i))); +} + +TEST_F(ClatdTest, TestGenIIDConfigured) { + struct in6_addr myaddr, expected; + ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id)); + ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected)); + ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr)); + config_generate_local_ipv6_subnet(&myaddr); + expect_ipv6_addr_equal(&expected, &myaddr); +} + +TEST_F(ClatdTest, TestGenIIDRandom) { + struct in6_addr interface_ipv6; + ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &interface_ipv6)); + Global_Clatd_Config.ipv6_host_id = in6addr_any; + + // Generate a boatload of random IIDs. + int onebits = 0; + uint64_t prev_iid = 0; + for (int i = 0; i < 100000; i++) { + struct in6_addr myaddr = interface_ipv6; + + config_generate_local_ipv6_subnet(&myaddr); + + // Check the generated IP address is in the same prefix as the interface IPv6 address. + EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr)); + + // Check that consecutive IIDs are not the same. + uint64_t iid = * (uint64_t*) (&myaddr.s6_addr[8]); + ASSERT_TRUE(iid != prev_iid) + << "Two consecutive random IIDs are the same: " + << std::showbase << std::hex + << iid << "\n"; + prev_iid = iid; + + // Check that the IID is checksum-neutral with the NAT64 prefix and the + // local prefix. + struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; + struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet; + + uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr))); + uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + + ip_checksum_add(0, &myaddr, sizeof(myaddr))); + + if (c1 != c2) { + char myaddr_str[INET6_ADDRSTRLEN], plat_str[INET6_ADDRSTRLEN], ipv4_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &myaddr, myaddr_str, sizeof(myaddr_str)); + inet_ntop(AF_INET6, plat_subnet, plat_str, sizeof(plat_str)); + inet_ntop(AF_INET, ipv4addr, ipv4_str, sizeof(ipv4_str)); + FAIL() + << "Bad IID: " << myaddr_str + << " not checksum-neutral with " << ipv4_str << " and " << plat_str + << std::showbase << std::hex + << "\n IPv4 checksum: " << c1 + << "\n IPv6 checksum: " << c2 + << "\n"; + } + + // Check that IIDs are roughly random and use all the bits by counting the + // total number of bits set to 1 in a random sample of 100000 generated IIDs. + onebits += count_onebits(&iid, sizeof(iid)); + } + EXPECT_LE(3190000, onebits); + EXPECT_GE(3210000, onebits); +} + +TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks the data. uint8_t v4_header[] = { IPV4_UDP_HEADER }; ASSERT_EQ(sizeof(struct iphdr), sizeof(v4_header)) << "Test IPv4 header: incorrect length\n"; @@ -681,3 +839,43 @@ TEST_F(ClatdTest, Fragmentation) { kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); } + +void check_translate_checksum_neutral(const uint8_t *original, size_t original_len, + size_t expected_len, const char *msg) { + uint8_t translated[MAXMTU]; + size_t translated_len = sizeof(translated); + do_translate_packet(original, original_len, translated, &translated_len, msg); + EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n"; + // do_translate_packet already checks packets for validity and verifies the checksum. + int original_check = get_transport_checksum(original); + int translated_check = get_transport_checksum(translated); + ASSERT_NE(-1, original_check); + ASSERT_NE(-1, translated_check); + ASSERT_EQ(original_check, translated_check) + << "Not checksum neutral: original and translated checksums differ\n"; +} + +TEST_F(ClatdTest, TranslateChecksumNeutral) { + // Generate a random clat IPv6 address and check that translation is checksum-neutral. + Global_Clatd_Config.ipv6_host_id = in6addr_any; + ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", + &Global_Clatd_Config.ipv6_local_subnet)); + config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); + ASSERT_NE((uint32_t) 0x00000464, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + ASSERT_NE((uint32_t) 0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + + // Check that translating UDP packets is checksum-neutral. First, IPv4. + uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; + fix_udp_checksum(udp_ipv4); + check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20, + "UDP/IPv4 -> UDP/IPv6 checksum neutral"); + + // Now try IPv6. + uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; + // The test packet uses the static IID, not the random IID. Fix up the source address. + struct ip6_hdr *ip6 = (struct ip6_hdr *) udp_ipv6; + memcpy(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet, sizeof(ip6->ip6_src)); + fix_udp_checksum(udp_ipv6); + check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20, + "UDP/IPv4 -> UDP/IPv6 checksum neutral"); +} diff --git a/config.c b/config.c index 623a1b0eaa..09d3df0914 100644 --- a/config.c +++ b/config.c @@ -31,6 +31,7 @@ #include "logging.h" #include "getaddr.h" #include "clatd.h" +#include "checksum.h" struct clat_config Global_Clatd_Config; @@ -149,6 +150,16 @@ void free_config() { } } +/* function: ipv6_prefix_equal + * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. + * a1 - first address + * a2 - second address + * returns: 0 if the subnets are different, 1 if they are the same. + */ +int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { + return !memcmp(a1, a2, 8); +} + /* function: dns64_detection * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer * net_id - (optional) netId to use, NETID_UNSET indicates use of default network @@ -175,6 +186,28 @@ void dns64_detection(unsigned net_id) { } +void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, + struct in6_addr *plat_subnet) { + // Fill last 8 bytes of IPv6 address with random bits. + arc4random_buf(&myaddr->s6_addr[8], 8); + + // Make the IID checksum-neutral. That is, make it so that: + // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) + // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): + // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) + // Do this by adjusting the two bytes in the middle of the IID. + + uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; + + uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); + uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + + ip_checksum_add(0, myaddr, sizeof(*myaddr)); + + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + myaddr->s6_addr[11] = delta >> 8; + myaddr->s6_addr[12] = delta & 0xff; +} + /* function: config_generate_local_ipv6_subnet * generates the local ipv6 subnet when given the interface ip * requires config.ipv6_host_id @@ -183,8 +216,16 @@ void dns64_detection(unsigned net_id) { 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]; + if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { + /* Generate a random interface ID. */ + gen_random_iid(interface_ip, + &Global_Clatd_Config.ipv4_local_subnet, + &Global_Clatd_Config.plat_subnet); + } else { + /* Use the specified interface ID. */ + for(i = 2; i < 4; i++) { + interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i]; + } } } @@ -195,10 +236,12 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { */ int subnet_from_interface(cnode *root, const char *interface) { union anyip *interface_ip; + char addrstr[INET6_ADDRSTRLEN]; - if(!config_item_ip6(root, "ipv6_host_id", "::464", &Global_Clatd_Config.ipv6_host_id)) + if(!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) return 0; + // TODO: check that the prefix length is /64. interface_ip = getinterface_ip(interface, AF_INET6); if(!interface_ip) { logmsg(ANDROID_LOG_FATAL,"unable to find an ipv6 ip on interface %s",interface); @@ -210,6 +253,9 @@ int subnet_from_interface(cnode *root, const char *interface) { config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); + inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); + logmsg(ANDROID_LOG_INFO, "Using %s on %s", addrstr, interface); + return 1; } @@ -240,9 +286,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat 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; @@ -275,6 +318,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat } } + if(!subnet_from_interface(root,Global_Clatd_Config.default_pdp_interface)) + goto failed; + return 1; diff --git a/config.h b/config.h index da7446efdc..654e9099c6 100644 --- a/config.h +++ b/config.h @@ -40,5 +40,6 @@ extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, unsigned net_id); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); +int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); #endif /* __CONFIG_H__ */ From 7612916deb5f40a8dcf3a31db8849450e146fc8d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 20 Oct 2014 17:24:51 +0900 Subject: [PATCH 095/261] Support more than one clatd at a time. The only change necessary is to stop calling the clat interface "clat4" and instead name it based on the upstream interface (specifically, "v4-". Bug: 12111730 Change-Id: I489ebb0e3b1408ace8b64ca09ec20366fefe8d83 --- clatd.c | 17 +++++++++++------ clatd.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clatd.c b/clatd.c index 4b6b8ccf73..f5d625d41e 100644 --- a/clatd.c +++ b/clatd.c @@ -51,7 +51,7 @@ #include "getaddr.h" #include "dump.h" -#define DEVICENAME4 "clat4" +#define DEVICEPREFIX "v4-" /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 @@ -156,11 +156,11 @@ int configure_packet_socket(int sock) { */ void interface_poll(const struct tun_data *tunnel) { union anyip *interface_ip; + char *interface = Global_Clatd_Config.default_pdp_interface; - interface_ip = getinterface_ip(Global_Clatd_Config.default_pdp_interface, AF_INET6); + interface_ip = getinterface_ip(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); + logmsg(ANDROID_LOG_WARN,"unable to find an ipv6 ip on interface %s", interface); return; } @@ -441,8 +441,7 @@ int main(int argc, char **argv) { char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; - - strcpy(tunnel.device4, DEVICENAME4); + unsigned len; while((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { switch(opt) { @@ -482,6 +481,12 @@ int main(int argc, char **argv) { exit(1); } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); + if (len >= sizeof(tunnel.device4)) { + logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); + exit(1); + } + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)", diff --git a/clatd.h b/clatd.h index ca7369b27c..e7cef4303a 100644 --- a/clatd.h +++ b/clatd.h @@ -23,7 +23,7 @@ #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) -#define CLATD_VERSION "1.3" +#define CLATD_VERSION "1.4" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) From 1352a3a26c4d7c32b38b7fadb837799a23014aa6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 21 Oct 2014 13:41:21 +0900 Subject: [PATCH 096/261] Unduplicate IPv6 address setting code. Currently, the IPv6 address gets set in two different codepaths depending on whether it's being configured on startup or happens because the interface changed its prefix. Refactor the two into a common function. Change-Id: I37035401bef7a57ff40540bd0f2aed0f6863269d --- clatd.c | 95 ++++++++++++++++++++++++++++++++------------------------ config.c | 37 +++------------------- config.h | 3 +- 3 files changed, 59 insertions(+), 76 deletions(-) diff --git a/clatd.c b/clatd.c index f5d625d41e..78ccccc626 100644 --- a/clatd.c +++ b/clatd.c @@ -150,42 +150,6 @@ int configure_packet_socket(int sock) { return 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; - char *interface = Global_Clatd_Config.default_pdp_interface; - - interface_ip = getinterface_ip(interface, AF_INET6); - if(!interface_ip) { - logmsg(ANDROID_LOG_WARN,"unable to find an ipv6 ip on interface %s", interface); - return; - } - - if(!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { - config_generate_local_ipv6_subnet(&interface_ip->ip6); - - 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 IPv6 address changed from %s to %s", from_addr, to_addr); - - // Start translating packets to the new prefix. - Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; - - // Update our packet socket filter to reflect the new 464xlat IP address. - if (!configure_packet_socket(tunnel->read_fd6)) { - // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because configure_packet_socket has already done so. - exit(1); - } - } - - free(interface_ip); -} - /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface * tunnel - tun device data @@ -275,6 +239,58 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { tunnel->read_fd6 = packetsock; } +/* function: update_clat_ipv6_address + * picks the clat IPv6 address and configures packet translation to use it. + * tunnel - tun device data + * interface - uplink interface name + * returns: 1 on success, 0 on failure + */ +int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) { + union anyip *interface_ip; + char addrstr[INET6_ADDRSTRLEN]; + + // TODO: check that the prefix length is /64. + interface_ip = getinterface_ip(interface, AF_INET6); + if (!interface_ip) { + logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); + return 0; + } + + // If our prefix hasn't changed, do nothing. (If this is the first time we configure an IPv6 + // address, Global_Clatd_Config.ipv6_local_subnet will be ::, which won't match our new prefix.) + if (ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { + free(interface_ip); + return 1; + } + + // Generate an interface ID. + config_generate_local_ipv6_subnet(&interface_ip->ip6); + inet_ntop(AF_INET6, &interface_ip->ip6, addrstr, sizeof(addrstr)); + + if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)) { + // Startup. + logmsg(ANDROID_LOG_INFO, "Using %s on %s", addrstr, interface); + } else { + // Prefix change. + char from_addr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, from_addr, sizeof(from_addr)); + logmsg(ANDROID_LOG_INFO, "clat IPv6 address changed from %s to %s", from_addr, addrstr); + } + + // Start translating packets to the new prefix. + Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; + free(interface_ip); + + // Update our packet socket filter to reflect the new 464xlat IP address. + if (!configure_packet_socket(tunnel->read_fd6)) { + // Things aren't going to work. Bail out and hope we have better luck next time. + // We don't log an error here because configure_packet_socket has already done so. + exit(1); + } + + return 1; +} + /* function: configure_interface * reads the configuration and applies it to the interface * uplink_interface - network interface to use to reach the ipv6 internet @@ -404,7 +420,7 @@ void event_loop(const struct tun_data *tunnel) { time_t now = time(NULL); if(last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { - interface_poll(tunnel); + update_clat_ipv6_address(tunnel, Global_Clatd_Config.default_pdp_interface); last_interface_poll = now; } } @@ -511,10 +527,7 @@ int main(int argc, char **argv) { configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); - if (!configure_packet_socket(tunnel.read_fd6)) { - // We've already logged an error. - exit(1); - } + update_clat_ipv6_address(&tunnel, uplink_interface); // Loop until someone sends us a signal or brings down the tun interface. if(signal(SIGTERM, stop_loop) == SIG_ERR) { diff --git a/config.c b/config.c index 09d3df0914..4939478d9a 100644 --- a/config.c +++ b/config.c @@ -229,36 +229,6 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { } } -/* 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; - char addrstr[INET6_ADDRSTRLEN]; - - if(!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) - return 0; - - // TODO: check that the prefix length is /64. - 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); - - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); - logmsg(ANDROID_LOG_INFO, "Using %s on %s", addrstr, interface); - - 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 @@ -284,7 +254,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat goto failed; } - strncpy(Global_Clatd_Config.default_pdp_interface, uplink_interface, sizeof(Global_Clatd_Config.default_pdp_interface)); + Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); + if (!Global_Clatd_Config.default_pdp_interface) + goto failed; if(!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu)) goto failed; @@ -318,10 +290,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat } } - if(!subnet_from_interface(root,Global_Clatd_Config.default_pdp_interface)) + if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; - return 1; failed: diff --git a/config.h b/config.h index 654e9099c6..a56d6fc8d6 100644 --- a/config.h +++ b/config.h @@ -19,7 +19,6 @@ #define __CONFIG_H__ #include -#include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" @@ -31,7 +30,7 @@ struct clat_config { 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 *default_pdp_interface; char *plat_from_dns64_hostname; }; From 8a41a5d140b3cf56a54bdeef234e89ee12cba0dc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 21 Oct 2014 12:37:48 +0900 Subject: [PATCH 097/261] Support 464xlat on broadcast interfaces such as wifi. This works by generating a random IID and then using the IPV6_JOIN_ANYCAST socket option on the write-only raw socket to configure an address on the interface. Change-Id: Ieb885b7c54454988e2e4254a14b4213cba3bd791 --- clatd.c | 4 ++++ setif.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ setif.h | 3 +++ 3 files changed, 58 insertions(+) diff --git a/clatd.c b/clatd.c index 78ccccc626..41e961e4c7 100644 --- a/clatd.c +++ b/clatd.c @@ -275,10 +275,12 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac char from_addr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, from_addr, sizeof(from_addr)); logmsg(ANDROID_LOG_INFO, "clat IPv6 address changed from %s to %s", from_addr, addrstr); + del_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet); } // Start translating packets to the new prefix. Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; + add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface); free(interface_ip); // Update our packet socket filter to reflect the new 464xlat IP address. @@ -534,9 +536,11 @@ int main(int argc, char **argv) { logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); exit(1); } + event_loop(&tunnel); logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface); + del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); return 0; } diff --git a/setif.c b/setif.c index 5ee00bccff..359ed24f59 100644 --- a/setif.c +++ b/setif.c @@ -23,8 +23,11 @@ #include #include +#include "logging.h" #include "netlink_msg.h" +#define DEBUG_OPTNAME(a) case (a): { optname = #a; break; } + /* function: add_address * adds an IP address to/from an interface, returns 0 on success and <0 on failure * ifname - name of interface to change @@ -127,3 +130,51 @@ cleanup: return retval; } + +static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) { + struct ipv6_mreq mreq = { *addr, ifindex }; + char *optname; + int ret; + + switch (what) { + DEBUG_OPTNAME(IPV6_JOIN_ANYCAST) + DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST) + default: + optname = "???"; + break; + } + + ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq)); + if (ret) { + logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno)); + } + + return ret; +} + +/* function: add_anycast_address + * adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure + * sock - the socket to add the address to + * addr - the IP address to add + * ifname - name of interface to add the address to + */ +int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { + int ifindex, s, ret; + + ifindex = if_nametoindex(ifname); + if (!ifindex) { + logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname); + return -ENODEV; + } + + return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex); +} + +/* function: del_anycast_address + * removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure + * sock - the socket to remove from, must have had the address added via add_anycast_address + * addr - the IP address to remove + */ +int del_anycast_address(int sock, struct in6_addr *addr) { + return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0); +} diff --git a/setif.h b/setif.h index 7f83f73dee..d31eed522f 100644 --- a/setif.h +++ b/setif.h @@ -21,4 +21,7 @@ int add_address(const char *ifname, int family, const void *address, int cidr, const void *broadcast); int if_up(const char *ifname, int mtu); +int add_anycast_address(int sock, const struct in6_addr *addr, const char *interface); +int del_anycast_address(int sock, const struct in6_addr *addr); + #endif From 798f9934fca523dfb57136bd185cf6e9460323ad Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 31 Oct 2014 21:54:33 +0900 Subject: [PATCH 098/261] Use different IPv4 addresses on different clat interfaces. Previously the code would use 192.0.0.4 on all clat interfaces. This works, but it has the problem when a clat interface goes down, we do not reset TCP connections on its IP address if there is another clat interface up. Fix this by assigning every clat interface its own IP address in 192.0.0.0/29. Bug: 12111730 Change-Id: I28c831acd93b0980efea8e90c1cdf8b607beac68 --- clatd.c | 22 +++++++++++++++- clatd.conf | 8 +++++- clatd_test.cpp | 53 ++++++++++++++++++++++++++++++++++++++ config.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++-- config.h | 6 ++++- 5 files changed, 154 insertions(+), 5 deletions(-) diff --git a/clatd.c b/clatd.c index 41e961e4c7..3f0af0bc25 100644 --- a/clatd.c +++ b/clatd.c @@ -157,6 +157,22 @@ int configure_packet_socket(int sock) { void configure_tun_ip(const struct tun_data *tunnel) { int status; + // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically, + // there is a race here - if another clatd calls config_select_ipv4_address after we do, but + // before we call add_address, it can end up having the same IP address as we do. But the time + // window in which this can happen is extremely small, and even if we end up with a duplicate + // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go + // down. + in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet, + Global_Clatd_Config.ipv4_local_prefixlen); + if (localaddr == INADDR_NONE) { + logmsg(ANDROID_LOG_FATAL,"No free IPv4 address in %s/%d", + inet_ntoa(Global_Clatd_Config.ipv4_local_subnet), + Global_Clatd_Config.ipv4_local_prefixlen); + exit(1); + } + Global_Clatd_Config.ipv4_local_subnet.s_addr = localaddr; + // Configure the interface before bringing it up. As soon as we bring the interface up, the // framework will be notified and will assume the interface's configuration has been finalized. status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, @@ -166,6 +182,10 @@ void configure_tun_ip(const struct tun_data *tunnel) { exit(1); } + char addrstr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr)); + logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4); + if((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) { logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(4) failed: %s",strerror(-status)); exit(1); @@ -269,7 +289,7 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)) { // Startup. - logmsg(ANDROID_LOG_INFO, "Using %s on %s", addrstr, interface); + logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface); } else { // Prefix change. char from_addr[INET6_ADDRSTRLEN]; diff --git a/clatd.conf b/clatd.conf index 3805c6df94..ff80975ea6 100644 --- a/clatd.conf +++ b/clatd.conf @@ -3,8 +3,14 @@ # A host IID of :: means to generate a checksum-neutral, random IID. ipv6_host_id :: -# ipv4 subnet for the local traffic to use. This is a /32 host address +# IPv4 address configuration to use when selecting a host address. The first +# clat daemon started will use the address specified by ipv4_local_subnet. If +# more than one daemon is run at the same time, subsequent daemons will use +# other addresses in the prefix of length ipv4_local prefixlen that contains +# ipv4_local_subnet. The default is to use the IANA-assigned range 192.0.0.0/29, +# which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). ipv4_local_subnet 192.0.0.4 +ipv4_local_prefixlen 29 # get the plat_subnet from dns lookups (requires DNS64) plat_from_dns64 yes diff --git a/clatd_test.cpp b/clatd_test.cpp index 171aecb3b7..085a9b7d41 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -683,6 +683,59 @@ TEST_F(ClatdTest, TestGenIIDRandom) { EXPECT_GE(3210000, onebits); } +extern "C" addr_free_func config_is_ipv4_address_free; +int never_free(in_addr_t /* addr */) { return 0; } +int always_free(in_addr_t /* addr */) { return 1; } +int only2_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 2; } +int over6_free(in_addr_t addr) { return (ntohl(addr) & 0xff) >= 6; } +int only10_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 10; } + +TEST_F(ClatdTest, SelectIPv4Address) { + struct in_addr addr; + + inet_pton(AF_INET, kIPv4LocalAddr, &addr); + + addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; + + // If no addresses are free, return INADDR_NONE. + config_is_ipv4_address_free = never_free; + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 16)); + + // If the configured address is free, pick that. But a prefix that's too big is invalid. + config_is_ipv4_address_free = always_free; + EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 29)); + EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 20)); + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 15)); + + // A prefix length of 32 works, but anything above it is invalid. + EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 32)); + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 33)); + + // If another address is free, pick it. + config_is_ipv4_address_free = over6_free; + EXPECT_EQ(inet_addr("192.0.0.6"), config_select_ipv4_address(&addr, 29)); + + // Check that we wrap around to addresses that are lower than the first address. + config_is_ipv4_address_free = only2_free; + EXPECT_EQ(inet_addr("192.0.0.2"), config_select_ipv4_address(&addr, 29)); + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 30)); + + // If a free address exists outside the prefix, we don't pick it. + config_is_ipv4_address_free = only10_free; + EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); + EXPECT_EQ(inet_addr("192.0.0.10"), config_select_ipv4_address(&addr, 24)); + + // Now try using the real function which sees if IP addresses are free using bind(). + // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. + config_is_ipv4_address_free = orig_config_is_ipv4_address_free; + addr.s_addr = inet_addr("8.8.8.8"); + EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29)); + + addr.s_addr = inet_addr("127.0.0.1"); + EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); +} + TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks the data. uint8_t v4_header[] = { IPV4_UDP_HEADER }; diff --git a/config.c b/config.c index 4939478d9a..de8a26f93e 100644 --- a/config.c +++ b/config.c @@ -185,7 +185,12 @@ void dns64_detection(unsigned net_id) { } } - +/* function: gen_random_iid + * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix + * myaddr - IPv6 address to write to + * ipv4_local_subnet - clat IPv4 address + * plat_subnet - NAT64 prefix + */ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, struct in6_addr *plat_subnet) { // Fill last 8 bytes of IPv6 address with random bits. @@ -208,6 +213,61 @@ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, myaddr->s6_addr[12] = delta & 0xff; } +// Factored out to a separate function for testability. +int connect_is_ipv4_address_free(in_addr_t addr) { + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) { + return 0; + } + + // Attempt to connect to the address. If the connection succeeds and getsockname returns the same + // the address then the address is already assigned to the system and we can't use it. + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 }; + socklen_t len = sizeof(sin); + int inuse = connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0 && + getsockname(s, (struct sockaddr *) &sin, &len) == 0 && + (size_t) len >= sizeof(sin) && + sin.sin_addr.s_addr == addr; + + close(s); + return !inuse; +} + +addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free; + +/* function: config_select_ipv4_address + * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order + * ip - the IP address from the configuration file + * prefixlen - the length of the prefix from which addresses may be selected. + * returns: the IPv4 address, or INADDR_NONE if no addresses were available + */ +in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) { + in_addr_t chosen = INADDR_NONE; + + // Don't accept prefixes that are too large because we scan addresses one by one. + if (prefixlen < 16 || prefixlen > 32) { + return chosen; + } + + // All these are in host byte order. + in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); + in_addr_t ipv4 = ntohl(ip->s_addr); + in_addr_t first_ipv4 = ipv4; + in_addr_t prefix = ipv4 & mask; + + // Pick the first IPv4 address in the pool, wrapping around if necessary. + // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0. + do { + if (config_is_ipv4_address_free(htonl(ipv4))) { + chosen = htonl(ipv4); + break; + } + ipv4 = prefix | ((ipv4 + 1) & ~mask); + } while (ipv4 != first_ipv4); + + return chosen; +} + /* function: config_generate_local_ipv6_subnet * generates the local ipv6 subnet when given the interface ip * requires config.ipv6_host_id @@ -264,7 +324,12 @@ int read_config(const char *file, const char *uplink_interface, const char *plat 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)) + if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, + &Global_Clatd_Config.ipv4_local_subnet)) + goto failed; + + if(!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, + &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; if(plat_prefix) { // plat subnet is coming from the command line @@ -311,6 +376,7 @@ void dump_config() { logmsg(ANDROID_LOG_DEBUG,"ipv4mtu = %d",Global_Clatd_Config.ipv4mtu); logmsg(ANDROID_LOG_DEBUG,"ipv6_local_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); logmsg(ANDROID_LOG_DEBUG,"ipv4_local_subnet = %s",inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); + logmsg(ANDROID_LOG_DEBUG,"ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); logmsg(ANDROID_LOG_DEBUG,"plat_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); logmsg(ANDROID_LOG_DEBUG,"default_pdp_interface = %s",Global_Clatd_Config.default_pdp_interface); } diff --git a/config.h b/config.h index a56d6fc8d6..05b3a5a2ea 100644 --- a/config.h +++ b/config.h @@ -21,7 +21,7 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" - +#define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" #define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { @@ -29,6 +29,7 @@ struct clat_config { struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; + int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; char *default_pdp_interface; char *plat_from_dns64_hostname; @@ -39,6 +40,9 @@ extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, unsigned net_id); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); +in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); +typedef int (*addr_free_func)(in_addr_t addr); + #endif /* __CONFIG_H__ */ From 2596f42a4f4c42bfb96ccbfc2700dbf70502ec75 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 10 Nov 2014 17:00:02 -0800 Subject: [PATCH 099/261] Never use a statically-configured IID on wifi. Bug: 12111730 Change-Id: I062d81f2b65e6e89baae0f840cbd673f8051d523 --- Android.mk | 2 +- clatd_test.cpp | 6 ++++++ config.c | 15 ++++++++++++++- config.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index b3b9fa3f0c..988d92510b 100644 --- a/Android.mk +++ b/Android.mk @@ -6,7 +6,7 @@ LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c conf LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl -LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils # The clat daemon. LOCAL_MODULE := clatd diff --git a/clatd_test.cpp b/clatd_test.cpp index 085a9b7d41..fe52c21eeb 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -565,6 +565,7 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); Global_Clatd_Config.ipv6_host_id = in6addr_any; + Global_Clatd_Config.use_dynamic_iid = 1; } }; @@ -621,11 +622,16 @@ TEST_F(ClatdTest, TestCountOnebits) { TEST_F(ClatdTest, TestGenIIDConfigured) { struct in6_addr myaddr, expected; + Global_Clatd_Config.use_dynamic_iid = 0; ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id)); ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected)); ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr)); config_generate_local_ipv6_subnet(&myaddr); expect_ipv6_addr_equal(&expected, &myaddr); + + Global_Clatd_Config.use_dynamic_iid = 1; + config_generate_local_ipv6_subnet(&myaddr); + EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&expected, &myaddr)); } TEST_F(ClatdTest, TestGenIIDRandom) { diff --git a/config.c b/config.c index de8a26f93e..b147868c5f 100644 --- a/config.c +++ b/config.c @@ -25,6 +25,7 @@ #include #include +#include #include "config.h" #include "dns64.h" @@ -276,7 +277,7 @@ in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { int i; - if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { + if (Global_Clatd_Config.use_dynamic_iid) { /* Generate a random interface ID. */ gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet, @@ -300,6 +301,7 @@ int read_config(const char *file, const char *uplink_interface, const char *plat unsigned net_id) { cnode *root = config_node("", ""); void *tmp_ptr = NULL; + unsigned flags; if(!root) { logmsg(ANDROID_LOG_FATAL,"out of memory"); @@ -358,6 +360,17 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; + /* In order to prevent multiple devices attempting to use the same clat address, never use a + statically-configured interface ID on a broadcast interface such as wifi. */ + if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { + ifc_init(); + ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags); + ifc_close(); + Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0; + } else { + Global_Clatd_Config.use_dynamic_iid = 1; + } + return 1; failed: diff --git a/config.h b/config.h index 05b3a5a2ea..b6d9ae158c 100644 --- a/config.h +++ b/config.h @@ -33,6 +33,7 @@ struct clat_config { struct in6_addr plat_subnet; char *default_pdp_interface; char *plat_from_dns64_hostname; + int use_dynamic_iid; }; extern struct clat_config Global_Clatd_Config; From fee9afb1a5d9f7f5df0c79e29368c61d7da16fb8 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 24 Nov 2014 18:43:39 -0800 Subject: [PATCH 100/261] Android-Clatd: Remove unused variable For build-system CFLAGS clean-up, fix unused-but-set variables. Change-Id: I3fc6b0d9bec87cd54d7b5199108cff576b4be677 --- clatd_test.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 9a09b48a15..b61da9f65f 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -431,7 +431,6 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { abort(); } - struct tun_pi tun_header = { 0, 0 }; char foo[512]; snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); @@ -442,13 +441,11 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * int version = ip_version(original); switch (version) { case 4: - tun_header.proto = htons(ETH_P_IP); expected_proto = htons(ETH_P_IPV6); read_fd = fds[1]; write_fd = fds[0]; break; case 6: - tun_header.proto = htons(ETH_P_IPV6); expected_proto = htons(ETH_P_IP); read_fd = fds[0]; write_fd = fds[1]; From ff6f7fe4bd983c623d490100fa17cdb4936b95a2 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 11:27:41 +0900 Subject: [PATCH 101/261] Move tun functions to a new tun.c. Change-Id: I4910b6cfca0bbf6bde2d1348f43d3131a1212399 --- Android.mk | 2 +- clatd.c | 39 +--------------------------------- clatd.h | 8 ------- tun.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tun.h | 30 ++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 47 deletions(-) create mode 100644 tun.c create mode 100644 tun.h diff --git a/Android.mk b/Android.mk index a640e06df8..819c86fcc3 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 icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.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 netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index dbf725b99c..e2c96e6009 100644 --- a/clatd.c +++ b/clatd.c @@ -50,6 +50,7 @@ #include "mtu.h" #include "getaddr.h" #include "dump.h" +#include "tun.h" #define DEVICENAME4 "clat4" @@ -65,44 +66,6 @@ void stop_loop() { 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: configure_packet_socket * Binds the packet socket and attaches the receive filter to it. * sock - the socket to configure diff --git a/clatd.h b/clatd.h index ca7369b27c..6368a09979 100644 --- a/clatd.h +++ b/clatd.h @@ -18,9 +18,6 @@ #ifndef __CLATD_H__ #define __CLATD_H__ -#include -#include - #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) #define CLATD_VERSION "1.3" @@ -33,9 +30,4 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 -struct tun_data { - char device4[IFNAMSIZ]; - int read_fd6, write_fd6, fd4; -}; - #endif /* __CLATD_H__ */ diff --git a/tun.c b/tun.c new file mode 100644 index 0000000000..ef6b111f51 --- /dev/null +++ b/tun.c @@ -0,0 +1,62 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * tun.c - tun device functions + */ +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/tun.h b/tun.h new file mode 100644 index 0000000000..9ccb037380 --- /dev/null +++ b/tun.h @@ -0,0 +1,30 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * tun.h - tun device functions + */ +#ifndef __TUN_H__ +#define __TUN_H__ + +struct tun_data { + char device4[IFNAMSIZ]; + int read_fd6, write_fd6, fd4; +}; + +int set_nonblocking(int fd); +int tun_open(); +int tun_alloc(char *dev, int fd); + +#endif From 6b2007aacd13344c9bc73d5d858bd903b432c228 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 12:53:05 +0900 Subject: [PATCH 102/261] Move send_tun into tun.c as well. Also remove a redundant include in checksum.c. Change-Id: I0b8858343b1496f22904d3b316b6c435be0f648a --- Android.mk | 2 +- checksum.c | 1 - clatd.h | 13 +++++++++++++ translate.c | 6 +----- translate.h | 11 ----------- tun.c | 7 +++++++ tun.h | 5 +++++ 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Android.mk b/Android.mk index 819c86fcc3..bdb9317238 100644 --- a/Android.mk +++ b/Android.mk @@ -30,7 +30,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c +LOCAL_SRC_FILES := clatd_test.cpp dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c tun.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog diff --git a/checksum.c b/checksum.c index 3dd1e00bef..23a7c02fb1 100644 --- a/checksum.c +++ b/checksum.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "checksum.h" diff --git a/clatd.h b/clatd.h index 6368a09979..ca7ad7407a 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,8 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include + #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) #define CLATD_VERSION "1.3" @@ -30,4 +32,15 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 +// A clat_packet is an array of iovec structures representing a packet that we are translating. +// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain +// specific parts of the packet. The packet_* functions operate on all the packet segments past a +// given position. +typedef enum { + CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX +} clat_packet_index; +typedef struct iovec clat_packet[CLAT_POS_MAX]; + #endif /* __CLATD_H__ */ diff --git a/translate.c b/translate.c index 487468b9cb..ddc9bac2d6 100644 --- a/translate.c +++ b/translate.c @@ -16,7 +16,6 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include -#include #include "icmp.h" #include "translate.h" @@ -25,6 +24,7 @@ #include "config.h" #include "logging.h" #include "debug.h" +#include "tun.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos @@ -465,10 +465,6 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t return CLAT_POS_PAYLOAD + 1; } -void send_tun(int fd, clat_packet out, int iov_len) { - writev(fd, out, iov_len); -} - // Weak symbol so we can override it in the unit test. void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak)); diff --git a/translate.h b/translate.h index 46e178b21b..aa8b736397 100644 --- a/translate.h +++ b/translate.h @@ -32,17 +32,6 @@ #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. -// A clat_packet is an array of iovec structures representing a packet that we are translating. -// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain -// specific parts of the packet. The packet_* functions operate on all the packet segments past a -// given position. -typedef enum { - CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX -} clat_packet_index; -typedef struct iovec clat_packet[CLAT_POS_MAX]; - // Calculates the checksum over all the packet components starting from pos. uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos); diff --git a/tun.c b/tun.c index ef6b111f51..a1b7254bcc 100644 --- a/tun.c +++ b/tun.c @@ -22,6 +22,9 @@ #include #include #include +#include + +#include "clatd.h" /* function: tun_open * tries to open the tunnel device @@ -60,3 +63,7 @@ int tun_alloc(char *dev, int fd) { strcpy(dev, ifr.ifr_name); return 0; } + +void send_tun(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} diff --git a/tun.h b/tun.h index 9ccb037380..771814d787 100644 --- a/tun.h +++ b/tun.h @@ -18,6 +18,10 @@ #ifndef __TUN_H__ #define __TUN_H__ +#include + +#include "clatd.h" + struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; @@ -26,5 +30,6 @@ struct tun_data { int set_nonblocking(int fd); int tun_open(); int tun_alloc(char *dev, int fd); +void send_tun(int fd, clat_packet out, int iov_len); #endif From b20719ebf403b16d36a231aeef96607f8c7aa252 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 10:51:32 +0900 Subject: [PATCH 103/261] Add a microbenchmark for tun write performance. Change-Id: Id73888d25afd9253634356436c57afec630a0846 --- Android.mk | 10 ++ clatd_microbenchmark.c | 214 +++++++++++++++++++++++++++++++++++++++++ tun.c | 24 ++++- tun.h | 4 +- 4 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 clatd_microbenchmark.c diff --git a/Android.mk b/Android.mk index bdb9317238..5079082557 100644 --- a/Android.mk +++ b/Android.mk @@ -35,3 +35,13 @@ LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_NATIVE_TEST) + +# Microbenchmark. +include $(CLEAR_VARS) + +LOCAL_MODULE := clatd_microbenchmark +LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +LOCAL_SRC_FILES := clatd_microbenchmark.c checksum.c tun.c +LOCAL_MODULE_TAGS := eng tests + +include $(BUILD_NATIVE_TEST) diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c new file mode 100644 index 0000000000..fed310066a --- /dev/null +++ b/clatd_microbenchmark.c @@ -0,0 +1,214 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * clatd_microbenchmark.c - micro-benchmark for clatd tun send path + * + * Run with: + * + * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark + * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checksum.h" +#include "tun.h" + +#define DEVICENAME "clat4" + +#define PORT 51339 +#define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr)) +#define NUMPACKETS 1000000 +#define SEC_TO_NANOSEC (1000 * 1000 * 1000) + +void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) { + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); +} + +void die(const char *str) { + perror(str); + exit(1); +} + +int setup_tun() { + int fd = tun_open(); + if (fd == -1) die("tun_open"); + + char dev[IFNAMSIZ] = DEVICENAME; + int ret = tun_alloc(dev, fd); + if (ret == -1) die("tun_alloc"); + struct ifreq ifr = { + .ifr_name = DEVICENAME, + }; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "192.0.0.4"); + if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); + init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "255.255.255.248"); + if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); + return fd; +} + +int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) { + struct tun_pi tun = { 0, htons(ETH_P_IP) }; + struct udphdr udp = { + .source = htons(1234), + .dest = htons(PORT), + .len = htons(len + sizeof(udp)), + .check = 0, + }; + struct iphdr ip = { + .version = 4, + .ihl = 5, + .tot_len = htons(len + sizeof(ip) + sizeof(udp)), + .frag_off = htons(IP_DF), + .ttl = 55, + .protocol = IPPROTO_UDP, + .saddr = htonl(0xc0000006), // 192.0.0.6 + .daddr = htonl(0xc0000004), // 192.0.0.4 + }; + clat_packet out = { + { &tun, sizeof(tun) }, // tun header + { &ip, sizeof(ip) }, // IP header + { NULL, 0 }, // Fragment header + { &udp, sizeof(udp) }, // Transport header + { NULL, 0 }, // ICMP error IP header + { NULL, 0 }, // ICMP error fragment header + { NULL, 0 }, // ICMP error transport header + { payload, len }, // Payload + }; + + ip.check = ip_checksum(&ip, sizeof(ip)); + + uint32_t sum; + sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); + sum = ip_checksum_add(sum, &udp, sizeof(udp)); + sum += payload_checksum; + udp.check = ip_checksum_finish(sum); + + return send_tun(fd, out, sizeof(out) / sizeof(out[0])); +} + +double timedelta(const struct timespec tv1, const struct timespec tv2) { + struct timespec end = tv2; + if (end.tv_nsec < tv1.tv_nsec) { + end.tv_sec -= 1; + end.tv_nsec += SEC_TO_NANOSEC; + } + double seconds = (end.tv_sec - tv1.tv_sec); + seconds += (((double) (end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); + return seconds; +} + +void benchmark(const char *name, int fd, int s, int num, int do_read, + uint8_t payload[], int len, uint32_t payload_sum) { + int i; + char buf[4096]; + struct timespec tv1, tv2; + int write_err = 0, read_err = 0; + clock_gettime(CLOCK_MONOTONIC, &tv1); + for (i = 0; i < num; i++) { + if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; + if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { + read_err++; + if (errno == ETIMEDOUT) { + printf("Timed out after %d packets!\n", i); + break; + } + } + } + clock_gettime(CLOCK_MONOTONIC, &tv2); + double seconds = timedelta(tv1, tv2); + int pps = (int) (i / seconds); + double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); + printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); + printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", + read_err, (float) read_err / i * 100, + write_err, (float) write_err / i * 100); +} + +int open_socket() { + int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); + + struct timeval tv = { 1, 0 }; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = ntohs(PORT), + .sin_addr = { INADDR_ANY } + }; + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) die ("bind"); + + return sock; +} + +int main() { + int fd = setup_tun(); + int sock = open_socket(); + + int i; + uint8_t payload[PAYLOADSIZE]; + for (i = 0; i < (int) sizeof(payload); i++) { + payload[i] = (uint8_t) i; + } + uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); + + // Check things are working. + char buf[4096]; + if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); + if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); + + benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); + + fd = setup_tun(); + set_nonblocking(fd); + benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); + close(fd); + + fd = setup_tun(); + set_nonblocking(fd); + benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); + + return 0; +} diff --git a/tun.c b/tun.c index a1b7254bcc..49f0ea782c 100644 --- a/tun.c +++ b/tun.c @@ -64,6 +64,26 @@ int tun_alloc(char *dev, int fd) { return 0; } -void send_tun(int fd, clat_packet out, int iov_len) { - writev(fd, out, iov_len); +/* function: set_nonblocking + * sets a filedescriptor to non-blocking mode + * fd - the filedescriptor + * returns: 0 on success, -1 on failure + */ +int set_nonblocking(int fd) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return flags; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +/* function: send_tun + * sends a clat_packet to a tun interface + * fd - the tun filedescriptor + * out - the packet to send + * iov_len - the number of entries in the clat_packet + * returns: number of bytes read on success, -1 on failure + */ +int send_tun(int fd, clat_packet out, int iov_len) { + return writev(fd, out, iov_len); } diff --git a/tun.h b/tun.h index 771814d787..946ab47fc8 100644 --- a/tun.h +++ b/tun.h @@ -27,9 +27,9 @@ struct tun_data { int read_fd6, write_fd6, fd4; }; -int set_nonblocking(int fd); int tun_open(); int tun_alloc(char *dev, int fd); -void send_tun(int fd, clat_packet out, int iov_len); +int send_tun(int fd, clat_packet out, int iov_len); +int set_nonblocking(int fd); #endif From cfbb4ac47fb065c888b63b03a5697d24c9c5952e Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Mon, 8 Dec 2014 16:30:50 -0800 Subject: [PATCH 104/261] Add missing include for IFF_BROADCAST to fix build Change-Id: Icc033a8b2ce4a08565aabe2b58ba81ec90604c4f --- config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.h b/config.h index b6d9ae158c..e31a81d1e2 100644 --- a/config.h +++ b/config.h @@ -19,6 +19,7 @@ #define __CONFIG_H__ #include +#include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" From 418109eb96b485cdc009ba88deb9f60f9dca06d3 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Mon, 8 Dec 2014 16:30:50 -0800 Subject: [PATCH 105/261] Add missing include for IFF_BROADCAST to fix build (cherry picked from commit cfbb4ac47fb065c888b63b03a5697d24c9c5952e) Change-Id: Ib8ae2275f412f0db51e7a68e34304b11ea23d4fe --- config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.h b/config.h index b6d9ae158c..e31a81d1e2 100644 --- a/config.h +++ b/config.h @@ -19,6 +19,7 @@ #define __CONFIG_H__ #include +#include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" From 2008e147e5c58ac6f81bc2bac87fbc1313d5a38c Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Thu, 23 Oct 2014 09:38:19 -0700 Subject: [PATCH 106/261] Cleanup: Remove unused "ipv6_local_address". This was originally used on the "clat" interface. That interface is not created anymore (since the switch to using a raw socket). Note that the v6 address used on the raw socket is a combination of the plat subnet plus a host ID, and not this link-local address. Also fixup default values to match clatd.conf values. (cherry picked from commit c0a8ce0d1ddcd3196c213e6df595955835a6942e) Change-Id: I3470a02e6798b65cae0f8d7f074ce51fb0647e6a --- clatd.conf | 3 --- config.c | 6 +----- config.h | 6 ++---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/clatd.conf b/clatd.conf index 0d4b79ef4a..b17b862227 100644 --- a/clatd.conf +++ b/clatd.conf @@ -5,9 +5,6 @@ ipv6_host_id ::464 # ipv4 subnet for the local traffic to use. This is a /32 host address ipv4_local_subnet 192.0.0.4 -# ipv6 extra link local address for the ip6 iface. -ipv6_local_address fe80::c000:0004 - # 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 diff --git a/config.c b/config.c index 13889a0f6c..623a1b0eaa 100644 --- a/config.c +++ b/config.c @@ -196,7 +196,7 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { 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)) + if(!config_item_ip6(root, "ipv6_host_id", "::464", &Global_Clatd_Config.ipv6_host_id)) return 0; interface_ip = getinterface_ip(interface, AF_INET6); @@ -252,9 +252,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address)) - 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); @@ -295,7 +292,6 @@ void dump_config() { 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_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer))); 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))); diff --git a/config.h b/config.h index fa47152055..da7446efdc 100644 --- a/config.h +++ b/config.h @@ -21,14 +21,12 @@ #include #include -#define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1" -#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004" +#define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" -#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com" +#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { int16_t mtu, ipv4mtu; - struct in6_addr ipv6_local_address; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; From 9353be2a5f1b4fd00b04e4c826f7f3c3ec6c5d46 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 3 Dec 2014 15:18:29 +0900 Subject: [PATCH 107/261] Switch the receive path to memory-mapped I/O with PACKET_RX_RING. Change-Id: I93231de8f108db782eaa43aceb6cc5281d3739c4 --- Android.mk | 2 +- clatd.c | 85 ++++++++++++++++-------------------- ring.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ring.h | 55 +++++++++++++++++++++++ tun.h | 2 + 5 files changed, 221 insertions(+), 49 deletions(-) create mode 100644 ring.c create mode 100644 ring.h diff --git a/Android.mk b/Android.mk index 5079082557..5a35e22469 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 icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c +LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index e2c96e6009..5cbcc1e641 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,7 @@ #include "getaddr.h" #include "dump.h" #include "tun.h" +#include "ring.h" #define DEVICENAME4 "clat4" @@ -229,13 +230,10 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { tunnel->write_fd6 = rawsock; - int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); - if (packetsock < 0) { - logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + tunnel->read_fd6 = ring_create(tunnel); + if (tunnel->read_fd6 < 0) { exit(1); } - - tunnel->read_fd6 = packetsock; } /* function: configure_interface @@ -282,16 +280,16 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, } /* 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 + * reads a packet from the tunnel fd and translates it + * read_fd - file descriptor to read original packet from + * write_fd - file descriptor to write translated packet to + * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 */ -void read_packet(int active_fd, const struct tun_data *tunnel) { +void read_packet(int read_fd, int write_fd, int to_ipv6) { ssize_t readlen; uint8_t buf[PACKETLEN], *packet; - int fd; - readlen = read(active_fd, buf, PACKETLEN); + readlen = read(read_fd, buf, PACKETLEN); if(readlen < 0) { logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); @@ -302,41 +300,32 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - if (active_fd == tunnel->fd4) { - 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; - } - - struct tun_pi *tun_header = (struct tun_pi *) buf; - uint16_t proto = ntohs(tun_header->proto); - if (proto != ETH_P_IP) { - logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); - return; - } - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); - } - - fd = tunnel->write_fd6; - packet = buf + header_size; - readlen -= header_size; - } else { - fd = tunnel->fd4; - packet = buf; + struct tun_pi *tun_header = (struct tun_pi *) buf; + if (readlen < (ssize_t) sizeof(*tun_header)) { + logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen); + return; } - translate_packet(fd, (fd == tunnel->write_fd6), packet, readlen); + uint16_t proto = ntohs(tun_header->proto); + if (proto != ETH_P_IP) { + logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); + return; + } + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + packet = (uint8_t *) (tun_header + 1); + readlen -= sizeof(*tun_header); + translate_packet(write_fd, to_ipv6, packet, readlen); } /* function: event_loop * reads packets from the tun network interface and passes them down the stack * tunnel - tun device data */ -void event_loop(const struct tun_data *tunnel) { +void event_loop(struct tun_data *tunnel) { time_t last_interface_poll; struct pollfd wait_fd[] = { { tunnel->read_fd6, POLLIN, 0 }, @@ -352,16 +341,16 @@ void event_loop(const struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno)); } } else { - size_t i; - for(i = 0; i < ARRAY_SIZE(wait_fd); i++) { - // Call read_packet if the socket has data to be read, but also if an - // error is waiting. If we don't call read() after getting POLLERR, a - // subsequent poll() will return immediately with POLLERR again, - // causing this code to spin in a loop. Calling read() will clear the - // socket error flag instead. - if(wait_fd[i].revents != 0) { - read_packet(wait_fd[i].fd,tunnel); - } + // Call read_packet if the socket has data to be read, but also if an + // error is waiting. If we don't call read() after getting POLLERR, a + // subsequent poll() will return immediately with POLLERR again, + // causing this code to spin in a loop. Calling read() will clear the + // socket error flag instead. + if (wait_fd[0].revents) { + ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); + } + if (wait_fd[1].revents) { + read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); } } diff --git a/ring.c b/ring.c new file mode 100644 index 0000000000..5e99fd5f5d --- /dev/null +++ b/ring.c @@ -0,0 +1,126 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ring.c - packet ring buffer functions + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "ring.h" +#include "translate.h" +#include "tun.h" + +int ring_create(struct tun_data *tunnel) { + int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + if (packetsock < 0) { + logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + return -1; + } + + int ver = TPACKET_V2; + if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *) &ver, sizeof(ver))) { + logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno)); + return -1; + } + + int on = 1; + if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *) &on, sizeof(on))) { + logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno)); + } + + struct packet_ring *ring = &tunnel->ring; + ring->numblocks = TP_NUM_BLOCKS; + + int total_frames = TP_FRAMES * ring->numblocks; + + struct tpacket_req req = { + .tp_frame_size = TP_FRAME_SIZE, // Frame size. + .tp_block_size = TP_BLOCK_SIZE, // Frames per block. + .tp_block_nr = ring->numblocks, // Number of blocks. + .tp_frame_nr = total_frames, // Total frames. + }; + + if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { + logmsg(ANDROID_LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno)); + return -1; + } + + size_t buflen = TP_BLOCK_SIZE * ring->numblocks; + ring->base = mmap(NULL, buflen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED|MAP_POPULATE, + packetsock, 0); + if (ring->base == MAP_FAILED) { + logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno)); + return -1; + } + + ring->block = 0; + ring->slot = 0; + ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE; + ring->next = (struct tpacket2_hdr *) ring->base; + + logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", + total_frames, buflen, ring->base); + + return packetsock; +} + +/* function: ring_advance + * advances to the next position in the packet ring + * ring - packet ring buffer + */ +static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) { + uint8_t *next = (uint8_t *) ring->next; + + ring->slot++; + next += TP_FRAME_SIZE; + + if (ring->slot == ring->numslots) { + ring->slot = 0; + ring->block++; + + if (ring->block < ring->numblocks) { + next += TP_FRAME_GAP; + } else { + ring->block = 0; + next = (uint8_t *) ring->base; + } + } + + ring->next = (struct tpacket2_hdr *) next; + return ring->next; +} + +/* function: ring_read + * reads a packet from the ring buffer and translates it + * read_fd - file descriptor to read original packet from + * write_fd - file descriptor to write translated packet to + * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 + */ +void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) { + struct tpacket2_hdr *tp = ring->next; + if (tp->tp_status & TP_STATUS_USER) { + uint8_t *packet = ((uint8_t *) tp) + tp->tp_net; + translate_packet(write_fd, to_ipv6, packet, tp->tp_len); + tp->tp_status = TP_STATUS_KERNEL; + tp = ring_advance(ring); + } +} diff --git a/ring.h b/ring.h new file mode 100644 index 0000000000..b9b8c11d2e --- /dev/null +++ b/ring.h @@ -0,0 +1,55 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ring.c - packet ring buffer functions + */ +#ifndef __RING_H__ +#define __RING_H__ + +#include +#include + +#include "clatd.h" + +struct tun_data; + +// Frame size. Must be a multiple of TPACKET_ALIGNMENT (=16) +// Why the 16? http://lxr.free-electrons.com/source/net/packet/af_packet.c?v=3.4#L1764 +#define TP_FRAME_SIZE (TPACKET_ALIGN(MAXMTU) + TPACKET_ALIGN(TPACKET2_HDRLEN) + 16) + +// Block size. Must be a multiple of the page size, and a power of two for efficient memory use. +#define TP_BLOCK_SIZE 65536 + +// In order to save memory, our frames are not an exact divider of the block size. Therefore, the +// mmaped region will have gaps corresponding to the empty space at the end of each block. +#define TP_FRAMES (TP_BLOCK_SIZE / TP_FRAME_SIZE) +#define TP_FRAME_GAP (TP_BLOCK_SIZE % TP_FRAME_SIZE) + +// TODO: Make this configurable. This requires some refactoring because the packet socket is +// opened before we drop privileges, but the configuration file is read after. A value of 16 +// results in 656 frames (1048576 bytes). +#define TP_NUM_BLOCKS 16 + +struct packet_ring { + uint8_t *base; + struct tpacket2_hdr *next; + int slot, numslots; + int block, numblocks; +}; + +int ring_create(struct tun_data *tunnel); +void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6); + +#endif diff --git a/tun.h b/tun.h index 946ab47fc8..bcdd10e3b0 100644 --- a/tun.h +++ b/tun.h @@ -21,10 +21,12 @@ #include #include "clatd.h" +#include "ring.h" struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; + struct packet_ring ring; }; int tun_open(); From 49454814a0a714189916cb2aea1922bc8784b13b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 31 Jan 2015 19:18:47 +0900 Subject: [PATCH 108/261] Make the raw socket and the tun fd nonblocking. Change-Id: I29a3d510ba0b766488bbd66d57829d9aaf70d21d --- clatd.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clatd.c b/clatd.c index 5cbcc1e641..68d250ea80 100644 --- a/clatd.c +++ b/clatd.c @@ -214,7 +214,7 @@ void drop_root() { * mark - the socket mark to use for the sending raw socket */ void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); exit(1); @@ -276,6 +276,12 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, exit(1); } + error = set_nonblocking(tunnel->fd4); + if (error < 0) { + logmsg(ANDROID_LOG_FATAL, "set_nonblocking failed: %s", strerror(errno)); + exit(1); + } + configure_tun_ip(tunnel); } @@ -292,7 +298,9 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { readlen = read(read_fd, buf, PACKETLEN); if(readlen < 0) { - logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); + if (errno != EAGAIN) { + logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); + } return; } else if(readlen == 0) { logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed"); From 6ea37346ad20ac623312051b293bf5656e8072f6 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 19 Feb 2015 17:46:01 -0800 Subject: [PATCH 109/261] Remove unused variables. Change-Id: I3b1384e78c55b7327b6a635d0a853665cbb59e8b --- setif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setif.c b/setif.c index 359ed24f59..07f5bacd7a 100644 --- a/setif.c +++ b/setif.c @@ -159,7 +159,7 @@ static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int * ifname - name of interface to add the address to */ int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { - int ifindex, s, ret; + int ifindex; ifindex = if_nametoindex(ifname); if (!ifindex) { From e17c0ba1a385e6f0e7053f6b0206ff963d6b5ba5 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 24 Nov 2014 18:43:39 -0800 Subject: [PATCH 110/261] Android-Clatd: Remove unused variable For build-system CFLAGS clean-up, fix unused-but-set variables. (cherry picked from commit fee9afb1a5d9f7f5df0c79e29368c61d7da16fb8) Change-Id: Icacc158baae40d18f56bb32bb400ae6e765b0dad --- clatd_test.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index fe52c21eeb..aa3af74c9a 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -432,7 +432,6 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, fds)) { abort(); } - struct tun_pi tun_header = { 0, 0 }; char foo[512]; snprintf(foo, sizeof(foo), "%s: Invalid original packet", msg); @@ -443,13 +442,11 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * int version = ip_version(original); switch (version) { case 4: - tun_header.proto = htons(ETH_P_IP); expected_proto = htons(ETH_P_IPV6); read_fd = fds[1]; write_fd = fds[0]; break; case 6: - tun_header.proto = htons(ETH_P_IPV6); expected_proto = htons(ETH_P_IP); read_fd = fds[0]; write_fd = fds[1]; From 4b3c23768cb81ac16838834d3ef3c26984137066 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 11:27:41 +0900 Subject: [PATCH 111/261] Move tun functions to a new tun.c. (cherry picked from commit ff6f7fe4bd983c623d490100fa17cdb4936b95a2) Change-Id: I1effc2309565375bbe98262dd602a0895ab49fcf --- Android.mk | 2 +- clatd.c | 39 +--------------------------------- clatd.h | 8 ------- tun.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tun.h | 30 ++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 47 deletions(-) create mode 100644 tun.c create mode 100644 tun.h diff --git a/Android.mk b/Android.mk index 988d92510b..3750eaf425 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 icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.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 netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index 3f0af0bc25..1de39c87f3 100644 --- a/clatd.c +++ b/clatd.c @@ -50,6 +50,7 @@ #include "mtu.h" #include "getaddr.h" #include "dump.h" +#include "tun.h" #define DEVICEPREFIX "v4-" @@ -65,44 +66,6 @@ void stop_loop() { 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: configure_packet_socket * Binds the packet socket and attaches the receive filter to it. * sock - the socket to configure diff --git a/clatd.h b/clatd.h index e7cef4303a..b80b7cd731 100644 --- a/clatd.h +++ b/clatd.h @@ -18,9 +18,6 @@ #ifndef __CLATD_H__ #define __CLATD_H__ -#include -#include - #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) #define CLATD_VERSION "1.4" @@ -33,9 +30,4 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 -struct tun_data { - char device4[IFNAMSIZ]; - int read_fd6, write_fd6, fd4; -}; - #endif /* __CLATD_H__ */ diff --git a/tun.c b/tun.c new file mode 100644 index 0000000000..ef6b111f51 --- /dev/null +++ b/tun.c @@ -0,0 +1,62 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * tun.c - tun device functions + */ +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/tun.h b/tun.h new file mode 100644 index 0000000000..9ccb037380 --- /dev/null +++ b/tun.h @@ -0,0 +1,30 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * tun.h - tun device functions + */ +#ifndef __TUN_H__ +#define __TUN_H__ + +struct tun_data { + char device4[IFNAMSIZ]; + int read_fd6, write_fd6, fd4; +}; + +int set_nonblocking(int fd); +int tun_open(); +int tun_alloc(char *dev, int fd); + +#endif From 8dcc99dbbb7a49370dfb30ab73ded5234385cfe9 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 12:53:05 +0900 Subject: [PATCH 112/261] Move send_tun into tun.c as well. Also remove a redundant include in checksum.c. (cherry picked from commit 6b2007aacd13344c9bc73d5d858bd903b432c228) Change-Id: I8fd33ff52375b51e5d2f15b0a206d0a2dd3ba461 --- Android.mk | 2 +- checksum.c | 1 - clatd.h | 13 +++++++++++++ translate.c | 6 +----- translate.h | 11 ----------- tun.c | 7 +++++++ tun.h | 5 +++++ 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Android.mk b/Android.mk index 3750eaf425..67b4466fa4 100644 --- a/Android.mk +++ b/Android.mk @@ -30,7 +30,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c +LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog diff --git a/checksum.c b/checksum.c index 3dd1e00bef..23a7c02fb1 100644 --- a/checksum.c +++ b/checksum.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "checksum.h" diff --git a/clatd.h b/clatd.h index b80b7cd731..f421f46bb4 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,8 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include + #define MAXMTU 1500 #define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) #define CLATD_VERSION "1.4" @@ -30,4 +32,15 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 +// A clat_packet is an array of iovec structures representing a packet that we are translating. +// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain +// specific parts of the packet. The packet_* functions operate on all the packet segments past a +// given position. +typedef enum { + CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, CLAT_POS_MAX +} clat_packet_index; +typedef struct iovec clat_packet[CLAT_POS_MAX]; + #endif /* __CLATD_H__ */ diff --git a/translate.c b/translate.c index 487468b9cb..ddc9bac2d6 100644 --- a/translate.c +++ b/translate.c @@ -16,7 +16,6 @@ * translate.c - CLAT functions / partial implementation of rfc6145 */ #include -#include #include "icmp.h" #include "translate.h" @@ -25,6 +24,7 @@ #include "config.h" #include "logging.h" #include "debug.h" +#include "tun.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos @@ -465,10 +465,6 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t return CLAT_POS_PAYLOAD + 1; } -void send_tun(int fd, clat_packet out, int iov_len) { - writev(fd, out, iov_len); -} - // Weak symbol so we can override it in the unit test. void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak)); diff --git a/translate.h b/translate.h index 46e178b21b..aa8b736397 100644 --- a/translate.h +++ b/translate.h @@ -32,17 +32,6 @@ #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. -// A clat_packet is an array of iovec structures representing a packet that we are translating. -// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain -// specific parts of the packet. The packet_* functions operate on all the packet segments past a -// given position. -typedef enum { - CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX -} clat_packet_index; -typedef struct iovec clat_packet[CLAT_POS_MAX]; - // Calculates the checksum over all the packet components starting from pos. uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos); diff --git a/tun.c b/tun.c index ef6b111f51..a1b7254bcc 100644 --- a/tun.c +++ b/tun.c @@ -22,6 +22,9 @@ #include #include #include +#include + +#include "clatd.h" /* function: tun_open * tries to open the tunnel device @@ -60,3 +63,7 @@ int tun_alloc(char *dev, int fd) { strcpy(dev, ifr.ifr_name); return 0; } + +void send_tun(int fd, clat_packet out, int iov_len) { + writev(fd, out, iov_len); +} diff --git a/tun.h b/tun.h index 9ccb037380..771814d787 100644 --- a/tun.h +++ b/tun.h @@ -18,6 +18,10 @@ #ifndef __TUN_H__ #define __TUN_H__ +#include + +#include "clatd.h" + struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; @@ -26,5 +30,6 @@ struct tun_data { int set_nonblocking(int fd); int tun_open(); int tun_alloc(char *dev, int fd); +void send_tun(int fd, clat_packet out, int iov_len); #endif From 908dd6bbea33c3a83c1e1b0b4a4010028cf09a46 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 8 Dec 2014 10:51:32 +0900 Subject: [PATCH 113/261] Add a microbenchmark for tun write performance. (cherry picked from commit b20719ebf403b16d36a231aeef96607f8c7aa252) Change-Id: Ied5c60ceaf6ae3c80a3e0ac0785581e758158d20 --- Android.mk | 10 ++ clatd_microbenchmark.c | 214 +++++++++++++++++++++++++++++++++++++++++ tun.c | 24 ++++- tun.h | 4 +- 4 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 clatd_microbenchmark.c diff --git a/Android.mk b/Android.mk index 67b4466fa4..b4ae7f05bf 100644 --- a/Android.mk +++ b/Android.mk @@ -35,3 +35,13 @@ LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_NATIVE_TEST) + +# Microbenchmark. +include $(CLEAR_VARS) + +LOCAL_MODULE := clatd_microbenchmark +LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +LOCAL_SRC_FILES := clatd_microbenchmark.c checksum.c tun.c +LOCAL_MODULE_TAGS := eng tests + +include $(BUILD_NATIVE_TEST) diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c new file mode 100644 index 0000000000..fed310066a --- /dev/null +++ b/clatd_microbenchmark.c @@ -0,0 +1,214 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * clatd_microbenchmark.c - micro-benchmark for clatd tun send path + * + * Run with: + * + * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark + * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "checksum.h" +#include "tun.h" + +#define DEVICENAME "clat4" + +#define PORT 51339 +#define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr)) +#define NUMPACKETS 1000000 +#define SEC_TO_NANOSEC (1000 * 1000 * 1000) + +void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) { + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); +} + +void die(const char *str) { + perror(str); + exit(1); +} + +int setup_tun() { + int fd = tun_open(); + if (fd == -1) die("tun_open"); + + char dev[IFNAMSIZ] = DEVICENAME; + int ret = tun_alloc(dev, fd); + if (ret == -1) die("tun_alloc"); + struct ifreq ifr = { + .ifr_name = DEVICENAME, + }; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "192.0.0.4"); + if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); + init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "255.255.255.248"); + if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); + return fd; +} + +int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) { + struct tun_pi tun = { 0, htons(ETH_P_IP) }; + struct udphdr udp = { + .source = htons(1234), + .dest = htons(PORT), + .len = htons(len + sizeof(udp)), + .check = 0, + }; + struct iphdr ip = { + .version = 4, + .ihl = 5, + .tot_len = htons(len + sizeof(ip) + sizeof(udp)), + .frag_off = htons(IP_DF), + .ttl = 55, + .protocol = IPPROTO_UDP, + .saddr = htonl(0xc0000006), // 192.0.0.6 + .daddr = htonl(0xc0000004), // 192.0.0.4 + }; + clat_packet out = { + { &tun, sizeof(tun) }, // tun header + { &ip, sizeof(ip) }, // IP header + { NULL, 0 }, // Fragment header + { &udp, sizeof(udp) }, // Transport header + { NULL, 0 }, // ICMP error IP header + { NULL, 0 }, // ICMP error fragment header + { NULL, 0 }, // ICMP error transport header + { payload, len }, // Payload + }; + + ip.check = ip_checksum(&ip, sizeof(ip)); + + uint32_t sum; + sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); + sum = ip_checksum_add(sum, &udp, sizeof(udp)); + sum += payload_checksum; + udp.check = ip_checksum_finish(sum); + + return send_tun(fd, out, sizeof(out) / sizeof(out[0])); +} + +double timedelta(const struct timespec tv1, const struct timespec tv2) { + struct timespec end = tv2; + if (end.tv_nsec < tv1.tv_nsec) { + end.tv_sec -= 1; + end.tv_nsec += SEC_TO_NANOSEC; + } + double seconds = (end.tv_sec - tv1.tv_sec); + seconds += (((double) (end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); + return seconds; +} + +void benchmark(const char *name, int fd, int s, int num, int do_read, + uint8_t payload[], int len, uint32_t payload_sum) { + int i; + char buf[4096]; + struct timespec tv1, tv2; + int write_err = 0, read_err = 0; + clock_gettime(CLOCK_MONOTONIC, &tv1); + for (i = 0; i < num; i++) { + if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; + if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { + read_err++; + if (errno == ETIMEDOUT) { + printf("Timed out after %d packets!\n", i); + break; + } + } + } + clock_gettime(CLOCK_MONOTONIC, &tv2); + double seconds = timedelta(tv1, tv2); + int pps = (int) (i / seconds); + double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); + printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); + printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", + read_err, (float) read_err / i * 100, + write_err, (float) write_err / i * 100); +} + +int open_socket() { + int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); + + struct timeval tv = { 1, 0 }; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = ntohs(PORT), + .sin_addr = { INADDR_ANY } + }; + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) die ("bind"); + + return sock; +} + +int main() { + int fd = setup_tun(); + int sock = open_socket(); + + int i; + uint8_t payload[PAYLOADSIZE]; + for (i = 0; i < (int) sizeof(payload); i++) { + payload[i] = (uint8_t) i; + } + uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); + + // Check things are working. + char buf[4096]; + if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); + if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); + + benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); + + fd = setup_tun(); + set_nonblocking(fd); + benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); + close(fd); + + fd = setup_tun(); + set_nonblocking(fd); + benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); + + return 0; +} diff --git a/tun.c b/tun.c index a1b7254bcc..49f0ea782c 100644 --- a/tun.c +++ b/tun.c @@ -64,6 +64,26 @@ int tun_alloc(char *dev, int fd) { return 0; } -void send_tun(int fd, clat_packet out, int iov_len) { - writev(fd, out, iov_len); +/* function: set_nonblocking + * sets a filedescriptor to non-blocking mode + * fd - the filedescriptor + * returns: 0 on success, -1 on failure + */ +int set_nonblocking(int fd) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return flags; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +/* function: send_tun + * sends a clat_packet to a tun interface + * fd - the tun filedescriptor + * out - the packet to send + * iov_len - the number of entries in the clat_packet + * returns: number of bytes read on success, -1 on failure + */ +int send_tun(int fd, clat_packet out, int iov_len) { + return writev(fd, out, iov_len); } diff --git a/tun.h b/tun.h index 771814d787..946ab47fc8 100644 --- a/tun.h +++ b/tun.h @@ -27,9 +27,9 @@ struct tun_data { int read_fd6, write_fd6, fd4; }; -int set_nonblocking(int fd); int tun_open(); int tun_alloc(char *dev, int fd); -void send_tun(int fd, clat_packet out, int iov_len); +int send_tun(int fd, clat_packet out, int iov_len); +int set_nonblocking(int fd); #endif From 7023303c34040c25ce4170b43adf020ebd53a068 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Mon, 8 Dec 2014 16:30:50 -0800 Subject: [PATCH 114/261] Add missing include for IFF_BROADCAST to fix build (cherry picked from commit cfbb4ac47fb065c888b63b03a5697d24c9c5952e) Change-Id: I03411d12ef4f45fe0c0c14dd6f546108e272da93 --- config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.h b/config.h index b6d9ae158c..e31a81d1e2 100644 --- a/config.h +++ b/config.h @@ -19,6 +19,7 @@ #define __CONFIG_H__ #include +#include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" From 78ce1be5f9e9bb26bacda70fdf1003c376eab97d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 3 Dec 2014 15:18:29 +0900 Subject: [PATCH 115/261] Switch the receive path to memory-mapped I/O with PACKET_RX_RING. (cherry picked from commit 9353be2a5f1b4fd00b04e4c826f7f3c3ec6c5d46) Change-Id: Ifb35b4efae0363a2006f3bba002e44a0560f3014 --- Android.mk | 2 +- clatd.c | 85 ++++++++++++++++-------------------- ring.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ring.h | 55 +++++++++++++++++++++++ tun.h | 2 + 5 files changed, 221 insertions(+), 49 deletions(-) create mode 100644 ring.c create mode 100644 ring.h diff --git a/Android.mk b/Android.mk index b4ae7f05bf..5f2fe639b1 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 icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c +LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include diff --git a/clatd.c b/clatd.c index 1de39c87f3..94cb3b5b59 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,7 @@ #include "getaddr.h" #include "dump.h" #include "tun.h" +#include "ring.h" #define DEVICEPREFIX "v4-" @@ -213,13 +214,10 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { tunnel->write_fd6 = rawsock; - int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); - if (packetsock < 0) { - logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + tunnel->read_fd6 = ring_create(tunnel); + if (tunnel->read_fd6 < 0) { exit(1); } - - tunnel->read_fd6 = packetsock; } /* function: update_clat_ipv6_address @@ -320,16 +318,16 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, } /* 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 + * reads a packet from the tunnel fd and translates it + * read_fd - file descriptor to read original packet from + * write_fd - file descriptor to write translated packet to + * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 */ -void read_packet(int active_fd, const struct tun_data *tunnel) { +void read_packet(int read_fd, int write_fd, int to_ipv6) { ssize_t readlen; uint8_t buf[PACKETLEN], *packet; - int fd; - readlen = read(active_fd, buf, PACKETLEN); + readlen = read(read_fd, buf, PACKETLEN); if(readlen < 0) { logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); @@ -340,41 +338,32 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { return; } - if (active_fd == tunnel->fd4) { - 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; - } - - struct tun_pi *tun_header = (struct tun_pi *) buf; - uint16_t proto = ntohs(tun_header->proto); - if (proto != ETH_P_IP) { - logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); - return; - } - - if(tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); - } - - fd = tunnel->write_fd6; - packet = buf + header_size; - readlen -= header_size; - } else { - fd = tunnel->fd4; - packet = buf; + struct tun_pi *tun_header = (struct tun_pi *) buf; + if (readlen < (ssize_t) sizeof(*tun_header)) { + logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen); + return; } - translate_packet(fd, (fd == tunnel->write_fd6), packet, readlen); + uint16_t proto = ntohs(tun_header->proto); + if (proto != ETH_P_IP) { + logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); + return; + } + + if(tun_header->flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + } + + packet = (uint8_t *) (tun_header + 1); + readlen -= sizeof(*tun_header); + translate_packet(write_fd, to_ipv6, packet, readlen); } /* function: event_loop * reads packets from the tun network interface and passes them down the stack * tunnel - tun device data */ -void event_loop(const struct tun_data *tunnel) { +void event_loop(struct tun_data *tunnel) { time_t last_interface_poll; struct pollfd wait_fd[] = { { tunnel->read_fd6, POLLIN, 0 }, @@ -390,16 +379,16 @@ void event_loop(const struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno)); } } else { - size_t i; - for(i = 0; i < ARRAY_SIZE(wait_fd); i++) { - // Call read_packet if the socket has data to be read, but also if an - // error is waiting. If we don't call read() after getting POLLERR, a - // subsequent poll() will return immediately with POLLERR again, - // causing this code to spin in a loop. Calling read() will clear the - // socket error flag instead. - if(wait_fd[i].revents != 0) { - read_packet(wait_fd[i].fd,tunnel); - } + // Call read_packet if the socket has data to be read, but also if an + // error is waiting. If we don't call read() after getting POLLERR, a + // subsequent poll() will return immediately with POLLERR again, + // causing this code to spin in a loop. Calling read() will clear the + // socket error flag instead. + if (wait_fd[0].revents) { + ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); + } + if (wait_fd[1].revents) { + read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); } } diff --git a/ring.c b/ring.c new file mode 100644 index 0000000000..5e99fd5f5d --- /dev/null +++ b/ring.c @@ -0,0 +1,126 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ring.c - packet ring buffer functions + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "ring.h" +#include "translate.h" +#include "tun.h" + +int ring_create(struct tun_data *tunnel) { + int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + if (packetsock < 0) { + logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); + return -1; + } + + int ver = TPACKET_V2; + if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *) &ver, sizeof(ver))) { + logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno)); + return -1; + } + + int on = 1; + if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *) &on, sizeof(on))) { + logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno)); + } + + struct packet_ring *ring = &tunnel->ring; + ring->numblocks = TP_NUM_BLOCKS; + + int total_frames = TP_FRAMES * ring->numblocks; + + struct tpacket_req req = { + .tp_frame_size = TP_FRAME_SIZE, // Frame size. + .tp_block_size = TP_BLOCK_SIZE, // Frames per block. + .tp_block_nr = ring->numblocks, // Number of blocks. + .tp_frame_nr = total_frames, // Total frames. + }; + + if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { + logmsg(ANDROID_LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno)); + return -1; + } + + size_t buflen = TP_BLOCK_SIZE * ring->numblocks; + ring->base = mmap(NULL, buflen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED|MAP_POPULATE, + packetsock, 0); + if (ring->base == MAP_FAILED) { + logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno)); + return -1; + } + + ring->block = 0; + ring->slot = 0; + ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE; + ring->next = (struct tpacket2_hdr *) ring->base; + + logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", + total_frames, buflen, ring->base); + + return packetsock; +} + +/* function: ring_advance + * advances to the next position in the packet ring + * ring - packet ring buffer + */ +static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) { + uint8_t *next = (uint8_t *) ring->next; + + ring->slot++; + next += TP_FRAME_SIZE; + + if (ring->slot == ring->numslots) { + ring->slot = 0; + ring->block++; + + if (ring->block < ring->numblocks) { + next += TP_FRAME_GAP; + } else { + ring->block = 0; + next = (uint8_t *) ring->base; + } + } + + ring->next = (struct tpacket2_hdr *) next; + return ring->next; +} + +/* function: ring_read + * reads a packet from the ring buffer and translates it + * read_fd - file descriptor to read original packet from + * write_fd - file descriptor to write translated packet to + * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 + */ +void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) { + struct tpacket2_hdr *tp = ring->next; + if (tp->tp_status & TP_STATUS_USER) { + uint8_t *packet = ((uint8_t *) tp) + tp->tp_net; + translate_packet(write_fd, to_ipv6, packet, tp->tp_len); + tp->tp_status = TP_STATUS_KERNEL; + tp = ring_advance(ring); + } +} diff --git a/ring.h b/ring.h new file mode 100644 index 0000000000..b9b8c11d2e --- /dev/null +++ b/ring.h @@ -0,0 +1,55 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ring.c - packet ring buffer functions + */ +#ifndef __RING_H__ +#define __RING_H__ + +#include +#include + +#include "clatd.h" + +struct tun_data; + +// Frame size. Must be a multiple of TPACKET_ALIGNMENT (=16) +// Why the 16? http://lxr.free-electrons.com/source/net/packet/af_packet.c?v=3.4#L1764 +#define TP_FRAME_SIZE (TPACKET_ALIGN(MAXMTU) + TPACKET_ALIGN(TPACKET2_HDRLEN) + 16) + +// Block size. Must be a multiple of the page size, and a power of two for efficient memory use. +#define TP_BLOCK_SIZE 65536 + +// In order to save memory, our frames are not an exact divider of the block size. Therefore, the +// mmaped region will have gaps corresponding to the empty space at the end of each block. +#define TP_FRAMES (TP_BLOCK_SIZE / TP_FRAME_SIZE) +#define TP_FRAME_GAP (TP_BLOCK_SIZE % TP_FRAME_SIZE) + +// TODO: Make this configurable. This requires some refactoring because the packet socket is +// opened before we drop privileges, but the configuration file is read after. A value of 16 +// results in 656 frames (1048576 bytes). +#define TP_NUM_BLOCKS 16 + +struct packet_ring { + uint8_t *base; + struct tpacket2_hdr *next; + int slot, numslots; + int block, numblocks; +}; + +int ring_create(struct tun_data *tunnel); +void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6); + +#endif diff --git a/tun.h b/tun.h index 946ab47fc8..bcdd10e3b0 100644 --- a/tun.h +++ b/tun.h @@ -21,10 +21,12 @@ #include #include "clatd.h" +#include "ring.h" struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; + struct packet_ring ring; }; int tun_open(); From d432b4f6bfb23b6fbf921a620d2661b2acb108fe Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 31 Jan 2015 19:18:47 +0900 Subject: [PATCH 116/261] Make the raw socket and the tun fd nonblocking. (cherry picked from commit 49454814a0a714189916cb2aea1922bc8784b13b) Change-Id: I6c0e0fba64e9a785cd225e8c543df06e864db8e7 --- clatd.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clatd.c b/clatd.c index 94cb3b5b59..faeb679f86 100644 --- a/clatd.c +++ b/clatd.c @@ -198,7 +198,7 @@ void drop_root() { * mark - the socket mark to use for the sending raw socket */ void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); exit(1); @@ -314,6 +314,12 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, exit(1); } + error = set_nonblocking(tunnel->fd4); + if (error < 0) { + logmsg(ANDROID_LOG_FATAL, "set_nonblocking failed: %s", strerror(errno)); + exit(1); + } + configure_tun_ip(tunnel); } @@ -330,7 +336,9 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { readlen = read(read_fd, buf, PACKETLEN); if(readlen < 0) { - logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); + if (errno != EAGAIN) { + logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); + } return; } else if(readlen == 0) { logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed"); From edb308b89abb85929762d06ba3e91e92308cfc24 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 19 Feb 2015 17:46:01 -0800 Subject: [PATCH 117/261] Remove unused variables. Change-Id: I3b1384e78c55b7327b6a635d0a853665cbb59e8b --- setif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setif.c b/setif.c index 359ed24f59..07f5bacd7a 100644 --- a/setif.c +++ b/setif.c @@ -159,7 +159,7 @@ static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int * ifname - name of interface to add the address to */ int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { - int ifindex, s, ret; + int ifindex; ifindex = if_nametoindex(ifname); if (!ifindex) { From 96d6901de7452ed1f0f11e264e44519f81d72aa2 Mon Sep 17 00:00:00 2001 From: Masaya Hoshina Date: Wed, 10 Jun 2015 22:17:49 +0900 Subject: [PATCH 118/261] Fix default_pdp_interface pointer reference The default_pdp_interface was an array until 1352a3a26c4d7c32b38b7fadb837799a23014aa6 but is now a pointer and should be used directly here. Change-Id: I153802bddb2412f1b1d9e7a61081a5c6d34ee2cb --- clatd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index faeb679f86..d57ea59e7d 100644 --- a/clatd.c +++ b/clatd.c @@ -75,7 +75,7 @@ int configure_packet_socket(int sock) { struct sockaddr_ll sll = { .sll_family = AF_PACKET, .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex((char *) &Global_Clatd_Config.default_pdp_interface), + .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. }; if (bind(sock, (struct sockaddr *) &sll, sizeof(sll))) { From 44f0955949f0830b7b67d328ac6ace40268e2d21 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 22 Sep 2015 10:53:44 -0700 Subject: [PATCH 119/261] Switch android-clat's microbenchmark to clang. Avoid GCC for an upcoming change. Change-Id: Id354f26a6ce11da21d8ead1f46ebedd68aa3c36b --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index 5f2fe639b1..636cf9cc07 100644 --- a/Android.mk +++ b/Android.mk @@ -39,6 +39,7 @@ include $(BUILD_NATIVE_TEST) # Microbenchmark. include $(CLEAR_VARS) +LOCAL_CLANG := true LOCAL_MODULE := clatd_microbenchmark LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_SRC_FILES := clatd_microbenchmark.c checksum.c tun.c From 2cb02c295dcd3cabf18d3384d9b12319e60dc974 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 12 Feb 2016 19:27:53 -0800 Subject: [PATCH 120/261] Add missing libnetutils dependency. Change-Id: Ibeba64eb530597f3ea1ec05c9409f333711235da --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 636cf9cc07..cdb781041c 100644 --- a/Android.mk +++ b/Android.mk @@ -32,7 +32,7 @@ LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c LOCAL_MODULE_TAGS := eng tests -LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SHARED_LIBRARIES := liblog libnetutils include $(BUILD_NATIVE_TEST) From 2dcca6feb91fd917e26c4989af6400de904466a2 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Wed, 24 Feb 2016 15:55:54 -0800 Subject: [PATCH 121/261] Convert a few instances of sprintf to snprintf. Change-Id: Ib3cdf8c3c817adc0eea0095ca4ef5fd66c8456b0 --- clatd_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 7e218f00fe..b901c30d2c 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -379,12 +379,12 @@ void check_data_matches(const void *expected, const void *actual, size_t len, co unsigned pos = 0; for (unsigned i = 0; i < len; i++) { if (i % 20 == 0) { - sprintf(expected_hexdump + pos, "\n "); - sprintf(actual_hexdump + pos, "\n "); + snprintf(expected_hexdump + pos, hexdump_len - pos, "\n "); + snprintf(actual_hexdump + pos, hexdump_len - pos, "\n "); pos += 4; } - sprintf(expected_hexdump + pos, " %02x", ((uint8_t *) expected)[i]); - sprintf(actual_hexdump + pos, " %02x", ((uint8_t *) actual)[i]); + snprintf(expected_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *) expected)[i]); + snprintf(actual_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *) actual)[i]); pos += 3; } FAIL() << msg << ": Data doesn't match" From c2f9edd16daf2752bbec4949f513df2e42fdc5c3 Mon Sep 17 00:00:00 2001 From: Paul Stewart Date: Fri, 11 Nov 2016 11:58:52 -0800 Subject: [PATCH 122/261] Update for libnl 3.2.25 NL headers have moved. Private headers are now in their own subdirectory. Bug: 32700211 Change-Id: Iac17ecac408ce44c373b868f210014999e373abe Test: Compile, connect to WiFi, run "iw link" --- getaddr.c | 1 + netlink_msg.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/getaddr.c b/getaddr.c index 5cae78b7ff..e0478cf286 100644 --- a/getaddr.c +++ b/getaddr.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include diff --git a/netlink_msg.c b/netlink_msg.c index 958559c0fa..13906f4861 100644 --- a/netlink_msg.c +++ b/netlink_msg.c @@ -22,8 +22,8 @@ #include #include -#include -#include +#include +#include #include #include #include From 5437b1d29c19e09afe9b088e9f4399e937c691c7 Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Tue, 13 Dec 2016 01:45:45 -0800 Subject: [PATCH 123/261] Disable -Waddress-of-packed-member temporarily. Bug: http://b/33566695 Taking the address of packed data members yields pointers that only have a guaranteed alignment of 1. Since this is an external/ project, we temporarily will disable this warning until a proper fix can be made in the upstream version of the project. Test: Builds successfully with updated toolchain. Change-Id: I8be14bdd5600108de227a1ec16d86caac452ec85 --- Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Android.mk b/Android.mk index cdb781041c..8c0a43ee76 100644 --- a/Android.mk +++ b/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +# Bug: http://b/33566695 +LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils @@ -30,6 +32,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +# Bug: http://b/33566695 +LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog libnetutils From 6f02370684fdab6f6b4ac1f73518d0c624406fcf Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Thu, 26 Jan 2017 07:59:09 +0000 Subject: [PATCH 124/261] Revert "Disable -Waddress-of-packed-member temporarily." This reverts commit 5437b1d29c19e09afe9b088e9f4399e937c691c7. Reason for revert: Change-Id: I7b53131056ecbf1b9780e46f8105d72138b59641 --- Android.mk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Android.mk b/Android.mk index 8c0a43ee76..cdb781041c 100644 --- a/Android.mk +++ b/Android.mk @@ -4,8 +4,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -# Bug: http://b/33566695 -LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils @@ -32,8 +30,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -# Bug: http://b/33566695 -LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog libnetutils From 86aef026336129f099741f47eeac6ceba33b9917 Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Thu, 26 Jan 2017 00:48:51 -0800 Subject: [PATCH 125/261] Revert "Revert "Disable -Waddress-of-packed-member temporarily."" This reverts commit 6f02370684fdab6f6b4ac1f73518d0c624406fcf. Bug: http://b/31532493 Test: Manual validation --- Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Android.mk b/Android.mk index cdb781041c..8c0a43ee76 100644 --- a/Android.mk +++ b/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +# Bug: http://b/33566695 +LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils @@ -30,6 +32,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := clatd_test LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter +# Bug: http://b/33566695 +LOCAL_CFLAGS += -Wno-address-of-packed-member LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c LOCAL_MODULE_TAGS := eng tests LOCAL_SHARED_LIBRARIES := liblog libnetutils From cc94e5f25303bc0ffdc2e327b56564f59f48804f Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Sat, 1 Apr 2017 10:00:19 -0700 Subject: [PATCH 126/261] clatd.c: remove unsetenv("ANDROID_DNS_MODE") As of bionic commit I835a7b42d6afbc9c67866594c7951cfd9b355d81, ANDROID_DNS_MODE is deleted from the environment when a security transition occurs. As netd and clatd run in different SELinux security domains, this variable will be automatically deleted. It's unnecessary (but harmless) to delete it ourselves. Delete unnecessary code. Test: Android compiles and runs with no obvious problems. Change-Id: Id46c1a6989a3441a11b4be3d8a28aa1b926c1c6d --- clatd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clatd.c b/clatd.c index d57ea59e7d..0b69238c1f 100644 --- a/clatd.c +++ b/clatd.c @@ -503,10 +503,6 @@ int main(int argc, char **argv) { exit(1); } - // When run from netd, the environment variable ANDROID_DNS_MODE is set to - // "local", but that only works for the netd process itself. - unsetenv("ANDROID_DNS_MODE"); - configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); update_clat_ipv6_address(&tunnel, uplink_interface); From e700a73dd03a0c111a8a55ef2f9f22b264663138 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Mon, 12 Jun 2017 11:57:45 -0700 Subject: [PATCH 127/261] Add OWNERS in external/android-clat * Owners are selected from top CL approvals or owners. They will be suggested to review/approve future CLs. * OWNERS files are recognized by the new find-owners plugin, see .md files in https://gerrit.googlesource.com/plugins/find-owners/+/master/src/main/resources/Documentation/ Test: build/make/tools/checkowners.py -c -v OWNERS Change-Id: Ied56f2eb0e21a1c61ec67c891d31c837b6bb1b7f --- OWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 OWNERS diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000000..8f0a28b49f --- /dev/null +++ b/OWNERS @@ -0,0 +1,2 @@ +lorenzo@google.com +ek@google.com From d1383bd268946d0c54e50e981b7d5c2c85509df4 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 21 Jun 2017 18:23:18 +0900 Subject: [PATCH 128/261] Increase the maximum exponential backoff for DNS64 queries. Currently, we only scale down DNS64 detection queries to one every two minutes. This wastes power on true IPv6-only networks such as the IMS networks of many operators. The only possible benefit is that if a NAT64 were to appear on a previously IPv6-only network, we detect it within two minutes. Set the maximum backoff to one query per 30 minutes, while maintaining the exponential backoff starting at 1 second. Bug: 62650901 Test: bullhead successfully detects NAT64 network Change-Id: Ia411018b92722615c3ac52bc7f360094bc96532f --- config.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index b147868c5f..2d7085bb63 100644 --- a/config.c +++ b/config.c @@ -180,8 +180,11 @@ void dns64_detection(unsigned net_id) { logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); sleep(backoff_sleep); backoff_sleep *= 2; - if(backoff_sleep >= 120) { - backoff_sleep = 120; + if(backoff_sleep >= 1800) { + // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the + // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only + // to IPv6 with NAT64). + backoff_sleep = 1800; } } } From 5fcb58ea1174aefb6f0a41eb273d78e72fdb5a97 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 21 Jun 2017 18:23:18 +0900 Subject: [PATCH 129/261] Increase the maximum exponential backoff for DNS64 queries. Currently, we only scale down DNS64 detection queries to one every two minutes. This wastes power on true IPv6-only networks such as the IMS networks of many operators. The only possible benefit is that if a NAT64 were to appear on a previously IPv6-only network, we detect it within two minutes. Set the maximum backoff to one query per 30 minutes, while maintaining the exponential backoff starting at 1 second. (cherry picked from commit d1383bd268946d0c54e50e981b7d5c2c85509df4) Bug: 62650901 Test: bullhead successfully detects NAT64 network Change-Id: Ib6a7df5b25690d8b564079efbcaf8325c37c2e48 --- config.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index b147868c5f..2d7085bb63 100644 --- a/config.c +++ b/config.c @@ -180,8 +180,11 @@ void dns64_detection(unsigned net_id) { logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); sleep(backoff_sleep); backoff_sleep *= 2; - if(backoff_sleep >= 120) { - backoff_sleep = 120; + if(backoff_sleep >= 1800) { + // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the + // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only + // to IPv6 with NAT64). + backoff_sleep = 1800; } } } From f5fe2672d0a4bd307459980018982954f58a46cc Mon Sep 17 00:00:00 2001 From: Jayachandran Chinnakkannu Date: Thu, 4 Jan 2018 18:08:53 +0000 Subject: [PATCH 130/261] Revert "clatd.c: remove unsetenv("ANDROID_DNS_MODE")" This reverts commit cc94e5f25303bc0ffdc2e327b56564f59f48804f. Reason for revert: XLAT doesn't work in permissive mode Bug: 69863844 Change-Id: I62e390b960465048fbbd68e60d0a2c9c7d2c82d3 --- clatd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clatd.c b/clatd.c index 0b69238c1f..c579df0324 100644 --- a/clatd.c +++ b/clatd.c @@ -503,6 +503,11 @@ int main(int argc, char **argv) { exit(1); } + // When run from netd, the environment variable ANDROID_DNS_MODE is set to + // "local", but that only works for the netd process itself. Removing the + // following line causes XLAT failure in permissive mode. + unsetenv("ANDROID_DNS_MODE"); + configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); update_clat_ipv6_address(&tunnel, uplink_interface); From 0b1d19ca5f77aa99fee2f390c76c40c9bfcef8b4 Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Thu, 29 Mar 2018 14:36:59 -0700 Subject: [PATCH 131/261] Resolve DNS64 hostname with Private DNS bypass Test: as follows - builds - flashes - boots - runtest -x external/android-clat/clatd_test.cpp passes Bug: 64133961 Bug: 72345192 Change-Id: If5238d0cdf27cb4cd689e1ff1ff776bc638d5a7b --- Android.mk | 2 +- dns64.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 8c0a43ee76..cae37fc71a 100644 --- a/Android.mk +++ b/Android.mk @@ -6,7 +6,7 @@ LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c conf LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter # Bug: http://b/33566695 LOCAL_CFLAGS += -Wno-address-of-packed-member -LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include +LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include system/netd/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils diff --git a/dns64.c b/dns64.c index 4e9252dc2e..a9ca61de25 100644 --- a/dns64.c +++ b/dns64.c @@ -27,6 +27,7 @@ #include "dns64.h" #include "logging.h" +#include "NetdClient.h" #include "resolv_netid.h" /* function: plat_prefix @@ -46,6 +47,11 @@ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); + // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). + if (net_id != NETID_UNSET) { + net_id += NETID_USE_LOCAL_NAMESERVERS; + } + status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); if (status != 0 || result == NULL) { logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", From eb5c05baea931ebe37f3fc161e231b7fc56f2aaa Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Thu, 29 Mar 2018 14:36:59 -0700 Subject: [PATCH 132/261] Resolve DNS64 hostname with Private DNS bypass Test: as follows - builds - flashes - boots - runtest -x external/android-clat/clatd_test.cpp passes Bug: 64133961 Bug: 72345192 Merged-In: I0b70f1655c9a202905a30838b002c9482898d506 Merged-In: I45214f0a04623012187168d9319babc98eb396de Change-Id: If5238d0cdf27cb4cd689e1ff1ff776bc638d5a7b (cherry picked from commit 0b1d19ca5f77aa99fee2f390c76c40c9bfcef8b4) --- Android.mk | 2 +- dns64.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 8c0a43ee76..cae37fc71a 100644 --- a/Android.mk +++ b/Android.mk @@ -6,7 +6,7 @@ LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c conf LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter # Bug: http://b/33566695 LOCAL_CFLAGS += -Wno-address-of-packed-member -LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include +LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include system/netd/include LOCAL_STATIC_LIBRARIES := libnl LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils diff --git a/dns64.c b/dns64.c index 4e9252dc2e..a9ca61de25 100644 --- a/dns64.c +++ b/dns64.c @@ -27,6 +27,7 @@ #include "dns64.h" #include "logging.h" +#include "NetdClient.h" #include "resolv_netid.h" /* function: plat_prefix @@ -46,6 +47,11 @@ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); + // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). + if (net_id != NETID_UNSET) { + net_id += NETID_USE_LOCAL_NAMESERVERS; + } + status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); if (status != 0 || result == NULL) { logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", From f1c79f8dac61141a518b87361cfef0619e3ef49e Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Thu, 29 Mar 2018 23:38:49 -0700 Subject: [PATCH 133/261] When manipulating flags, |= rather than += Test: as follows - builds - flashes - boots - runtest -x external/android-clat/clatd_test.cpp passes Bug: 64133961 Bug: 72345192 Change-Id: Ieb46fa6a158ec7470e261cc6e9345a3266c81a04 --- dns64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns64.c b/dns64.c index a9ca61de25..ed28eb0255 100644 --- a/dns64.c +++ b/dns64.c @@ -49,7 +49,7 @@ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). if (net_id != NETID_UNSET) { - net_id += NETID_USE_LOCAL_NAMESERVERS; + net_id |= NETID_USE_LOCAL_NAMESERVERS; } status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); From cdc003dc8a8ffb418f33518ac545016e6991534a Mon Sep 17 00:00:00 2001 From: Erik Kline Date: Thu, 29 Mar 2018 23:38:49 -0700 Subject: [PATCH 134/261] When manipulating flags, |= rather than += Test: as follows - builds - flashes - boots - runtest -x external/android-clat/clatd_test.cpp passes Bug: 64133961 Bug: 72345192 Merged-In: I10367d3b83acce55e3492812fd04cbdf5f0e6e1a Merged-In: I5c13652a2c79c07318cc20311c9666535df91d7d Change-Id: Ieb46fa6a158ec7470e261cc6e9345a3266c81a04 (cherry picked from commit f1c79f8dac61141a518b87361cfef0619e3ef49e) --- dns64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns64.c b/dns64.c index a9ca61de25..ed28eb0255 100644 --- a/dns64.c +++ b/dns64.c @@ -49,7 +49,7 @@ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). if (net_id != NETID_UNSET) { - net_id += NETID_USE_LOCAL_NAMESERVERS; + net_id |= NETID_USE_LOCAL_NAMESERVERS; } status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); From 69dc60de5d53488a3c45737ec17b242d703d682b Mon Sep 17 00:00:00 2001 From: Bernie Innocenti Date: Mon, 14 May 2018 20:40:49 +0900 Subject: [PATCH 135/261] Avoid spinning endlessly on packet socket errors In the event the packet socket has an error, it is likely not cleared and clatd can spin in a tight loop. This may happen when a network is going down (ENETDOWN, ENETUNREACH, ...). Ordinarily the ConnectivityService/Nat464Xlat/ClatdController control plane would tear down clatd in this case. However, if the control plane is deadlocked, clatd will chew up CPU indefinitely. This fix consists of detecting when poll() exits due to a socket error and trying to clear the error by issuing a no-op recv() on the packet socket and a 0-byte read() for the /dev/tun socket. Test: manually, on a marlyn device (kernel 3.18.70) Bug: 78602493 Change-Id: Ic23f999712a674df11e981a4314ad371e8d5fb6d --- clatd.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index c579df0324..168cc6989f 100644 --- a/clatd.c +++ b/clatd.c @@ -382,19 +382,28 @@ void event_loop(struct tun_data *tunnel) { last_interface_poll = time(NULL); while(running) { - if(poll(wait_fd, 2, NO_TRAFFIC_INTERFACE_POLL_FREQUENCY*1000) == -1) { - if(errno != EINTR) { - logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno)); + if (poll(wait_fd, ARRAY_SIZE(wait_fd), + NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) { + if (errno != EINTR) { + logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s", strerror(errno)); } } else { + if (wait_fd[0].revents & POLLIN) { + ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); + } + // If any other bit is set, assume it's due to an error (i.e. POLLERR). + if (wait_fd[0].revents & ~POLLIN) { + // ring_read doesn't clear the error indication on the socket. + recv(tunnel->read_fd6, NULL, 0, MSG_PEEK); + logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", + strerror(errno)); + } + // Call read_packet if the socket has data to be read, but also if an // error is waiting. If we don't call read() after getting POLLERR, a // subsequent poll() will return immediately with POLLERR again, // causing this code to spin in a loop. Calling read() will clear the // socket error flag instead. - if (wait_fd[0].revents) { - ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); - } if (wait_fd[1].revents) { read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); } From 7ac4a30be5d8cbfafc740f9f86d30f37d15f4483 Mon Sep 17 00:00:00 2001 From: Bernie Innocenti Date: Mon, 14 May 2018 20:40:49 +0900 Subject: [PATCH 136/261] Avoid spinning endlessly on packet socket errors In the event the packet socket has an error, it is likely not cleared and clatd can spin in a tight loop. This may happen when a network is going down (ENETDOWN, ENETUNREACH, ...). Ordinarily the ConnectivityService/Nat464Xlat/ClatdController control plane would tear down clatd in this case. However, if the control plane is deadlocked, clatd will chew up CPU indefinitely. This fix consists of detecting when poll() exits due to a socket error and trying to clear the error by issuing a no-op recv() on the packet socket and a 0-byte read() for the /dev/tun socket. Test: manually, on a marlyn device (kernel 3.18.70) Bug: 78602493 Change-Id: Ic23f999712a674df11e981a4314ad371e8d5fb6d (cherry picked from commit 69dc60de5d53488a3c45737ec17b242d703d682b) --- clatd.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index c579df0324..168cc6989f 100644 --- a/clatd.c +++ b/clatd.c @@ -382,19 +382,28 @@ void event_loop(struct tun_data *tunnel) { last_interface_poll = time(NULL); while(running) { - if(poll(wait_fd, 2, NO_TRAFFIC_INTERFACE_POLL_FREQUENCY*1000) == -1) { - if(errno != EINTR) { - logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s",strerror(errno)); + if (poll(wait_fd, ARRAY_SIZE(wait_fd), + NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) { + if (errno != EINTR) { + logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s", strerror(errno)); } } else { + if (wait_fd[0].revents & POLLIN) { + ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); + } + // If any other bit is set, assume it's due to an error (i.e. POLLERR). + if (wait_fd[0].revents & ~POLLIN) { + // ring_read doesn't clear the error indication on the socket. + recv(tunnel->read_fd6, NULL, 0, MSG_PEEK); + logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", + strerror(errno)); + } + // Call read_packet if the socket has data to be read, but also if an // error is waiting. If we don't call read() after getting POLLERR, a // subsequent poll() will return immediately with POLLERR again, // causing this code to spin in a loop. Calling read() will clear the // socket error flag instead. - if (wait_fd[0].revents) { - ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); - } if (wait_fd[1].revents) { read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); } From 1e562182f997b6853f3f2b710845e975f037925e Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Fri, 16 Nov 2018 12:52:08 -0800 Subject: [PATCH 137/261] Convert to Android.bp See build/soong/README.md for more information. Test: cd external/android-clat; mma Test: atest clatd_test Change-Id: Ib9691dd8f3ee899743751e72ea35ef106b03de92 --- Android.bp | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Android.mk | 52 ------------------------------ 2 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 Android.bp delete mode 100644 Android.mk diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000000..2603472b08 --- /dev/null +++ b/Android.bp @@ -0,0 +1,95 @@ +// The clat daemon. +cc_binary { + name: "clatd", + + srcs: [ + "clatd.c", + "dump.c", + "checksum.c", + "translate.c", + "icmp.c", + "ipv4.c", + "ipv6.c", + "config.c", + "dns64.c", + "logging.c", + "getaddr.c", + "netlink_callbacks.c", + "netlink_msg.c", + "setif.c", + "mtu.c", + "tun.c", + "ring.c", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused-parameter", + + // Bug: http://b/33566695 + "-Wno-address-of-packed-member", + ], + + include_dirs: ["bionic/libc/dns/include"], + header_libs: ["libnetd_client_headers"], + static_libs: ["libnl"], + shared_libs: [ + "libcutils", + "liblog", + "libnetutils", + ], + +} + +// The configuration file. +prebuilt_etc { + name: "clatd.conf", + src: "clatd.conf", +} + +// Unit tests. +cc_test { + name: "clatd_test", + cflags: [ + "-Wall", + "-Werror", + "-Wunused-parameter", + + // Bug: http://b/33566695 + "-Wno-address-of-packed-member", + ], + + srcs: [ + "clatd_test.cpp", + "checksum.c", + "translate.c", + "icmp.c", + "ipv4.c", + "ipv6.c", + "logging.c", + "config.c", + "tun.c", + ], + + shared_libs: [ + "liblog", + "libnetutils", + ], +} + +// Microbenchmark. +cc_test { + name: "clatd_microbenchmark", + + cflags: [ + "-Wall", + "-Werror", + "-Wunused-parameter", + ], + srcs: [ + "clatd_microbenchmark.c", + "checksum.c", + "tun.c", + ], +} diff --git a/Android.mk b/Android.mk deleted file mode 100644 index cae37fc71a..0000000000 --- a/Android.mk +++ /dev/null @@ -1,52 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c netlink_callbacks.c netlink_msg.c setif.c mtu.c tun.c ring.c - -LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -# Bug: http://b/33566695 -LOCAL_CFLAGS += -Wno-address-of-packed-member -LOCAL_C_INCLUDES := external/libnl/include bionic/libc/dns/include system/netd/include -LOCAL_STATIC_LIBRARIES := libnl -LOCAL_SHARED_LIBRARIES := libcutils liblog libnetutils - -# The clat daemon. -LOCAL_MODULE := clatd - -include $(BUILD_EXECUTABLE) - - -# The configuration file. -include $(CLEAR_VARS) - -LOCAL_MODULE := clatd.conf -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT)/etc -LOCAL_SRC_FILES := $(LOCAL_MODULE) - -include $(BUILD_PREBUILT) - - -# Unit tests. -include $(CLEAR_VARS) - -LOCAL_MODULE := clatd_test -LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -# Bug: http://b/33566695 -LOCAL_CFLAGS += -Wno-address-of-packed-member -LOCAL_SRC_FILES := clatd_test.cpp checksum.c translate.c icmp.c ipv4.c ipv6.c logging.c config.c tun.c -LOCAL_MODULE_TAGS := eng tests -LOCAL_SHARED_LIBRARIES := liblog libnetutils - -include $(BUILD_NATIVE_TEST) - -# Microbenchmark. -include $(CLEAR_VARS) - -LOCAL_CLANG := true -LOCAL_MODULE := clatd_microbenchmark -LOCAL_CFLAGS := -Wall -Werror -Wunused-parameter -LOCAL_SRC_FILES := clatd_microbenchmark.c checksum.c tun.c -LOCAL_MODULE_TAGS := eng tests - -include $(BUILD_NATIVE_TEST) From 3850bee1d5dd1eae3c28ce09f8e4bd0c7099c5a8 Mon Sep 17 00:00:00 2001 From: junyulai Date: Tue, 6 Nov 2018 21:59:44 +0800 Subject: [PATCH 138/261] clatd: Add clang-format style config and pre-upload hook. Copied from system/netd, but with modifications to fit existing coding style. Bug: 118848635 Test: 1. build all 2. atest clatd_test 3. clang-format --style file -i *.{c,h,cpp} Change-Id: Iedc7cb149ad117615b1dcf9488fbf160294c9bb0 --- .clang-format | 11 +++++++++++ PREUPLOAD.cfg | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 .clang-format create mode 100644 PREUPLOAD.cfg diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..6682f77177 --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# Please keep this file in sync with system/core/.clang-format-4 +# +BasedOnStyle: Google +AlignConsecutiveAssignments: true +AlignEscapedNewlines: Right +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +TabWidth: 2 diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000000..c8dbf77fa1 --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,5 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp From c4e591aa04552541a8ae63af00541baa9046858b Mon Sep 17 00:00:00 2001 From: junyulai Date: Mon, 26 Nov 2018 22:36:10 +0900 Subject: [PATCH 139/261] Re-indent clatd code with clang-format clang-format --style file -i *.{c,h,cpp} Bug: 118848635 Test: 1. browse over ipv6-only network 2. atest clatd_test 3. clang-format --style file -i *.{c,h,cpp} Change-Id: I7389426101df7745370d0ac5c55176cd8fe2b08b --- checksum.c | 68 +++++------ checksum.h | 2 +- clatd.c | 209 ++++++++++++++++---------------- clatd.h | 14 ++- clatd_microbenchmark.c | 265 ++++++++++++++++++++-------------------- clatd_test.cpp | 236 ++++++++++++++++++------------------ config.c | 267 +++++++++++++++++++++-------------------- config.h | 4 +- dns64.c | 21 ++-- dump.c | 176 +++++++++++++-------------- dump.h | 26 ++-- getaddr.c | 54 ++++----- icmp.c | 18 ++- ipv4.c | 40 +++--- ipv6.c | 38 +++--- logging.c | 4 +- mtu.c | 14 +-- netlink_callbacks.c | 18 +-- netlink_msg.c | 47 ++++---- ring.c | 46 +++---- setif.c | 44 ++++--- setif.h | 3 +- translate.c | 150 +++++++++++------------ translate.h | 31 +++-- tun.c | 18 ++- 25 files changed, 907 insertions(+), 906 deletions(-) diff --git a/checksum.c b/checksum.c index 23a7c02fb1..11b51493a3 100644 --- a/checksum.c +++ b/checksum.c @@ -15,33 +15,33 @@ * * checksum.c - ipv4/ipv6 checksum calculation */ +#include #include #include -#include -#include -#include #include -#include +#include +#include +#include #include "checksum.h" /* function: ip_checksum_add * adds data to a checksum * current - the current checksum (or 0 to start a new checksum) - * data - the data to add to the checksum - * len - length of data + * data - the data to add to the checksum + * len - length of data */ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { - uint32_t checksum = current; - int left = len; + uint32_t checksum = current; + int left = len; const uint16_t *data_16 = data; - while(left > 1) { + while (left > 1) { checksum += *data_16; data_16++; left -= 2; } - if(left) { + if (left) { checksum += *(uint8_t *)data_16; } @@ -50,49 +50,48 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { /* function: ip_checksum_fold * folds a 32-bit partial checksum into 16 bits - * temp_sum - sum from ip_checksum_add - * returns: the folded checksum in network byte order + * temp_sum - sum from ip_checksum_add + * returns: the folded checksum in network byte order */ uint16_t ip_checksum_fold(uint32_t temp_sum) { - while(temp_sum > 0xffff) + while (temp_sum > 0xffff) { temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); - + } return temp_sum; } /* function: ip_checksum_finish * folds and closes the checksum - * temp_sum - sum from ip_checksum_add - * returns: a header checksum value in network byte order + * temp_sum - sum from ip_checksum_add + * returns: a header checksum value in network byte order */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { - return ~ip_checksum_fold(temp_sum); -} +uint16_t ip_checksum_finish(uint32_t temp_sum) { return ~ip_checksum_fold(temp_sum); } /* function: ip_checksum * combined ip_checksum_add and ip_checksum_finish - * data - data to checksum - * len - length of data + * 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); + temp_sum = ip_checksum_add(0, data, len); return ip_checksum_finish(temp_sum); } /* function: ipv6_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp/icmp headers - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) - * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments + * ip6 - the ipv6 header + * len - the transport length (transport header + payload) + * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments */ uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t) len); + checksum_len = htonl((uint32_t)len); checksum_next = htonl(protocol); uint32_t current = 0; + current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); @@ -103,16 +102,17 @@ uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, ui /* function: ipv4_pseudo_header_checksum * calculate the pseudo header checksum for use in tcp/udp headers - * ip - the ipv4 header - * len - the transport length (transport header + payload) + * ip - the ipv4 header + * len - the transport length (transport header + payload) */ uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { uint16_t temp_protocol, temp_length; temp_protocol = htons(ip->protocol); - temp_length = htons(len); + temp_length = htons(len); uint32_t current = 0; + current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); @@ -123,15 +123,15 @@ uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { /* function: ip_checksum_adjust * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums - * checksum - the header checksum in the original packet in network byte order - * old_hdr_sum - the pseudo-header checksum of the original packet - * new_hdr_sum - the pseudo-header checksum of the translated packet - * returns: the new header checksum in network byte order + * checksum - the header checksum in the original packet in network byte order + * old_hdr_sum - the pseudo-header checksum of the original packet + * new_hdr_sum - the pseudo-header checksum of the translated packet + * returns: the new header checksum in network byte order */ uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { // Algorithm suggested in RFC 1624. // http://tools.ietf.org/html/rfc1624#section-3 - checksum = ~checksum; + checksum = ~checksum; uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); uint16_t folded_old = ip_checksum_fold(old_hdr_sum); if (folded_sum > folded_old) { diff --git a/checksum.h b/checksum.h index d0af88ed33..669b42c505 100644 --- a/checksum.h +++ b/checksum.h @@ -18,9 +18,9 @@ #ifndef __CHECKSUM_H__ #define __CHECKSUM_H__ -#include #include #include +#include uint32_t ip_checksum_add(uint32_t current, const void *data, int len); uint16_t ip_checksum_finish(uint32_t temp_sum); diff --git a/clatd.c b/clatd.c index 168cc6989f..d8df0d815b 100644 --- a/clatd.c +++ b/clatd.c @@ -15,43 +15,43 @@ * * 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 +#include #include +#include +#include #include -#include "translate.h" #include "clatd.h" #include "config.h" -#include "logging.h" -#include "resolv_netid.h" -#include "setif.h" -#include "mtu.h" -#include "getaddr.h" #include "dump.h" -#include "tun.h" +#include "getaddr.h" +#include "logging.h" +#include "mtu.h" +#include "resolv_netid.h" #include "ring.h" +#include "setif.h" +#include "translate.h" +#include "tun.h" #define DEVICEPREFIX "v4-" @@ -63,13 +63,11 @@ volatile sig_atomic_t running = 1; /* function: stop_loop * signal handler: stop the event loop */ -void stop_loop() { - running = 0; -} +void stop_loop() { running = 0; } /* function: configure_packet_socket * Binds the packet socket and attaches the receive filter to it. - * sock - the socket to configure + * sock - the socket to configure */ int configure_packet_socket(int sock) { struct sockaddr_ll sll = { @@ -78,12 +76,14 @@ int configure_packet_socket(int sock) { .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. }; - if (bind(sock, (struct sockaddr *) &sll, sizeof(sll))) { + if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); return 0; } uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; + + // clang-format off struct sock_filter filter_code[] = { // Load the first four bytes of the IPv6 destination address (starts 24 bytes in). // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads @@ -99,12 +99,10 @@ int configure_packet_socket(int sock) { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1), BPF_STMT(BPF_RET | BPF_K, PACKETLEN), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - struct sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code + BPF_STMT(BPF_RET | BPF_K, 0), }; + // clang-format on + struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code }; if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) { logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno)); @@ -116,7 +114,7 @@ int configure_packet_socket(int sock) { /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface - * tunnel - tun device data + * tunnel - tun device data */ void configure_tun_ip(const struct tun_data *tunnel) { int status; @@ -130,7 +128,7 @@ void configure_tun_ip(const struct tun_data *tunnel) { in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet, Global_Clatd_Config.ipv4_local_prefixlen); if (localaddr == INADDR_NONE) { - logmsg(ANDROID_LOG_FATAL,"No free IPv4 address in %s/%d", + logmsg(ANDROID_LOG_FATAL, "No free IPv4 address in %s/%d", inet_ntoa(Global_Clatd_Config.ipv4_local_subnet), Global_Clatd_Config.ipv4_local_prefixlen); exit(1); @@ -139,10 +137,10 @@ void configure_tun_ip(const struct tun_data *tunnel) { // 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)); + 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); } @@ -150,8 +148,8 @@ void configure_tun_ip(const struct tun_data *tunnel) { inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr)); logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4); - if((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) { - logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(4) failed: %s",strerror(-status)); + 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); } } @@ -161,19 +159,19 @@ void configure_tun_ip(const struct tun_data *tunnel) { */ void drop_root() { gid_t groups[] = { AID_INET, AID_VPN }; - if(setgroups(sizeof(groups)/sizeof(groups[0]), groups) < 0) { - logmsg(ANDROID_LOG_FATAL,"drop_root/setgroups failed: %s",strerror(errno)); + 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)); + 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)); + if (setuid(AID_CLAT) < 0) { + logmsg(ANDROID_LOG_FATAL, "drop_root/setuid failed: %s", strerror(errno)); exit(1); } @@ -183,19 +181,19 @@ void drop_root() { memset(&cap, 0, sizeof(cap)); header.version = _LINUX_CAPABILITY_VERSION; - header.pid = 0; // 0 = change myself + 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)); + if (capset(&header, &cap) < 0) { + logmsg(ANDROID_LOG_FATAL, "drop_root/capset failed: %s", strerror(errno)); exit(1); } } /* function: open_sockets * opens a packet socket to receive IPv6 packets and a raw socket to send them - * tunnel - tun device data - * mark - the socket mark to use for the sending raw socket + * tunnel - tun device data + * mark - the socket mark to use for the sending raw socket */ void open_sockets(struct tun_data *tunnel, uint32_t mark) { int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); @@ -222,9 +220,9 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { /* function: update_clat_ipv6_address * picks the clat IPv6 address and configures packet translation to use it. - * tunnel - tun device data - * interface - uplink interface name - * returns: 1 on success, 0 on failure + * tunnel - tun device data + * interface - uplink interface name + * returns: 1 on success, 0 on failure */ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) { union anyip *interface_ip; @@ -266,9 +264,9 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac // Update our packet socket filter to reflect the new 464xlat IP address. if (!configure_packet_socket(tunnel->read_fd6)) { - // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because configure_packet_socket has already done so. - exit(1); + // Things aren't going to work. Bail out and hope we have better luck next time. + // We don't log an error here because configure_packet_socket has already done so. + exit(1); } return 1; @@ -276,41 +274,42 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac /* function: configure_interface * reads the configuration and applies it to the interface - * uplink_interface - network interface to use to reach the ipv6 internet - * plat_prefix - PLAT prefix to use - * tunnel - tun device data - * net_id - NetID to use, NETID_UNSET indicates use of default network + * uplink_interface - network interface to use to reach the ipv6 internet + * plat_prefix - PLAT prefix to use + * tunnel - tun device data + * net_id - NetID to use, NETID_UNSET indicates use of default network */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel, unsigned net_id) { +void configure_interface(const char *uplink_interface, const char *plat_prefix, + struct tun_data *tunnel, unsigned net_id) { int error; - if(!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { - logmsg(ANDROID_LOG_FATAL,"read_config failed"); + if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { + logmsg(ANDROID_LOG_FATAL, "read_config failed"); exit(1); } - if(Global_Clatd_Config.mtu > MAXMTU) { - logmsg(ANDROID_LOG_WARN,"Max MTU is %d, requested %d", MAXMTU, Global_Clatd_Config.mtu); + 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) { + 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); + 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); + if (Global_Clatd_Config.mtu < 1280) { + logmsg(ANDROID_LOG_WARN, "mtu too small = %d", Global_Clatd_Config.mtu); Global_Clatd_Config.mtu = 1280; } - if(Global_Clatd_Config.ipv4mtu <= 0 || - Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { + if (Global_Clatd_Config.ipv4mtu <= 0 || + Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; - logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu); + logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); } error = tun_alloc(tunnel->device4, tunnel->fd4); - if(error < 0) { - logmsg(ANDROID_LOG_FATAL,"tun_alloc/4 failed: %s",strerror(errno)); + if (error < 0) { + logmsg(ANDROID_LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno)); exit(1); } @@ -325,9 +324,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, /* function: read_packet * reads a packet from the tunnel fd and translates it - * read_fd - file descriptor to read original packet from - * write_fd - file descriptor to write translated packet to - * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 + * read_fd - file descriptor to read original packet from + * write_fd - file descriptor to write translated packet to + * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 */ void read_packet(int read_fd, int write_fd, int to_ipv6) { ssize_t readlen; @@ -335,20 +334,20 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { readlen = read(read_fd, buf, PACKETLEN); - if(readlen < 0) { + if (readlen < 0) { if (errno != EAGAIN) { - logmsg(ANDROID_LOG_WARN,"read_packet/read error: %s", strerror(errno)); + 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"); + } else if (readlen == 0) { + logmsg(ANDROID_LOG_WARN, "read_packet/tun interface removed"); running = 0; return; } - struct tun_pi *tun_header = (struct tun_pi *) buf; - if (readlen < (ssize_t) sizeof(*tun_header)) { - logmsg(ANDROID_LOG_WARN,"read_packet/short read: got %ld bytes", readlen); + struct tun_pi *tun_header = (struct tun_pi *)buf; + if (readlen < (ssize_t)sizeof(*tun_header)) { + logmsg(ANDROID_LOG_WARN, "read_packet/short read: got %ld bytes", readlen); return; } @@ -358,18 +357,18 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { return; } - if(tun_header->flags != 0) { + if (tun_header->flags != 0) { logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); } - packet = (uint8_t *) (tun_header + 1); + packet = (uint8_t *)(tun_header + 1); readlen -= sizeof(*tun_header); translate_packet(write_fd, to_ipv6, packet, readlen); } /* function: event_loop * reads packets from the tun network interface and passes them down the stack - * tunnel - tun device data + * tunnel - tun device data */ void event_loop(struct tun_data *tunnel) { time_t last_interface_poll; @@ -381,11 +380,10 @@ void event_loop(struct tun_data *tunnel) { // start the poll timer last_interface_poll = time(NULL); - while(running) { - if (poll(wait_fd, ARRAY_SIZE(wait_fd), - NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) { + while (running) { + if (poll(wait_fd, ARRAY_SIZE(wait_fd), NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) { if (errno != EINTR) { - logmsg(ANDROID_LOG_WARN,"event_loop/poll returned an error: %s", strerror(errno)); + logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno)); } } else { if (wait_fd[0].revents & POLLIN) { @@ -395,8 +393,7 @@ void event_loop(struct tun_data *tunnel) { if (wait_fd[0].revents & ~POLLIN) { // ring_read doesn't clear the error indication on the socket. recv(tunnel->read_fd6, NULL, 0, MSG_PEEK); - logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", - strerror(errno)); + logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", strerror(errno)); } // Call read_packet if the socket has data to be read, but also if an @@ -410,7 +407,7 @@ void event_loop(struct tun_data *tunnel) { } time_t now = time(NULL); - if(last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { + if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { update_clat_ipv6_address(tunnel, Global_Clatd_Config.default_pdp_interface); last_interface_poll = now; } @@ -430,13 +427,13 @@ void print_help() { /* function: parse_unsigned * parses a string as a decimal/hex/octal unsigned integer - * str - the string to parse - * out - the unsigned integer to write to, gets clobbered on failure + * str - the string to parse + * out - the unsigned integer to write to, gets clobbered on failure */ int parse_unsigned(const char *str, unsigned *out) { - char *end_ptr; - *out = strtoul(str, &end_ptr, 0); - return *str && !*end_ptr; + char *end_ptr; + *out = strtoul(str, &end_ptr, 0); + return *str && !*end_ptr; } /* function: main @@ -447,11 +444,11 @@ int main(int argc, char **argv) { int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; unsigned net_id = NETID_UNSET; - uint32_t mark = MARK_UNSET; + uint32_t mark = MARK_UNSET; unsigned len; - while((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { - switch(opt) { + while ((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { + switch (opt) { case 'i': uplink_interface = optarg; break; @@ -468,12 +465,12 @@ int main(int argc, char **argv) { print_help(); exit(0); default: - logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char) optopt); + logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt); exit(1); } } - if(uplink_interface == NULL) { + if (uplink_interface == NULL) { logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); exit(1); } @@ -494,10 +491,8 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", - CLATD_VERSION, uplink_interface, - net_id_str ? net_id_str : "(none)", - mark_str ? mark_str : "(none)"); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, + uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); // open our raw sockets before dropping privs open_sockets(&tunnel, mark); @@ -507,7 +502,7 @@ int main(int argc, char **argv) { // we can create tun devices as non-root because we're in the VPN group. tunnel.fd4 = tun_open(); - if(tunnel.fd4 < 0) { + if (tunnel.fd4 < 0) { logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); exit(1); } @@ -522,14 +517,14 @@ int main(int argc, char **argv) { update_clat_ipv6_address(&tunnel, uplink_interface); // Loop until someone sends us a signal or brings down the tun interface. - if(signal(SIGTERM, stop_loop) == SIG_ERR) { + if (signal(SIGTERM, stop_loop) == SIG_ERR) { logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); exit(1); } event_loop(&tunnel); - logmsg(ANDROID_LOG_INFO,"Shutting down clat on %s", uplink_interface); + logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); return 0; diff --git a/clatd.h b/clatd.h index f421f46bb4..327a948d9c 100644 --- a/clatd.h +++ b/clatd.h @@ -21,7 +21,7 @@ #include #define MAXMTU 1500 -#define PACKETLEN (MAXMTU+sizeof(struct tun_pi)) +#define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) #define CLATD_VERSION "1.4" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -37,9 +37,15 @@ // specific parts of the packet. The packet_* functions operate on all the packet segments past a // given position. typedef enum { - CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, CLAT_POS_MAX + CLAT_POS_TUNHDR, + CLAT_POS_IPHDR, + CLAT_POS_FRAGHDR, + CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, + CLAT_POS_ICMPERR_FRAGHDR, + CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, + CLAT_POS_MAX } clat_packet_index; typedef struct iovec clat_packet[CLAT_POS_MAX]; diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index fed310066a..83be006d76 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -21,25 +21,25 @@ * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark * */ +#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 +#include #include "checksum.h" #include "tun.h" @@ -52,163 +52,162 @@ #define SEC_TO_NANOSEC (1000 * 1000 * 1000) void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) { - sin->sin_family = AF_INET; - sin->sin_port = 0; - sin->sin_addr.s_addr = inet_addr(addr); + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = inet_addr(addr); } void die(const char *str) { - perror(str); - exit(1); + perror(str); + exit(1); } int setup_tun() { - int fd = tun_open(); - if (fd == -1) die("tun_open"); + int fd = tun_open(); + if (fd == -1) die("tun_open"); - char dev[IFNAMSIZ] = DEVICENAME; - int ret = tun_alloc(dev, fd); - if (ret == -1) die("tun_alloc"); - struct ifreq ifr = { - .ifr_name = DEVICENAME, - }; + char dev[IFNAMSIZ] = DEVICENAME; + int ret = tun_alloc(dev, fd); + if (ret == -1) die("tun_alloc"); + struct ifreq ifr = { + .ifr_name = DEVICENAME, + }; - int s = socket(AF_INET, SOCK_DGRAM, 0); - init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "192.0.0.4"); - if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); - init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "255.255.255.248"); - if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); - if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); - ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); - if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); - return fd; + int s = socket(AF_INET, SOCK_DGRAM, 0); + init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "192.0.0.4"); + if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); + init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "255.255.255.248"); + if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); + if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); + return fd; } int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) { - struct tun_pi tun = { 0, htons(ETH_P_IP) }; - struct udphdr udp = { - .source = htons(1234), - .dest = htons(PORT), - .len = htons(len + sizeof(udp)), - .check = 0, - }; - struct iphdr ip = { - .version = 4, - .ihl = 5, - .tot_len = htons(len + sizeof(ip) + sizeof(udp)), - .frag_off = htons(IP_DF), - .ttl = 55, - .protocol = IPPROTO_UDP, - .saddr = htonl(0xc0000006), // 192.0.0.6 - .daddr = htonl(0xc0000004), // 192.0.0.4 - }; - clat_packet out = { - { &tun, sizeof(tun) }, // tun header - { &ip, sizeof(ip) }, // IP header - { NULL, 0 }, // Fragment header - { &udp, sizeof(udp) }, // Transport header - { NULL, 0 }, // ICMP error IP header - { NULL, 0 }, // ICMP error fragment header - { NULL, 0 }, // ICMP error transport header - { payload, len }, // Payload - }; + struct tun_pi tun = { 0, htons(ETH_P_IP) }; + struct udphdr udp = { + .source = htons(1234), + .dest = htons(PORT), + .len = htons(len + sizeof(udp)), + .check = 0, + }; + struct iphdr ip = { + .version = 4, + .ihl = 5, + .tot_len = htons(len + sizeof(ip) + sizeof(udp)), + .frag_off = htons(IP_DF), + .ttl = 55, + .protocol = IPPROTO_UDP, + .saddr = htonl(0xc0000006), // 192.0.0.6 + .daddr = htonl(0xc0000004), // 192.0.0.4 + }; + clat_packet out = { + { &tun, sizeof(tun) }, // tun header + { &ip, sizeof(ip) }, // IP header + { NULL, 0 }, // Fragment header + { &udp, sizeof(udp) }, // Transport header + { NULL, 0 }, // ICMP error IP header + { NULL, 0 }, // ICMP error fragment header + { NULL, 0 }, // ICMP error transport header + { payload, len }, // Payload + }; - ip.check = ip_checksum(&ip, sizeof(ip)); + ip.check = ip_checksum(&ip, sizeof(ip)); - uint32_t sum; - sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); - sum = ip_checksum_add(sum, &udp, sizeof(udp)); - sum += payload_checksum; - udp.check = ip_checksum_finish(sum); + uint32_t sum; + sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); + sum = ip_checksum_add(sum, &udp, sizeof(udp)); + sum += payload_checksum; + udp.check = ip_checksum_finish(sum); - return send_tun(fd, out, sizeof(out) / sizeof(out[0])); + return send_tun(fd, out, sizeof(out) / sizeof(out[0])); } double timedelta(const struct timespec tv1, const struct timespec tv2) { - struct timespec end = tv2; - if (end.tv_nsec < tv1.tv_nsec) { - end.tv_sec -= 1; - end.tv_nsec += SEC_TO_NANOSEC; - } - double seconds = (end.tv_sec - tv1.tv_sec); - seconds += (((double) (end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); - return seconds; + struct timespec end = tv2; + if (end.tv_nsec < tv1.tv_nsec) { + end.tv_sec -= 1; + end.tv_nsec += SEC_TO_NANOSEC; + } + double seconds = (end.tv_sec - tv1.tv_sec); + seconds += (((double)(end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); + return seconds; } -void benchmark(const char *name, int fd, int s, int num, int do_read, - uint8_t payload[], int len, uint32_t payload_sum) { - int i; - char buf[4096]; - struct timespec tv1, tv2; - int write_err = 0, read_err = 0; - clock_gettime(CLOCK_MONOTONIC, &tv1); - for (i = 0; i < num; i++) { - if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; - if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { - read_err++; - if (errno == ETIMEDOUT) { - printf("Timed out after %d packets!\n", i); - break; - } - } +void benchmark(const char *name, int fd, int s, int num, int do_read, uint8_t payload[], int len, + uint32_t payload_sum) { + int i; + char buf[4096]; + struct timespec tv1, tv2; + int write_err = 0, read_err = 0; + clock_gettime(CLOCK_MONOTONIC, &tv1); + for (i = 0; i < num; i++) { + if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; + if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { + read_err++; + if (errno == ETIMEDOUT) { + printf("Timed out after %d packets!\n", i); + break; + } } - clock_gettime(CLOCK_MONOTONIC, &tv2); - double seconds = timedelta(tv1, tv2); - int pps = (int) (i / seconds); - double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); - printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); - printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", - read_err, (float) read_err / i * 100, - write_err, (float) write_err / i * 100); + } + clock_gettime(CLOCK_MONOTONIC, &tv2); + double seconds = timedelta(tv1, tv2); + int pps = (int)(i / seconds); + double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); + printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); + printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", read_err, (float)read_err / i * 100, + write_err, (float)write_err / i * 100); } int open_socket() { - int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); - int on = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); - struct timeval tv = { 1, 0 }; - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); + struct timeval tv = { 1, 0 }; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_port = ntohs(PORT), - .sin_addr = { INADDR_ANY } - }; - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) die ("bind"); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = ntohs(PORT), + .sin_addr = { INADDR_ANY }, + }; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) die("bind"); - return sock; + return sock; } int main() { - int fd = setup_tun(); - int sock = open_socket(); + int fd = setup_tun(); + int sock = open_socket(); - int i; - uint8_t payload[PAYLOADSIZE]; - for (i = 0; i < (int) sizeof(payload); i++) { - payload[i] = (uint8_t) i; - } - uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); + int i; + uint8_t payload[PAYLOADSIZE]; + for (i = 0; i < (int)sizeof(payload); i++) { + payload[i] = (uint8_t)i; + } + uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); - // Check things are working. - char buf[4096]; - if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); - if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); + // Check things are working. + char buf[4096]; + if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); + if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); - benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); - close(fd); + benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); - fd = setup_tun(); - set_nonblocking(fd); - benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); - close(fd); + fd = setup_tun(); + set_nonblocking(fd); + benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); + close(fd); - fd = setup_tun(); - set_nonblocking(fd); - benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); - close(fd); + fd = setup_tun(); + set_nonblocking(fd); + benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); + close(fd); - return 0; + return 0; } diff --git a/clatd_test.cpp b/clatd_test.cpp index b901c30d2c..2a10101f89 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -18,28 +18,29 @@ #include -#include #include #include +#include #include #include extern "C" { #include "checksum.h" -#include "translate.h" -#include "config.h" #include "clatd.h" +#include "config.h" +#include "translate.h" } // For convenience. #define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0]) // Default translation parameters. -static const char kIPv4LocalAddr[] = "192.0.0.4"; -static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464"; +static const char kIPv4LocalAddr[] = "192.0.0.4"; +static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464"; static const char kIPv6PlatSubnet[] = "64:ff9b::"; +// clang-format off // Test packet portions. Defined as macros because it's easy to concatenate them to make packets. #define IPV4_HEADER(p, c1, c2) \ 0x45, 0x00, 0, 41, /* Version=4, IHL=5, ToS=0x80, len=41 */ \ @@ -164,6 +165,7 @@ static const uint8_t kReassembledIPv4[] = { 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; +// clang-format on // Expected checksums. static const uint32_t kUdpPartialChecksum = 0xd5c8; @@ -185,9 +187,9 @@ int is_ipv6_fragment(struct ip6_hdr *ip6, size_t len) { if (ip6->ip6_nxt != IPPROTO_FRAGMENT) { return 0; } - struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1); return len >= sizeof(*ip6) + sizeof(*frag) && - (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG)); + (frag->ip6f_offlg & (IP6F_OFF_MASK | IP6F_MORE_FRAG)); } int ipv4_fragment_offset(struct iphdr *ip) { @@ -200,50 +202,50 @@ int ipv6_fragment_offset(struct ip6_frag *frag) { void check_packet(const uint8_t *packet, size_t len, const char *msg) { void *payload; - size_t payload_length = 0; + size_t payload_length = 0; uint32_t pseudo_checksum = 0; - uint8_t protocol = 0; - int version = ip_version(packet); + uint8_t protocol = 0; + int version = ip_version(packet); switch (version) { case 4: { - struct iphdr *ip = (struct iphdr *) packet; + struct iphdr *ip = (struct iphdr *)packet; ASSERT_GE(len, sizeof(*ip)) << msg << ": IPv4 packet shorter than IPv4 header\n"; EXPECT_EQ(5, ip->ihl) << msg << ": Unsupported IP header length\n"; EXPECT_EQ(len, ntohs(ip->tot_len)) << msg << ": Incorrect IPv4 length\n"; EXPECT_EQ(0, ip_checksum(ip, sizeof(*ip))) << msg << ": Incorrect IP checksum\n"; protocol = ip->protocol; - payload = ip + 1; + payload = ip + 1; if (!is_ipv4_fragment(ip)) { - payload_length = len - sizeof(*ip); + payload_length = len - sizeof(*ip); pseudo_checksum = ipv4_pseudo_header_checksum(ip, payload_length); } ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMP) - << msg << ": Unsupported IPv4 protocol " << protocol << "\n"; + << msg << ": Unsupported IPv4 protocol " << protocol << "\n"; break; } case 6: { - struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; + struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; ASSERT_GE(len, sizeof(*ip6)) << msg << ": IPv6 packet shorter than IPv6 header\n"; EXPECT_EQ(len - sizeof(*ip6), htons(ip6->ip6_plen)) << msg << ": Incorrect IPv6 length\n"; if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { - struct ip6_frag *frag = (struct ip6_frag *) (ip6 + 1); + struct ip6_frag *frag = (struct ip6_frag *)(ip6 + 1); ASSERT_GE(len, sizeof(*ip6) + sizeof(*frag)) - << msg << ": IPv6 fragment: short fragment header\n"; + << msg << ": IPv6 fragment: short fragment header\n"; protocol = frag->ip6f_nxt; - payload = frag + 1; + payload = frag + 1; // Even though the packet has a Fragment header, it might not be a fragment. if (!is_ipv6_fragment(ip6, len)) { payload_length = len - sizeof(*ip6) - sizeof(*frag); } } else { // Since there are no extension headers except Fragment, this must be the payload. - protocol = ip6->ip6_nxt; - payload = ip6 + 1; + protocol = ip6->ip6_nxt; + payload = ip6 + 1; payload_length = len - sizeof(*ip6); } ASSERT_TRUE(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_ICMPV6) - << msg << ": Unsupported IPv6 next header " << protocol; + << msg << ": Unsupported IPv6 next header " << protocol; if (payload_length) { pseudo_checksum = ipv6_pseudo_header_checksum(ip6, payload_length, protocol); } @@ -257,7 +259,7 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) { // If we understand the payload, verify the checksum. if (payload_length) { uint16_t checksum; - switch(protocol) { + switch (protocol) { case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_ICMPV6: @@ -274,7 +276,7 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) { } if (protocol == IPPROTO_UDP) { - struct udphdr *udp = (struct udphdr *) payload; + struct udphdr *udp = (struct udphdr *)payload; EXPECT_NE(0, udp->check) << msg << ": UDP checksum 0 should be 0xffff"; // If this is not a fragment, check the UDP length field. if (payload_length) { @@ -285,15 +287,15 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) { void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets, uint8_t *reassembled, size_t *reassembled_len, const char *msg) { - struct iphdr *ip = NULL; + struct iphdr *ip = NULL; struct ip6_hdr *ip6 = NULL; - size_t total_length, pos = 0; + size_t total_length, pos = 0; uint8_t protocol = 0; - uint8_t version = ip_version(fragments[0]); + uint8_t version = ip_version(fragments[0]); for (int i = 0; i < numpackets; i++) { const uint8_t *packet = fragments[i]; - int len = lengths[i]; + int len = lengths[i]; int headersize, payload_offset; ASSERT_EQ(ip_version(packet), version) << msg << ": Inconsistent fragment versions\n"; @@ -301,32 +303,32 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu switch (version) { case 4: { - struct iphdr *ip_orig = (struct iphdr *) packet; - headersize = sizeof(*ip_orig); + struct iphdr *ip_orig = (struct iphdr *)packet; + headersize = sizeof(*ip_orig); ASSERT_TRUE(is_ipv4_fragment(ip_orig)) - << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n"; - ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip): 0)) - << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n"; + << msg << ": IPv4 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv4_fragment_offset(ip_orig) * 8 + ((i != 0) ? sizeof(*ip) : 0)) + << msg << ": IPv4 fragment #" << i + 1 << ": inconsistent offset\n"; - headersize = sizeof(*ip_orig); + headersize = sizeof(*ip_orig); payload_offset = headersize; if (pos == 0) { - ip = (struct iphdr *) reassembled; + ip = (struct iphdr *)reassembled; } break; } case 6: { - struct ip6_hdr *ip6_orig = (struct ip6_hdr *) packet; - struct ip6_frag *frag = (struct ip6_frag *) (ip6_orig + 1); + struct ip6_hdr *ip6_orig = (struct ip6_hdr *)packet; + struct ip6_frag *frag = (struct ip6_frag *)(ip6_orig + 1); ASSERT_TRUE(is_ipv6_fragment(ip6_orig, len)) - << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n"; - ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6): 0)) - << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n"; + << msg << ": IPv6 fragment #" << i + 1 << " not a fragment\n"; + ASSERT_EQ(pos, ipv6_fragment_offset(frag) * 8 + ((i != 0) ? sizeof(*ip6) : 0)) + << msg << ": IPv6 fragment #" << i + 1 << ": inconsistent offset\n"; - headersize = sizeof(*ip6_orig); + headersize = sizeof(*ip6_orig); payload_offset = sizeof(*ip6_orig) + sizeof(*frag); if (pos == 0) { - ip6 = (struct ip6_hdr *) reassembled; + ip6 = (struct ip6_hdr *)reassembled; protocol = frag->ip6f_nxt; } break; @@ -337,7 +339,7 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu // If this is the first fragment, copy the header. if (pos == 0) { - ASSERT_LT(headersize, (int) *reassembled_len) << msg << ": Reassembly buffer too small\n"; + ASSERT_LT(headersize, (int)*reassembled_len) << msg << ": Reassembly buffer too small\n"; memcpy(reassembled, packet, headersize); total_length = headersize; pos += headersize; @@ -351,21 +353,20 @@ void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int nu pos += payload_length; } - // Fix up the reassembled headers to reflect fragmentation and length (and IPv4 checksum). ASSERT_EQ(total_length, pos) << msg << ": Reassembled packet length incorrect\n"; if (ip) { ip->frag_off &= ~htons(IP_MF); ip->tot_len = htons(total_length); - ip->check = 0; - ip->check = ip_checksum(ip, sizeof(*ip)); + ip->check = 0; + ip->check = ip_checksum(ip, sizeof(*ip)); ASSERT_FALSE(is_ipv4_fragment(ip)) << msg << ": reassembled IPv4 packet is a fragment!\n"; } if (ip6) { - ip6->ip6_nxt = protocol; + ip6->ip6_nxt = protocol; ip6->ip6_plen = htons(total_length - sizeof(*ip6)); ASSERT_FALSE(is_ipv6_fragment(ip6, ip6->ip6_plen)) - << msg << ": reassembled IPv6 packet is a fragment!\n"; + << msg << ": reassembled IPv6 packet is a fragment!\n"; } *reassembled_len = total_length; @@ -383,8 +384,8 @@ void check_data_matches(const void *expected, const void *actual, size_t len, co snprintf(actual_hexdump + pos, hexdump_len - pos, "\n "); pos += 4; } - snprintf(expected_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *) expected)[i]); - snprintf(actual_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *) actual)[i]); + snprintf(expected_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)expected)[i]); + snprintf(actual_hexdump + pos, hexdump_len - pos, " %02x", ((uint8_t *)actual)[i]); pos += 3; } FAIL() << msg << ": Data doesn't match" @@ -393,27 +394,27 @@ void check_data_matches(const void *expected, const void *actual, size_t len, co } } -void fix_udp_checksum(uint8_t* packet) { +void fix_udp_checksum(uint8_t *packet) { uint32_t pseudo_checksum; uint8_t version = ip_version(packet); struct udphdr *udp; switch (version) { case 4: { - struct iphdr *ip = (struct iphdr *) packet; - udp = (struct udphdr *) (ip + 1); - pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len)); + struct iphdr *ip = (struct iphdr *)packet; + udp = (struct udphdr *)(ip + 1); + pseudo_checksum = ipv4_pseudo_header_checksum(ip, ntohs(udp->len)); break; } case 6: { - struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; - udp = (struct udphdr *) (ip6 + 1); - pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP); + struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; + udp = (struct udphdr *)(ip6 + 1); + pseudo_checksum = ipv6_pseudo_header_checksum(ip6, ntohs(udp->len), IPPROTO_UDP); break; } default: FAIL() << "unsupported IP version" << version << "\n"; return; - } + } udp->check = 0; udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len))); @@ -422,9 +423,7 @@ void fix_udp_checksum(uint8_t* packet) { // Testing stub for send_rawv6. The real version uses sendmsg() with a // destination IPv6 address, and attempting to call that on our test socketpair // fd results in EINVAL. -extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { - writev(fd, out, iov_len); -} +extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) { writev(fd, out, iov_len); } void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen, const char *msg) { @@ -443,13 +442,13 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * switch (version) { case 4: expected_proto = htons(ETH_P_IPV6); - read_fd = fds[1]; - write_fd = fds[0]; + read_fd = fds[1]; + write_fd = fds[0]; break; case 6: expected_proto = htons(ETH_P_IP); - read_fd = fds[0]; - write_fd = fds[1]; + read_fd = fds[0]; + write_fd = fds[1]; break; default: FAIL() << msg << ": Unsupported IP version " << version << "\n"; @@ -464,11 +463,12 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * struct tun_pi new_tun_header; struct iovec iov[] = { { &new_tun_header, sizeof(new_tun_header) }, - { out, *outlen } + { out, *outlen }, }; + int len = readv(read_fd, iov, 2); - if (len > (int) sizeof(new_tun_header)) { - ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n"; + if (len > (int)sizeof(new_tun_header)) { + ASSERT_LT((size_t)len, *outlen) << msg << ": Translated packet buffer too small\n"; EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n"; *outlen = len - sizeof(new_tun_header); check_packet(out, *outlen, msg); @@ -483,8 +483,8 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * } } -void check_translated_packet(const uint8_t *original, size_t original_len, - const uint8_t *expected, size_t expected_len, const char *msg) { +void check_translated_packet(const uint8_t *original, size_t original_len, const uint8_t *expected, + size_t expected_len, const char *msg) { uint8_t translated[MAXMTU]; size_t translated_len = sizeof(translated); do_translate_packet(original, original_len, translated, &translated_len, msg); @@ -499,8 +499,8 @@ void check_fragment_translation(const uint8_t *original[], const size_t original // Check that each of the fragments translates as expected. char frag_msg[512]; snprintf(frag_msg, sizeof(frag_msg), "%s: fragment #%d", msg, i + 1); - check_translated_packet(original[i], original_lengths[i], - expected[i], expected_lengths[i], frag_msg); + check_translated_packet(original[i], original_lengths[i], expected[i], expected_lengths[i], + frag_msg); } // Sanity check that reassembling the original and translated fragments produces valid packets. @@ -524,17 +524,17 @@ int get_transport_checksum(const uint8_t *packet) { int version = ip_version(packet); switch (version) { case 4: - ip = (struct iphdr *) packet; + ip = (struct iphdr *)packet; if (is_ipv4_fragment(ip)) { - return -1; + return -1; } protocol = ip->protocol; - payload = ip + 1; + payload = ip + 1; break; case 6: - ip6 = (struct ip6_hdr *) packet; + ip6 = (struct ip6_hdr *)packet; protocol = ip6->ip6_nxt; - payload = ip6 + 1; + payload = ip6 + 1; break; default: return -1; @@ -542,10 +542,10 @@ int get_transport_checksum(const uint8_t *packet) { switch (protocol) { case IPPROTO_UDP: - return ((struct udphdr *) payload)->check; + return ((struct udphdr *)payload)->check; case IPPROTO_TCP: - return ((struct tcphdr *) payload)->check; + return ((struct tcphdr *)payload)->check; case IPPROTO_FRAGMENT: default: @@ -561,7 +561,7 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); - Global_Clatd_Config.ipv6_host_id = in6addr_any; + Global_Clatd_Config.ipv6_host_id = in6addr_any; Global_Clatd_Config.use_dynamic_iid = 1; } }; @@ -597,7 +597,7 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { int count_onebits(const void *data, size_t size) { int onebits = 0; for (size_t pos = 0; pos < size; pos++) { - uint8_t *byte = ((uint8_t*) data) + pos; + uint8_t *byte = ((uint8_t *)data) + pos; for (int shift = 0; shift < 8; shift++) { onebits += (*byte >> shift) & 1; } @@ -611,7 +611,7 @@ TEST_F(ClatdTest, TestCountOnebits) { ASSERT_EQ(1, count_onebits(&i, sizeof(i))); i <<= 61; ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i |= ((uint64_t) 1 << 33); + i |= ((uint64_t)1 << 33); ASSERT_EQ(2, count_onebits(&i, sizeof(i))); i = 0xf1000202020000f0; ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i))); @@ -637,10 +637,10 @@ TEST_F(ClatdTest, TestGenIIDRandom) { Global_Clatd_Config.ipv6_host_id = in6addr_any; // Generate a boatload of random IIDs. - int onebits = 0; + int onebits = 0; uint64_t prev_iid = 0; for (int i = 0; i < 100000; i++) { - struct in6_addr myaddr = interface_ipv6; + struct in6_addr myaddr = interface_ipv6; config_generate_local_ipv6_subnet(&myaddr); @@ -648,7 +648,7 @@ TEST_F(ClatdTest, TestGenIIDRandom) { EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr)); // Check that consecutive IIDs are not the same. - uint64_t iid = * (uint64_t*) (&myaddr.s6_addr[8]); + uint64_t iid = *(uint64_t *)(&myaddr.s6_addr[8]); ASSERT_TRUE(iid != prev_iid) << "Two consecutive random IIDs are the same: " << std::showbase << std::hex @@ -657,7 +657,7 @@ TEST_F(ClatdTest, TestGenIIDRandom) { // Check that the IID is checksum-neutral with the NAT64 prefix and the // local prefix. - struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; + struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet; uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr))); @@ -732,7 +732,7 @@ TEST_F(ClatdTest, SelectIPv4Address) { // Now try using the real function which sees if IP addresses are free using bind(). // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - addr.s_addr = inet_addr("8.8.8.8"); + addr.s_addr = inet_addr("8.8.8.8"); EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29)); addr.s_addr = inet_addr("127.0.0.1"); @@ -753,13 +753,13 @@ TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks check_packet. struct udphdr *udp; uint8_t v4_udp_packet[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; - udp = (struct udphdr *) (v4_udp_packet + sizeof(struct iphdr)); + udp = (struct udphdr *)(v4_udp_packet + sizeof(struct iphdr)); fix_udp_checksum(v4_udp_packet); ASSERT_EQ(kUdpV4Checksum, udp->check) << "UDP/IPv4 packet checksum sanity check\n"; check_packet(v4_udp_packet, sizeof(v4_udp_packet), "UDP/IPv4 packet sanity check"); uint8_t v6_udp_packet[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; - udp = (struct udphdr *) (v6_udp_packet + sizeof(struct ip6_hdr)); + udp = (struct udphdr *)(v6_udp_packet + sizeof(struct ip6_hdr)); fix_udp_checksum(v6_udp_packet); ASSERT_EQ(kUdpV6Checksum, udp->check) << "UDP/IPv6 packet checksum sanity check\n"; check_packet(v6_udp_packet, sizeof(v6_udp_packet), "UDP/IPv6 packet sanity check"); @@ -773,53 +773,53 @@ TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks reassemble_packet. uint8_t reassembled[MAXMTU]; size_t total_length = sizeof(reassembled); - reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), - reassembled, &total_length, "Reassembly sanity check"); + reassemble_packet(kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv4Fragments), reassembled, + &total_length, "Reassembly sanity check"); check_packet(reassembled, total_length, "IPv4 Reassembled packet is valid"); ASSERT_EQ(sizeof(kReassembledIPv4), total_length) << "IPv4 reassembly sanity check: length\n"; - ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *) reassembled)) - << "Sanity check: reassembled packet is a fragment!\n"; + ASSERT_TRUE(!is_ipv4_fragment((struct iphdr *)reassembled)) + << "Sanity check: reassembled packet is a fragment!\n"; check_data_matches(kReassembledIPv4, reassembled, total_length, "IPv4 reassembly sanity check"); total_length = sizeof(reassembled); - reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), - reassembled, &total_length, "IPv6 reassembly sanity check"); - ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *) reassembled, total_length)) - << "Sanity check: reassembled packet is a fragment!\n"; + reassemble_packet(kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv6Fragments), reassembled, + &total_length, "IPv6 reassembly sanity check"); + ASSERT_TRUE(!is_ipv6_fragment((struct ip6_hdr *)reassembled, total_length)) + << "Sanity check: reassembled packet is a fragment!\n"; check_packet(reassembled, total_length, "IPv6 Reassembled packet is valid"); } TEST_F(ClatdTest, PseudoChecksum) { uint32_t pseudo_checksum; - uint8_t v4_header[] = { IPV4_UDP_HEADER }; + uint8_t v4_header[] = { IPV4_UDP_HEADER }; uint8_t v4_pseudo_header[] = { IPV4_PSEUDOHEADER(v4_header, UDP_LEN) }; - pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *) v4_header, UDP_LEN); + pseudo_checksum = ipv4_pseudo_header_checksum((struct iphdr *)v4_header, UDP_LEN); EXPECT_EQ(ip_checksum_finish(pseudo_checksum), ip_checksum(v4_pseudo_header, sizeof(v4_pseudo_header))) - << "ipv4_pseudo_header_checksum incorrect\n"; + << "ipv4_pseudo_header_checksum incorrect\n"; - uint8_t v6_header[] = { IPV6_UDP_HEADER }; + uint8_t v6_header[] = { IPV6_UDP_HEADER }; uint8_t v6_pseudo_header[] = { IPV6_PSEUDOHEADER(v6_header, IPPROTO_UDP, UDP_LEN) }; - pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *) v6_header, UDP_LEN, IPPROTO_UDP); + pseudo_checksum = ipv6_pseudo_header_checksum((struct ip6_hdr *)v6_header, UDP_LEN, IPPROTO_UDP); EXPECT_EQ(ip_checksum_finish(pseudo_checksum), ip_checksum(v6_pseudo_header, sizeof(v6_pseudo_header))) - << "ipv6_pseudo_header_checksum incorrect\n"; + << "ipv6_pseudo_header_checksum incorrect\n"; } TEST_F(ClatdTest, TransportChecksum) { - uint8_t udphdr[] = { UDP_HEADER }; + uint8_t udphdr[] = { UDP_HEADER }; uint8_t payload[] = { PAYLOAD }; EXPECT_EQ(kUdpPartialChecksum, ip_checksum_add(0, udphdr, sizeof(udphdr))) - << "UDP partial checksum\n"; + << "UDP partial checksum\n"; EXPECT_EQ(kPayloadPartialChecksum, ip_checksum_add(0, payload, sizeof(payload))) - << "Payload partial checksum\n"; + << "Payload partial checksum\n"; - uint8_t ip[] = { IPV4_UDP_HEADER }; - uint8_t ip6[] = { IPV6_UDP_HEADER }; - uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *) ip, UDP_LEN); - uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *) ip6, UDP_LEN, - IPPROTO_UDP); + uint8_t ip[] = { IPV4_UDP_HEADER }; + uint8_t ip6[] = { IPV6_UDP_HEADER }; + uint32_t ipv4_pseudo_sum = ipv4_pseudo_header_checksum((struct iphdr *)ip, UDP_LEN); + uint32_t ipv6_pseudo_sum = + ipv6_pseudo_header_checksum((struct ip6_hdr *)ip6, UDP_LEN, IPPROTO_UDP); EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; EXPECT_EQ(0x2644bU, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; @@ -886,12 +886,10 @@ TEST_F(ClatdTest, Translate) { } TEST_F(ClatdTest, Fragmentation) { - check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, - kIPv6Fragments, kIPv6FragLengths, + check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation"); - check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, - kIPv4Fragments, kIPv4FragLengths, + check_fragment_translation(kIPv6Fragments, kIPv6FragLengths, kIPv4Fragments, kIPv4FragLengths, ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); } @@ -902,12 +900,12 @@ void check_translate_checksum_neutral(const uint8_t *original, size_t original_l do_translate_packet(original, original_len, translated, &translated_len, msg); EXPECT_EQ(expected_len, translated_len) << msg << ": Translated packet length incorrect\n"; // do_translate_packet already checks packets for validity and verifies the checksum. - int original_check = get_transport_checksum(original); + int original_check = get_transport_checksum(original); int translated_check = get_transport_checksum(translated); ASSERT_NE(-1, original_check); ASSERT_NE(-1, translated_check); ASSERT_EQ(original_check, translated_check) - << "Not checksum neutral: original and translated checksums differ\n"; + << "Not checksum neutral: original and translated checksums differ\n"; } TEST_F(ClatdTest, TranslateChecksumNeutral) { @@ -916,8 +914,8 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); - ASSERT_NE((uint32_t) 0x00000464, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); - ASSERT_NE((uint32_t) 0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + ASSERT_NE((uint32_t)0x00000464, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); // Check that translating UDP packets is checksum-neutral. First, IPv4. uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; @@ -928,7 +926,7 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { // Now try IPv6. uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; // The test packet uses the static IID, not the random IID. Fix up the source address. - struct ip6_hdr *ip6 = (struct ip6_hdr *) udp_ipv6; + struct ip6_hdr *ip6 = (struct ip6_hdr *)udp_ipv6; memcpy(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet, sizeof(ip6->ip6_src)); fix_udp_checksum(udp_ipv6); check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20, diff --git a/config.c b/config.c index 2d7085bb63..91a65fbe20 100644 --- a/config.c +++ b/config.c @@ -16,75 +16,80 @@ * config.c - configuration settings */ -#include -#include #include -#include -#include #include +#include +#include +#include +#include #include #include #include +#include "checksum.h" +#include "clatd.h" #include "config.h" #include "dns64.h" -#include "logging.h" #include "getaddr.h" -#include "clatd.h" -#include "checksum.h" +#include "logging.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 + * 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); + 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 + * 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) { +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); + 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)); + 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); + 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); + 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); + 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; @@ -92,24 +97,26 @@ int16_t *config_item_int16_t(cnode *root, const char *item_name, const char *def } /* 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 + * 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) { +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); + 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); + if (status <= 0) { + logmsg(ANDROID_LOG_FATAL, "invalid IPv4 address specified for %s: %s", item_name, tmp); return NULL; } @@ -117,24 +124,26 @@ struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *d } /* 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 + * 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) { +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); + 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); + if (status <= 0) { + logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for %s: %s", item_name, tmp); return NULL; } @@ -145,7 +154,7 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char * frees the memory used by the global config variable */ void free_config() { - if(Global_Clatd_Config.plat_from_dns64_hostname) { + if (Global_Clatd_Config.plat_from_dns64_hostname) { free(Global_Clatd_Config.plat_from_dns64_hostname); Global_Clatd_Config.plat_from_dns64_hostname = NULL; } @@ -153,17 +162,16 @@ void free_config() { /* function: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. - * a1 - first address - * a2 - second address - * returns: 0 if the subnets are different, 1 if they are the same. + * a1 - first address + * a2 - second address + * returns: 0 if the subnets are different, 1 if they are the same. */ -int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { - return !memcmp(a1, a2, 8); -} +int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } /* function: dns64_detection - * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network + * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response + * with a query backoff timer + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ void dns64_detection(unsigned net_id) { int backoff_sleep, status; @@ -171,16 +179,16 @@ void dns64_detection(unsigned net_id) { backoff_sleep = 1; - while(1) { - status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname,net_id,&tmp_ptr); - if(status > 0) { + while (1) { + status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname, net_id, &tmp_ptr); + if (status > 0) { memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); return; } logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); sleep(backoff_sleep); backoff_sleep *= 2; - if(backoff_sleep >= 1800) { + if (backoff_sleep >= 1800) { // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only // to IPv6 with NAT64). @@ -191,9 +199,9 @@ void dns64_detection(unsigned net_id) { /* function: gen_random_iid * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix - * myaddr - IPv6 address to write to - * ipv4_local_subnet - clat IPv4 address - * plat_subnet - NAT64 prefix + * myaddr - IPv6 address to write to + * ipv4_local_subnet - clat IPv4 address + * plat_subnet - NAT64 prefix */ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, struct in6_addr *plat_subnet) { @@ -212,7 +220,7 @@ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + ip_checksum_add(0, myaddr, sizeof(*myaddr)); - uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); myaddr->s6_addr[11] = delta >> 8; myaddr->s6_addr[12] = delta & 0xff; } @@ -227,10 +235,9 @@ int connect_is_ipv4_address_free(in_addr_t addr) { // Attempt to connect to the address. If the connection succeeds and getsockname returns the same // the address then the address is already assigned to the system and we can't use it. struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 }; - socklen_t len = sizeof(sin); - int inuse = connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0 && - getsockname(s, (struct sockaddr *) &sin, &len) == 0 && - (size_t) len >= sizeof(sin) && + socklen_t len = sizeof(sin); + int inuse = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 && + getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) && sin.sin_addr.s_addr == addr; close(s); @@ -241,127 +248,123 @@ addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free; /* function: config_select_ipv4_address * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order - * ip - the IP address from the configuration file - * prefixlen - the length of the prefix from which addresses may be selected. - * returns: the IPv4 address, or INADDR_NONE if no addresses were available + * ip - the IP address from the configuration file + * prefixlen - the length of the prefix from which addresses may be selected. + * returns: the IPv4 address, or INADDR_NONE if no addresses were available */ in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) { in_addr_t chosen = INADDR_NONE; // Don't accept prefixes that are too large because we scan addresses one by one. if (prefixlen < 16 || prefixlen > 32) { - return chosen; + return chosen; } // All these are in host byte order. - in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); - in_addr_t ipv4 = ntohl(ip->s_addr); + in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); + in_addr_t ipv4 = ntohl(ip->s_addr); in_addr_t first_ipv4 = ipv4; - in_addr_t prefix = ipv4 & mask; + in_addr_t prefix = ipv4 & mask; // Pick the first IPv4 address in the pool, wrapping around if necessary. // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0. do { - if (config_is_ipv4_address_free(htonl(ipv4))) { - chosen = htonl(ipv4); - break; - } - ipv4 = prefix | ((ipv4 + 1) & ~mask); + if (config_is_ipv4_address_free(htonl(ipv4))) { + chosen = htonl(ipv4); + break; + } + ipv4 = prefix | ((ipv4 + 1) & ~mask); } while (ipv4 != first_ipv4); return chosen; } /* function: config_generate_local_ipv6_subnet - * generates the local ipv6 subnet when given the interface ip - * requires config.ipv6_host_id - * interface_ip - in: interface ip, out: local ipv6 host address + * generates the local ipv6 subnet when given the interface ip requires config.ipv6_host_id + * interface_ip - in: interface ip, out: local ipv6 host address */ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { int i; if (Global_Clatd_Config.use_dynamic_iid) { /* Generate a random interface ID. */ - gen_random_iid(interface_ip, - &Global_Clatd_Config.ipv4_local_subnet, + gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet); } else { /* Use the specified interface ID. */ - for(i = 2; i < 4; i++) { + for (i = 2; i < 4; i++) { interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i]; } } } /* function: read_config - * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on failure, 1 on success - * file - filename to parse - * uplink_interface - interface to use to reach the internet and supplier of address space - * plat_prefix - (optional) plat prefix to use, otherwise follow config file - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network + * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on + * failure, 1 on success + * file - filename to parse + * uplink_interface - interface to use to reach the internet and supplier of address space + * plat_prefix - (optional) plat prefix to use, otherwise follow config file + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id) { - cnode *root = config_node("", ""); + unsigned net_id) { + cnode *root = config_node("", ""); void *tmp_ptr = NULL; unsigned flags; - if(!root) { - logmsg(ANDROID_LOG_FATAL,"out of memory"); + 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); + if (root->first_child == NULL) { + logmsg(ANDROID_LOG_FATAL, "Could not read config file %s", file); goto failed; } Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); - if (!Global_Clatd_Config.default_pdp_interface) + if (!Global_Clatd_Config.default_pdp_interface) goto failed; + + if (!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu)) goto failed; + + if (!config_item_int16_t(root, "ipv4mtu", "-1", &Global_Clatd_Config.ipv4mtu)) goto failed; + + if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, + &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if(!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu)) + if (!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, + &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; - if(!config_item_int16_t(root, "ipv4mtu", "-1", &Global_Clatd_Config.ipv4mtu)) - goto failed; - - if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, - &Global_Clatd_Config.ipv4_local_subnet)) - goto failed; - - if(!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, - &Global_Clatd_Config.ipv4_local_prefixlen)) - goto failed; - - if(plat_prefix) { // plat subnet is coming from the command line - if(inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) { - logmsg(ANDROID_LOG_FATAL,"invalid IPv6 address specified for plat prefix: %s", plat_prefix); + 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) { + if (!tmp_ptr || strcmp(tmp_ptr, "no") == 0) { free(tmp_ptr); - if(!config_item_ip6(root, "plat_subnet", NULL, &Global_Clatd_Config.plat_subnet)) { + 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))) + if (!(Global_Clatd_Config.plat_from_dns64_hostname = + config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME))) goto failed; dns64_detection(net_id); } } - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) - goto failed; + if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; /* In order to prevent multiple devices attempting to use the same clat address, never use a statically-configured interface ID on a broadcast interface such as wifi. */ @@ -388,11 +391,17 @@ failed: void dump_config() { char charbuffer[INET6_ADDRSTRLEN]; - logmsg(ANDROID_LOG_DEBUG,"mtu = %d",Global_Clatd_Config.mtu); - logmsg(ANDROID_LOG_DEBUG,"ipv4mtu = %d",Global_Clatd_Config.ipv4mtu); - logmsg(ANDROID_LOG_DEBUG,"ipv6_local_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG,"ipv4_local_subnet = %s",inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG,"ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); - logmsg(ANDROID_LOG_DEBUG,"plat_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG,"default_pdp_interface = %s",Global_Clatd_Config.default_pdp_interface); + logmsg(ANDROID_LOG_DEBUG, "mtu = %d", Global_Clatd_Config.mtu); + logmsg(ANDROID_LOG_DEBUG, "ipv4mtu = %d", Global_Clatd_Config.ipv4mtu); + logmsg( + ANDROID_LOG_DEBUG, "ipv6_local_subnet = %s", + inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); + logmsg( + ANDROID_LOG_DEBUG, "ipv4_local_subnet = %s", + inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); + logmsg(ANDROID_LOG_DEBUG, "ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); + logmsg(ANDROID_LOG_DEBUG, "plat_subnet = %s", + inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); + logmsg(ANDROID_LOG_DEBUG, "default_pdp_interface = %s", + Global_Clatd_Config.default_pdp_interface); } diff --git a/config.h b/config.h index e31a81d1e2..e521b74250 100644 --- a/config.h +++ b/config.h @@ -18,8 +18,8 @@ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#include #include +#include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" @@ -40,7 +40,7 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id); + unsigned net_id); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); diff --git a/dns64.c b/dns64.c index ed28eb0255..5ca85d999b 100644 --- a/dns64.c +++ b/dns64.c @@ -16,25 +16,26 @@ * dns64.c - find the nat64 prefix with a dns64 lookup */ -#include -#include #include #include -#include +#include #include #include +#include +#include #include +#include "NetdClient.h" #include "dns64.h" #include "logging.h" -#include "NetdClient.h" #include "resolv_netid.h" /* function: plat_prefix - * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on failure - * ipv4_name - name to lookup - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network - * prefix - the plat /96 prefix + * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on + * failure + * ipv4_name - name to lookup + * net_id - (optional) netId to use, NETID_UNSET indicates use of default network + * prefix - the plat /96 prefix */ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) { const struct addrinfo hints = { @@ -54,8 +55,8 @@ int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); if (status != 0 || result == NULL) { - logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", - ipv4_name, status, gai_strerror(status)); + logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", ipv4_name, status, + gai_strerror(status)); return 0; } diff --git a/dump.c b/dump.c index 27b75d1e69..2c948ffea1 100644 --- a/dump.c +++ b/dump.c @@ -15,23 +15,23 @@ * * dump.c - print various headers for debugging */ -#include #include +#include #include #include +#include +#include #include #include -#include -#include -#include #include -#include -#include +#include +#include +#include -#include "debug.h" #include "checksum.h" #include "clatd.h" +#include "debug.h" #include "logging.h" #if CLAT_DEBUG @@ -44,29 +44,29 @@ void dump_ip(struct iphdr *header) { frag_flags = ntohs(header->frag_off); printf("IP packet\n"); - printf("header_len = %x\n",header->ihl); - printf("version = %x\n",header->version); - printf("tos = %x\n",header->tos); - printf("tot_len = %x\n",ntohs(header->tot_len)); - printf("id = %x\n",ntohs(header->id)); + printf("header_len = %x\n", header->ihl); + printf("version = %x\n", header->version); + printf("tos = %x\n", header->tos); + printf("tot_len = %x\n", ntohs(header->tot_len)); + printf("id = %x\n", ntohs(header->id)); printf("frag: "); - if(frag_flags & IP_RF) { + if (frag_flags & IP_RF) { printf("(RF) "); } - if(frag_flags & IP_DF) { + if (frag_flags & IP_DF) { printf("DF "); } - if(frag_flags & IP_MF) { + if (frag_flags & IP_MF) { printf("MF "); } - printf("offset = %x\n",frag_flags & IP_OFFMASK); - printf("ttl = %x\n",header->ttl); - printf("protocol = %x\n",header->protocol); - printf("checksum = %x\n",ntohs(header->check)); + printf("offset = %x\n", frag_flags & IP_OFFMASK); + printf("ttl = %x\n", header->ttl); + printf("protocol = %x\n", header->protocol); + printf("checksum = %x\n", ntohs(header->check)); inet_ntop(AF_INET, &header->saddr, addrstr, sizeof(addrstr)); - printf("saddr = %s\n",addrstr); + printf("saddr = %s\n", addrstr); inet_ntop(AF_INET, &header->daddr, addrstr, sizeof(addrstr)); - printf("daddr = %s\n",addrstr); + printf("daddr = %s\n", addrstr); } /* print ip6 header */ @@ -74,138 +74,138 @@ void dump_ip6(struct ip6_hdr *header) { char addrstr[INET6_ADDRSTRLEN]; printf("ipv6\n"); - printf("version = %x\n",header->ip6_vfc >> 4); - printf("traffic class = %x\n",header->ip6_flow >> 20); - printf("flow label = %x\n",ntohl(header->ip6_flow & 0x000fffff)); - printf("payload len = %x\n",ntohs(header->ip6_plen)); - printf("next header = %x\n",header->ip6_nxt); - printf("hop limit = %x\n",header->ip6_hlim); + printf("version = %x\n", header->ip6_vfc >> 4); + printf("traffic class = %x\n", header->ip6_flow >> 20); + printf("flow label = %x\n", ntohl(header->ip6_flow & 0x000fffff)); + printf("payload len = %x\n", ntohs(header->ip6_plen)); + printf("next header = %x\n", header->ip6_nxt); + printf("hop limit = %x\n", header->ip6_hlim); inet_ntop(AF_INET6, &header->ip6_src, addrstr, sizeof(addrstr)); - printf("source = %s\n",addrstr); + printf("source = %s\n", addrstr); inet_ntop(AF_INET6, &header->ip6_dst, addrstr, sizeof(addrstr)); - printf("dest = %s\n",addrstr); + printf("dest = %s\n", addrstr); } /* print icmp header */ void dump_icmp(struct icmphdr *icmp) { printf("ICMP\n"); - printf("icmp.type = %x ",icmp->type); - if(icmp->type == ICMP_ECHOREPLY) { + printf("icmp.type = %x ", icmp->type); + if (icmp->type == ICMP_ECHOREPLY) { printf("echo reply"); - } else if(icmp->type == ICMP_ECHO) { + } else if (icmp->type == ICMP_ECHO) { printf("echo request"); } else { printf("other"); } printf("\n"); - printf("icmp.code = %x\n",icmp->code); - printf("icmp.checksum = %x\n",ntohs(icmp->checksum)); - if(icmp->type == ICMP_ECHOREPLY || icmp->type == ICMP_ECHO) { - printf("icmp.un.echo.id = %x\n",ntohs(icmp->un.echo.id)); - printf("icmp.un.echo.sequence = %x\n",ntohs(icmp->un.echo.sequence)); + printf("icmp.code = %x\n", icmp->code); + printf("icmp.checksum = %x\n", ntohs(icmp->checksum)); + if (icmp->type == ICMP_ECHOREPLY || icmp->type == ICMP_ECHO) { + printf("icmp.un.echo.id = %x\n", ntohs(icmp->un.echo.id)); + printf("icmp.un.echo.sequence = %x\n", ntohs(icmp->un.echo.sequence)); } } /* print icmp6 header */ void dump_icmp6(struct icmp6_hdr *icmp6) { printf("ICMP6\n"); - printf("type = %x",icmp6->icmp6_type); - if(icmp6->icmp6_type == ICMP6_ECHO_REQUEST) { + printf("type = %x", icmp6->icmp6_type); + if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) { printf("(echo request)"); - } else if(icmp6->icmp6_type == ICMP6_ECHO_REPLY) { + } else if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) { printf("(echo reply)"); } printf("\n"); - printf("code = %x\n",icmp6->icmp6_code); + printf("code = %x\n", icmp6->icmp6_code); - printf("checksum = %x\n",icmp6->icmp6_cksum); + printf("checksum = %x\n", icmp6->icmp6_cksum); - if((icmp6->icmp6_type == ICMP6_ECHO_REQUEST) || (icmp6->icmp6_type == ICMP6_ECHO_REPLY)) { - printf("icmp6_id = %x\n",icmp6->icmp6_id); - printf("icmp6_seq = %x\n",icmp6->icmp6_seq); + if ((icmp6->icmp6_type == ICMP6_ECHO_REQUEST) || (icmp6->icmp6_type == ICMP6_ECHO_REPLY)) { + printf("icmp6_id = %x\n", icmp6->icmp6_id); + printf("icmp6_seq = %x\n", icmp6->icmp6_seq); } } /* print udp header */ -void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, - const uint8_t *payload, size_t payload_size) { +void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const uint8_t *payload, + size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, udp, sizeof(struct udphdr)); temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size); - my_checksum = ip_checksum_finish(temp_checksum); + my_checksum = ip_checksum_finish(temp_checksum); printf("UDP\n"); - printf("source = %x\n",ntohs(udp->source)); - printf("dest = %x\n",ntohs(udp->dest)); - printf("len = %x\n",ntohs(udp->len)); - printf("check = %x (mine %x)\n",udp->check,my_checksum); + printf("source = %x\n", ntohs(udp->source)); + printf("dest = %x\n", ntohs(udp->dest)); + printf("len = %x\n", ntohs(udp->len)); + printf("check = %x (mine %x)\n", udp->check, my_checksum); } /* print ipv4/udp header */ -void dump_udp(const struct udphdr *udp, const struct iphdr *ip, - const uint8_t *payload, size_t payload_size) { +void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload, + size_t payload_size) { uint32_t temp_checksum; temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print ipv6/udp header */ -void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, - const uint8_t *payload, size_t payload_size) { +void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload, + size_t payload_size) { uint32_t temp_checksum; temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP); dump_udp_generic(udp, temp_checksum, payload, payload_size); } /* print tcp header */ -void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) { +void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, + uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr)); - if(options) { + if (options) { temp_checksum = ip_checksum_add(temp_checksum, options, options_size); } temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size); - my_checksum = ip_checksum_finish(temp_checksum); + my_checksum = ip_checksum_finish(temp_checksum); printf("TCP\n"); - printf("source = %x\n",ntohs(tcp->source)); - printf("dest = %x\n",ntohs(tcp->dest)); - printf("seq = %x\n",ntohl(tcp->seq)); - printf("ack = %x\n",ntohl(tcp->ack_seq)); - printf("d_off = %x\n",tcp->doff); - printf("res1 = %x\n",tcp->res1); + printf("source = %x\n", ntohs(tcp->source)); + printf("dest = %x\n", ntohs(tcp->dest)); + printf("seq = %x\n", ntohl(tcp->seq)); + printf("ack = %x\n", ntohl(tcp->ack_seq)); + printf("d_off = %x\n", tcp->doff); + printf("res1 = %x\n", tcp->res1); #ifdef __BIONIC__ - printf("CWR = %x\n",tcp->cwr); - printf("ECE = %x\n",tcp->ece); + printf("CWR = %x\n", tcp->cwr); + printf("ECE = %x\n", tcp->ece); #else - printf("CWR/ECE = %x\n",tcp->res2); + printf("CWR/ECE = %x\n", tcp->res2); #endif - printf("urg = %x ack = %x psh = %x rst = %x syn = %x fin = %x\n", - tcp->urg, tcp->ack, tcp->psh, tcp->rst, tcp->syn, tcp->fin); - printf("window = %x\n",ntohs(tcp->window)); - printf("check = %x [mine %x]\n",tcp->check,my_checksum); - printf("urgent = %x\n",tcp->urg_ptr); + printf("urg = %x ack = %x psh = %x rst = %x syn = %x fin = %x\n", tcp->urg, tcp->ack, + tcp->psh, tcp->rst, tcp->syn, tcp->fin); + printf("window = %x\n", ntohs(tcp->window)); + printf("check = %x [mine %x]\n", tcp->check, my_checksum); + printf("urgent = %x\n", tcp->urg_ptr); - if(options) { + if (options) { size_t i; printf("options: "); - for(i=0; i +#include +#include +#include +#include +#include +#include + void dump_ip(struct iphdr *header); void dump_icmp(struct icmphdr *icmp); -void dump_udp(const struct udphdr *udp, const struct iphdr *ip, - const uint8_t *payload, size_t payload_size); -void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, - const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size); +void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload, + size_t payload_size); +void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload, + size_t payload_size, const char *options, size_t options_size); void dump_ip6(struct ip6_hdr *header); void dump_icmp6(struct icmp6_hdr *icmp6); -void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, - const uint8_t *payload, size_t payload_size); -void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, - const uint8_t *payload, size_t payload_size, - const char *options, size_t options_size); +void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload, + size_t payload_size); +void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload, + size_t payload_size, const char *options, size_t options_size); void logcat_hexdump(const char *info, const uint8_t *data, size_t len); void dump_iovec(const struct iovec *iov, int iov_len); diff --git a/getaddr.c b/getaddr.c index e0478cf286..6f4bc866f8 100644 --- a/getaddr.c +++ b/getaddr.c @@ -15,10 +15,10 @@ * * getaddr.c - get a locally configured address */ -#include -#include -#include #include +#include +#include +#include #include #include @@ -26,8 +26,8 @@ #include #include "getaddr.h" -#include "netlink_msg.h" #include "logging.h" +#include "netlink_msg.h" // shared state between getinterface_ip and getaddr_cb struct target { @@ -39,8 +39,8 @@ struct target { /* function: getaddr_cb * callback for getinterface_ip - * msg - netlink message - * data - (struct target) info for which address we're looking for + * msg - netlink message + * data - (struct target) info for which address we're looking for */ static int getaddr_cb(struct nl_msg *msg, void *data) { struct ifaddrmsg *ifa_p; @@ -51,24 +51,22 @@ static int getaddr_cb(struct nl_msg *msg, void *data) { ifa_p = (struct ifaddrmsg *)nlmsg_data(nlmsg_hdr(msg)); rta_p = (struct rtattr *)IFA_RTA(ifa_p); - if(ifa_p->ifa_index != targ_p->ifindex) - return NL_OK; + if (ifa_p->ifa_index != targ_p->ifindex) return NL_OK; - if(ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) - return NL_OK; + if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return NL_OK; rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); for (; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) { - switch(rta_p->rta_type) { + switch (rta_p->rta_type) { case IFA_ADDRESS: - if((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) { + if ((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) { memcpy(&targ_p->ip.ip6, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr)); targ_p->foundip = 1; return NL_OK; } break; case IFA_LOCAL: - if(targ_p->family == AF_INET) { + if (targ_p->family == AF_INET) { memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr)); targ_p->foundip = 1; return NL_OK; @@ -82,9 +80,9 @@ static int getaddr_cb(struct nl_msg *msg, void *data) { /* function: error_handler * error callback for getinterface_ip - * nla - source of the error message - * err - netlink message - * arg - (struct target) info for which address we're looking for + * nla - source of the error message + * err - netlink message + * arg - (struct target) info for which address we're looking for */ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, __attribute__((unused)) struct nlmsgerr *err, @@ -93,9 +91,10 @@ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, } /* function: getinterface_ip - * finds the first global non-privacy IP of the given family for the given interface, or returns NULL. caller frees pointer - * interface - interface to look for - * family - family + * finds the first global non-privacy IP of the given family for the given interface, or returns + * NULL. caller frees pointer + * interface - interface to look for + * family - family */ union anyip *getinterface_ip(const char *interface, int family) { struct ifaddrmsg ifa; @@ -103,18 +102,18 @@ union anyip *getinterface_ip(const char *interface, int family) { struct target targ; union anyip *retval = NULL; - targ.family = family; + targ.family = family; targ.foundip = 0; targ.ifindex = if_nametoindex(interface); - if(targ.ifindex == 0) { - return NULL; // interface not found + if (targ.ifindex == 0) { + return NULL; // interface not found } memset(&ifa, 0, sizeof(ifa)); ifa.ifa_family = targ.family; callbacks = nl_cb_alloc(NL_CB_DEFAULT); - if(!callbacks) { + if (!callbacks) { goto cleanup; } nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, getaddr_cb, &targ); @@ -123,18 +122,17 @@ union anyip *getinterface_ip(const char *interface, int family) { // sends message and waits for a response send_ifaddrmsg(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &ifa, callbacks); - if(targ.foundip) { + if (targ.foundip) { retval = malloc(sizeof(union anyip)); - if(!retval) { - logmsg(ANDROID_LOG_FATAL,"getinterface_ip/out of memory"); + if (!retval) { + 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); + if (callbacks) nl_cb_put(callbacks); return retval; } diff --git a/icmp.c b/icmp.c index 75a4a4d1c4..f9ba113eee 100644 --- a/icmp.c +++ b/icmp.c @@ -16,13 +16,13 @@ * icmp.c - convenience functions for translating ICMP and ICMPv6 packets. */ +#include +#include #include #include -#include -#include -#include "logging.h" #include "icmp.h" +#include "logging.h" /* function: icmp_guess_ttl * Guesses the number of hops a received packet has traversed based on its TTL. @@ -44,17 +44,13 @@ uint8_t icmp_guess_ttl(uint8_t ttl) { * Determines whether an ICMP type is an error message. * type: the ICMP type */ -int is_icmp_error(uint8_t type) { - return type == 3 || type == 11 || type == 12; -} +int is_icmp_error(uint8_t type) { return type == 3 || type == 11 || type == 12; } /* function: is_icmp6_error * Determines whether an ICMPv6 type is an error message. * type: the ICMPv6 type */ -int is_icmp6_error(uint8_t type) { - return type < 128; -} +int is_icmp6_error(uint8_t type) { return type < 128; } /* function: icmp_to_icmp6_type * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2. @@ -112,7 +108,7 @@ uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) { case ICMP_UNREACH_PRECEDENCE_CUTOFF: return ICMP6_DST_UNREACH_ADMIN; - // Otherwise, we don't understand this ICMP type/code combination. Fall through. + // Otherwise, we don't understand this ICMP type/code combination. Fall through. } } logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code); @@ -172,7 +168,7 @@ uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) { case ICMP6_DST_UNREACH_NOPORT: return ICMP_UNREACH_PORT; - // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through. + // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through. } } diff --git a/ipv4.c b/ipv4.c index 4b0db3952c..fce400f67e 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,11 +17,11 @@ */ #include -#include "translate.h" #include "checksum.h" -#include "logging.h" #include "debug.h" #include "dump.h" +#include "logging.h" +#include "translate.h" /* function: icmp_packet * translates an icmp packet @@ -36,12 +36,12 @@ int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *ic const uint8_t *payload; size_t payload_size; - if(len < sizeof(struct icmphdr)) { + if (len < sizeof(struct icmphdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)"); return 0; } - payload = (const uint8_t *) (icmp + 1); + payload = (const uint8_t *)(icmp + 1); payload_size = len - sizeof(struct icmphdr); return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size); @@ -55,8 +55,8 @@ int icmp_packet(clat_packet out, clat_packet_index pos, const struct icmphdr *ic * returns: the highest position in the output clat_packet that's filled in */ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { - const struct iphdr *header = (struct iphdr *) packet; - struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base; + const struct iphdr *header = (struct iphdr *)packet; + struct ip6_hdr *ip6_targ = (struct ip6_hdr *)out[pos].iov_base; struct ip6_frag *frag_hdr; size_t frag_hdr_len; uint8_t nxthdr; @@ -65,22 +65,22 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s uint32_t old_sum, new_sum; int iov_len; - if(len < sizeof(struct iphdr)) { + if (len < sizeof(struct iphdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header"); return 0; } - if(header->ihl < 5) { + 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 + 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) { + if (header->version != 4) { logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version); return 0; } @@ -90,8 +90,8 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s * translate the options. */ - next_header = packet + header->ihl*4; - len_left = len - header->ihl * 4; + next_header = packet + header->ihl * 4; + len_left = len - header->ihl * 4; nxthdr = header->protocol; if (nxthdr == IPPROTO_ICMP) { @@ -115,26 +115,26 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr); // If the IPv4 packet is fragmented, add a Fragment header. - frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base; - frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); + frag_hdr = (struct ip6_frag *)out[pos + 1].iov_base; + frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header); out[pos + 1].iov_len = frag_hdr_len; if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) { // Non-first fragment. Copy the rest of the packet as is. iov_len = generic_packet(out, pos + 2, next_header, len_left); } else if (nxthdr == IPPROTO_ICMPV6) { - iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left); + iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *)next_header, new_sum, len_left); } else if (nxthdr == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, - len_left); + iov_len = + tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, - len_left); + iov_len = + udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); } else if (nxthdr == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG - logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); + logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x", header->protocol); logcat_hexdump("ipv4/protocol", packet, len); #endif return 0; diff --git a/ipv6.c b/ipv6.c index b4853138dd..a11dd1ae89 100644 --- a/ipv6.c +++ b/ipv6.c @@ -19,12 +19,12 @@ #include -#include "translate.h" #include "checksum.h" -#include "logging.h" -#include "dump.h" #include "config.h" #include "debug.h" +#include "dump.h" +#include "logging.h" +#include "translate.h" /* function: icmp6_packet * takes an icmp6 packet and sets it up for translation @@ -39,12 +39,12 @@ int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr const uint8_t *payload; size_t payload_size; - if(len < sizeof(struct icmp6_hdr)) { + if (len < sizeof(struct icmp6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)"); return 0; } - payload = (const uint8_t *) (icmp6 + 1); + payload = (const uint8_t *)(icmp6 + 1); payload_size = len - sizeof(struct icmp6_hdr); return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); @@ -76,8 +76,8 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i * returns: the highest position in the output clat_packet that's filled in */ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { - const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet; - struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base; + const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; + struct iphdr *ip_targ = (struct iphdr *)out[pos].iov_base; struct ip6_frag *frag_hdr = NULL; uint8_t protocol; const uint8_t *next_header; @@ -85,14 +85,14 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s uint32_t old_sum, new_sum; int iov_len; - if(len < sizeof(struct ip6_hdr)) { + if (len < sizeof(struct ip6_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); return 0; } - if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst); - return 0; // silently ignore + return 0; // silently ignore } // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless @@ -111,7 +111,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s } next_header = packet + sizeof(struct ip6_hdr); - len_left = len - sizeof(struct ip6_hdr); + len_left = len - sizeof(struct ip6_hdr); protocol = ip6->ip6_nxt; @@ -125,7 +125,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s // If there's a Fragment header, parse it and decide what the next header is. // Do this before calculating the pseudo-header checksum because it updates the next header value. if (protocol == IPPROTO_FRAGMENT) { - frag_hdr = (struct ip6_frag *) next_header; + frag_hdr = (struct ip6_frag *)next_header; if (len_left < sizeof(*frag_hdr)) { logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); return 0; @@ -139,7 +139,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s // ICMP and ICMPv6 have different protocol numbers. if (protocol == IPPROTO_ICMPV6) { - protocol = IPPROTO_ICMP; + protocol = IPPROTO_ICMP; ip_targ->protocol = IPPROTO_ICMP; } @@ -155,13 +155,13 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { iov_len = generic_packet(out, pos + 2, next_header, len_left); } else if (protocol == IPPROTO_ICMP) { - iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left); + iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *)next_header, len_left); } else if (protocol == IPPROTO_TCP) { - iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum, - len_left); + iov_len = + tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left); } else if (protocol == IPPROTO_UDP) { - iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum, - len_left); + iov_len = + udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); } else if (protocol == IPPROTO_GRE) { iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { @@ -174,6 +174,6 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s // Set the length and calculate the checksum. ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos)); - ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); + ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); return iov_len; } diff --git a/logging.c b/logging.c index c90b1cfc3f..79d98e1793 100644 --- a/logging.c +++ b/logging.c @@ -16,11 +16,11 @@ * logging.c - print a log message */ -#include #include +#include -#include "logging.h" #include "debug.h" +#include "logging.h" /* function: logmsg * prints a log message to android's log buffer diff --git a/mtu.c b/mtu.c index 975bf0e06d..567d177609 100644 --- a/mtu.c +++ b/mtu.c @@ -16,12 +16,12 @@ * mtu.c - get interface mtu */ -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include "mtu.h" @@ -34,12 +34,12 @@ int getifmtu(const char *ifname) { struct ifreq if_mtu; fd = socket(AF_INET, SOCK_STREAM, 0); - if(fd < 0) { + if (fd < 0) { return -1; } strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ); if_mtu.ifr_name[IFNAMSIZ - 1] = '\0'; - if(ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { + if (ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { return -1; } return if_mtu.ifr_mtu; diff --git a/netlink_callbacks.c b/netlink_callbacks.c index a79aa7615a..804976dda3 100644 --- a/netlink_callbacks.c +++ b/netlink_callbacks.c @@ -15,8 +15,8 @@ * * netlink_callbacks.c - generic callbacks for netlink responses */ -#include #include +#include #include #include @@ -29,7 +29,7 @@ */ static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) { int *retval = data; - *retval = 0; + *retval = 0; return NL_OK; } @@ -39,26 +39,26 @@ static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) { * err - netlink error message * arg - pointer to an int, stores the error code */ -static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, - struct nlmsgerr *err, void *arg) { +static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) { int *retval = arg; - if(err->error < 0) { + if (err->error < 0) { *retval = err->error; } else { - *retval = 0; // NLMSG_ERROR used as reply type on no error + *retval = 0; // NLMSG_ERROR used as reply type on no error } return NL_OK; } /* function: alloc_ack_callbacks - * allocates a set of netlink callbacks. returns NULL on failure. callbacks will modify retval with <0 meaning failure - * retval - shared state between caller and callback functions + * allocates a set of netlink callbacks. returns NULL on failure. callbacks will modify retval + * with <0 meaning failure retval - shared state between caller and callback functions */ struct nl_cb *alloc_ack_callbacks(int *retval) { struct nl_cb *callbacks; callbacks = nl_cb_alloc(NL_CB_DEFAULT); - if(!callbacks) { + if (!callbacks) { return NULL; } nl_cb_set(callbacks, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, retval); diff --git a/netlink_msg.c b/netlink_msg.c index 13906f4861..be76ecda83 100644 --- a/netlink_msg.c +++ b/netlink_msg.c @@ -16,29 +16,29 @@ * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink */ -#include +#include #include #include +#include #include -#include #include #include -#include -#include #include +#include +#include -#include "netlink_msg.h" #include "netlink_callbacks.h" +#include "netlink_msg.h" /* function: family_size * returns the size of the address structure for the given family, or 0 on error * family - AF_INET or AF_INET6 */ size_t inet_family_size(int family) { - if(family == AF_INET) { + if (family == AF_INET) { return sizeof(struct in_addr); - } else if(family == AF_INET6) { + } else if (family == AF_INET6) { return sizeof(struct in6_addr); } else { return 0; @@ -52,11 +52,12 @@ size_t inet_family_size(int family) { * payload_struct - pointer to a struct to add to netlink message * payload_len - bytelength of structure */ -struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, size_t payload_len) { +struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, + size_t payload_len) { struct nl_msg *msg; msg = nlmsg_alloc(); - if(!msg) { + if (!msg) { return NULL; } @@ -65,9 +66,9 @@ struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_ return NULL; } - msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len); + msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len); msg->nm_nlh->nlmsg_flags = flags; - msg->nm_nlh->nlmsg_type = type; + msg->nm_nlh->nlmsg_type = type; memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len); @@ -116,7 +117,7 @@ int netlink_set_kernel_only(struct nl_sock *nl_sk) { } int sockfd = nl_socket_get_fd(nl_sk); - return connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)); + return connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); } /* function: send_netlink_msg @@ -128,23 +129,18 @@ void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) { struct nl_sock *nl_sk; nl_sk = nl_socket_alloc(); - if(!nl_sk) - goto cleanup; + if (!nl_sk) goto cleanup; - if(nl_connect(nl_sk, NETLINK_ROUTE) != 0) - goto cleanup; + if (nl_connect(nl_sk, NETLINK_ROUTE) != 0) goto cleanup; - if(nl_send_auto_complete(nl_sk, msg) < 0) - goto cleanup; + if (nl_send_auto_complete(nl_sk, msg) < 0) goto cleanup; - if(netlink_set_kernel_only(nl_sk) < 0) - goto cleanup; + if (netlink_set_kernel_only(nl_sk) < 0) goto cleanup; nl_recvmsgs(nl_sk, callbacks); cleanup: - if(nl_sk) - nl_socket_free(nl_sk); + if (nl_sk) nl_socket_free(nl_sk); } /* function: send_ifaddrmsg @@ -158,8 +154,7 @@ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct struct nl_msg *msg = NULL; msg = nlmsg_alloc_ifaddr(type, flags, ifa); - if(!msg) - return; + if (!msg) return; send_netlink_msg(msg, callbacks); @@ -172,10 +167,10 @@ void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct */ int netlink_sendrecv(struct nl_msg *msg) { struct nl_cb *callbacks = NULL; - int retval = -EIO; + int retval = -EIO; callbacks = alloc_ack_callbacks(&retval); - if(!callbacks) { + if (!callbacks) { return -ENOMEM; } diff --git a/ring.c b/ring.c index 5e99fd5f5d..61d40d2e15 100644 --- a/ring.c +++ b/ring.c @@ -16,13 +16,13 @@ * ring.c - packet ring buffer functions */ -#include -#include #include -#include -#include +#include #include #include +#include +#include +#include #include "logging.h" #include "ring.h" @@ -37,26 +37,26 @@ int ring_create(struct tun_data *tunnel) { } int ver = TPACKET_V2; - if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *) &ver, sizeof(ver))) { + if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *)&ver, sizeof(ver))) { logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno)); return -1; } int on = 1; - if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *) &on, sizeof(on))) { + if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *)&on, sizeof(on))) { logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno)); } struct packet_ring *ring = &tunnel->ring; - ring->numblocks = TP_NUM_BLOCKS; + ring->numblocks = TP_NUM_BLOCKS; int total_frames = TP_FRAMES * ring->numblocks; struct tpacket_req req = { - .tp_frame_size = TP_FRAME_SIZE, // Frame size. - .tp_block_size = TP_BLOCK_SIZE, // Frames per block. - .tp_block_nr = ring->numblocks, // Number of blocks. - .tp_frame_nr = total_frames, // Total frames. + .tp_frame_size = TP_FRAME_SIZE, // Frame size. + .tp_block_size = TP_BLOCK_SIZE, // Frames per block. + .tp_block_nr = ring->numblocks, // Number of blocks. + .tp_frame_nr = total_frames, // Total frames. }; if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { @@ -65,20 +65,20 @@ int ring_create(struct tun_data *tunnel) { } size_t buflen = TP_BLOCK_SIZE * ring->numblocks; - ring->base = mmap(NULL, buflen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED|MAP_POPULATE, + ring->base = mmap(NULL, buflen, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, packetsock, 0); if (ring->base == MAP_FAILED) { logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno)); return -1; } - ring->block = 0; - ring->slot = 0; + ring->block = 0; + ring->slot = 0; ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE; - ring->next = (struct tpacket2_hdr *) ring->base; + ring->next = (struct tpacket2_hdr *)ring->base; - logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", - total_frames, buflen, ring->base); + logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", total_frames, + buflen, ring->base); return packetsock; } @@ -87,8 +87,8 @@ int ring_create(struct tun_data *tunnel) { * advances to the next position in the packet ring * ring - packet ring buffer */ -static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) { - uint8_t *next = (uint8_t *) ring->next; +static struct tpacket2_hdr *ring_advance(struct packet_ring *ring) { + uint8_t *next = (uint8_t *)ring->next; ring->slot++; next += TP_FRAME_SIZE; @@ -101,11 +101,11 @@ static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) { next += TP_FRAME_GAP; } else { ring->block = 0; - next = (uint8_t *) ring->base; + next = (uint8_t *)ring->base; } } - ring->next = (struct tpacket2_hdr *) next; + ring->next = (struct tpacket2_hdr *)next; return ring->next; } @@ -118,9 +118,9 @@ static struct tpacket2_hdr* ring_advance(struct packet_ring *ring) { void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) { struct tpacket2_hdr *tp = ring->next; if (tp->tp_status & TP_STATUS_USER) { - uint8_t *packet = ((uint8_t *) tp) + tp->tp_net; + uint8_t *packet = ((uint8_t *)tp) + tp->tp_net; translate_packet(write_fd, to_ipv6, packet, tp->tp_len); tp->tp_status = TP_STATUS_KERNEL; - tp = ring_advance(ring); + tp = ring_advance(ring); } } diff --git a/setif.c b/setif.c index 07f5bacd7a..184a46b24a 100644 --- a/setif.c +++ b/setif.c @@ -16,8 +16,8 @@ * setif.c - network interface configuration */ #include -#include #include +#include #include #include @@ -26,7 +26,11 @@ #include "logging.h" #include "netlink_msg.h" -#define DEBUG_OPTNAME(a) case (a): { optname = #a; break; } +#define DEBUG_OPTNAME(a) \ + case (a): { \ + optname = #a; \ + break; \ + } /* function: add_address * adds an IP address to/from an interface, returns 0 on success and <0 on failure @@ -36,14 +40,15 @@ * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) */ -int add_address(const char *ifname, int family, const void *address, int prefixlen, const void *broadcast) { +int add_address(const char *ifname, int family, const void *address, int prefixlen, + const void *broadcast) { int retval; size_t addr_size; struct ifaddrmsg ifa; struct nl_msg *msg = NULL; addr_size = inet_family_size(family); - if(addr_size == 0) { + if (addr_size == 0) { retval = -EAFNOSUPPORT; goto cleanup; } @@ -53,29 +58,30 @@ int add_address(const char *ifname, int family, const void *address, int prefixl retval = -ENODEV; goto cleanup; } - ifa.ifa_family = family; + ifa.ifa_family = family; ifa.ifa_prefixlen = prefixlen; - ifa.ifa_scope = RT_SCOPE_UNIVERSE; + ifa.ifa_scope = RT_SCOPE_UNIVERSE; - msg = nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); - if(!msg) { + msg = + nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); + if (!msg) { retval = -ENOMEM; goto cleanup; } - if(nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { + if (nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { retval = -ENOMEM; goto cleanup; } - if(family == AF_INET6) { + if (family == AF_INET6) { // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS - if(nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { + if (nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { retval = -ENOMEM; goto cleanup; } - } else if(family == AF_INET) { + } else if (family == AF_INET) { // AF_INET gets IFA_LOCAL + IFA_BROADCAST - if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { + if (nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { retval = -ENOMEM; goto cleanup; } @@ -87,8 +93,7 @@ int add_address(const char *ifname, int family, const void *address, int prefixl retval = netlink_sendrecv(msg); cleanup: - if(msg) - nlmsg_free(msg); + if (msg) nlmsg_free(msg); return retval; } @@ -109,15 +114,15 @@ int if_up(const char *ifname, int mtu) { goto cleanup; } ifi.ifi_change = IFF_UP; - ifi.ifi_flags = IFF_UP; + ifi.ifi_flags = IFF_UP; msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); - if(!msg) { + if (!msg) { retval = -ENOMEM; goto cleanup; } - if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { + if (nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { retval = -ENOMEM; goto cleanup; } @@ -125,8 +130,7 @@ int if_up(const char *ifname, int mtu) { retval = netlink_sendrecv(msg); cleanup: - if(msg) - nlmsg_free(msg); + if (msg) nlmsg_free(msg); return retval; } diff --git a/setif.h b/setif.h index d31eed522f..2d0adb11c2 100644 --- a/setif.h +++ b/setif.h @@ -18,7 +18,8 @@ #ifndef __SETIF_H__ #define __SETIF_H__ -int add_address(const char *ifname, int family, const void *address, int cidr, const void *broadcast); +int add_address(const char *ifname, int family, const void *address, int cidr, + const void *broadcast); int if_up(const char *ifname, int mtu); int add_anycast_address(int sock, const struct in6_addr *addr, const char *interface); diff --git a/translate.c b/translate.c index ddc9bac2d6..58a7e9d72a 100644 --- a/translate.c +++ b/translate.c @@ -17,13 +17,13 @@ */ #include -#include "icmp.h" -#include "translate.h" #include "checksum.h" #include "clatd.h" #include "config.h" -#include "logging.h" #include "debug.h" +#include "icmp.h" +#include "logging.h" +#include "translate.h" #include "tun.h" /* function: packet_checksum @@ -97,7 +97,7 @@ struct in6_addr ipv4_addr_to_ipv6_addr(uint32_t addr4) { return Global_Clatd_Config.ipv6_local_subnet; } else { // Assumes a /96 plat subnet. - addr6 = Global_Clatd_Config.plat_subnet; + addr6 = Global_Clatd_Config.plat_subnet; addr6.s6_addr32[3] = addr4; return addr6; } @@ -125,15 +125,15 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, int ttl_guess; memset(ip, 0, sizeof(struct iphdr)); - ip->ihl = 5; - ip->version = 4; - ip->tos = 0; - ip->tot_len = htons(sizeof(struct iphdr) + payload_len); - ip->id = 0; + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + ip->tot_len = htons(sizeof(struct iphdr) + payload_len); + ip->id = 0; ip->frag_off = htons(IP_DF); - ip->ttl = old_header->ip6_hlim; + ip->ttl = old_header->ip6_hlim; ip->protocol = protocol; - ip->check = 0; + ip->check = 0; ip->saddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_src); ip->daddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_dst); @@ -141,7 +141,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol, // Third-party ICMPv6 message. This may have been originated by an native IPv6 address. // In that case, the source IPv6 address can't be translated and we need to make up an IPv4 // source address. For now, use 255.0.0., which at least looks useful in traceroute. - if ((uint32_t) ip->saddr == INADDR_NONE) { + if ((uint32_t)ip->saddr == INADDR_NONE) { ttl_guess = icmp_guess_ttl(old_header->ip6_hlim); ip->saddr = htonl((0xff << 24) + ttl_guess); } @@ -158,9 +158,9 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol const struct iphdr *old_header) { memset(ip6, 0, sizeof(struct ip6_hdr)); - ip6->ip6_vfc = 6 << 4; + ip6->ip6_vfc = 6 << 4; ip6->ip6_plen = htons(payload_len); - ip6->ip6_nxt = protocol; + ip6->ip6_nxt = protocol; ip6->ip6_hlim = old_header->ttl; ip6->ip6_src = ipv4_addr_to_ipv6_addr(old_header->saddr); @@ -178,13 +178,13 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ, const struct iphdr *old_header) { uint16_t frag_flags = ntohs(old_header->frag_off); - uint16_t frag_off = frag_flags & IP_OFFMASK; + uint16_t frag_off = frag_flags & IP_OFFMASK; if (frag_off == 0 && (frag_flags & IP_MF) == 0) { // Not a fragment. return 0; } - frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; + frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt; frag_hdr->ip6f_reserved = 0; // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits. frag_hdr->ip6f_offlg = htons(frag_off << 3); @@ -192,7 +192,7 @@ size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_tar frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG; } frag_hdr->ip6f_ident = htonl(ntohs(old_header->id)); - ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; + ip6_targ->ip6_nxt = IPPROTO_FRAGMENT; return sizeof(*frag_hdr); } @@ -210,7 +210,7 @@ uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ frag_off |= IP_MF; } ip_targ->frag_off = htons(frag_off); - ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); + ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff); ip_targ->protocol = frag_hdr->ip6f_nxt; return frag_hdr->ip6f_nxt; } @@ -232,15 +232,13 @@ int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr * memset(icmp6_targ, 0, sizeof(struct icmp6_hdr)); - icmp6_type = icmp_to_icmp6_type(icmp->type, icmp->code); + icmp6_type = icmp_to_icmp6_type(icmp->type, icmp->code); icmp6_targ->icmp6_type = icmp6_type; icmp6_targ->icmp6_code = icmp_to_icmp6_code(icmp->type, icmp->code); out[pos].iov_len = sizeof(struct icmp6_hdr); - if (pos == CLAT_POS_TRANSPORTHDR && - is_icmp_error(icmp->type) && - icmp6_type != ICMP6_PARAM_PROB) { + if (pos == CLAT_POS_TRANSPORTHDR && is_icmp_error(icmp->type) && icmp6_type != ICMP6_PARAM_PROB) { // An ICMP error we understand, one level deep. // Translate the nested packet (the one that caused the error). clat_packet_len = ipv4_packet(out, pos + 1, payload, payload_size); @@ -254,11 +252,11 @@ int icmp_to_icmp6(clat_packet out, clat_packet_index pos, const struct icmphdr * checksum = checksum + htons(20); } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) { // Ping packet. - icmp6_targ->icmp6_id = icmp->un.echo.id; - icmp6_targ->icmp6_seq = icmp->un.echo.sequence; - out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; - clat_packet_len = CLAT_POS_PAYLOAD + 1; + icmp6_targ->icmp6_id = icmp->un.echo.id; + icmp6_targ->icmp6_seq = icmp->un.echo.sequence; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; + clat_packet_len = CLAT_POS_PAYLOAD + 1; } else { // Unknown type/code. The type/code conversion functions have already logged an error. return 0; @@ -286,27 +284,26 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr memset(icmp_targ, 0, sizeof(struct icmphdr)); - icmp_type = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code); + icmp_type = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code); icmp_targ->type = icmp_type; icmp_targ->code = icmp6_to_icmp_code(icmp6->icmp6_type, icmp6->icmp6_code); out[pos].iov_len = sizeof(struct icmphdr); - if (pos == CLAT_POS_TRANSPORTHDR && - is_icmp6_error(icmp6->icmp6_type) && + if (pos == CLAT_POS_TRANSPORTHDR && is_icmp6_error(icmp6->icmp6_type) && icmp_type != ICMP_PARAMETERPROB) { // An ICMPv6 error we understand, one level deep. // Translate the nested packet (the one that caused the error). clat_packet_len = ipv6_packet(out, pos + 1, payload, payload_size); } else if (icmp_type == ICMP_ECHO || icmp_type == ICMP_ECHOREPLY) { // Ping packet. - icmp_targ->un.echo.id = icmp6->icmp6_id; - icmp_targ->un.echo.sequence = icmp6->icmp6_seq; - out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; - clat_packet_len = CLAT_POS_PAYLOAD + 1; + icmp_targ->un.echo.id = icmp6->icmp6_id; + icmp_targ->un.echo.sequence = icmp6->icmp6_seq; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; + clat_packet_len = CLAT_POS_PAYLOAD + 1; } else { - // Unknown type/code. The type/code conversion functions have already logged an error. + // Unknown type/code. The type/code conversion functions have already logged an error. return 0; } @@ -325,9 +322,9 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr * returns: the highest position in the output clat_packet that's filled in */ int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len) { - out[pos].iov_len = 0; - out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; - out[CLAT_POS_PAYLOAD].iov_len = len; + out[pos].iov_len = 0; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload; + out[CLAT_POS_PAYLOAD].iov_len = len; return CLAT_POS_PAYLOAD + 1; } @@ -340,17 +337,17 @@ int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payloa * new_sum - pseudo-header checksum of new header * len - size of ip payload */ -int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, size_t len) { +int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum, + uint32_t new_sum, size_t len) { const uint8_t *payload; size_t payload_size; - if(len < sizeof(struct udphdr)) { - logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)"); + if (len < sizeof(struct udphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "udp_packet/(too small)"); return 0; } - payload = (const uint8_t *) (udp + 1); + payload = (const uint8_t *)(udp + 1); payload_size = len - sizeof(struct udphdr); return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size); @@ -364,28 +361,28 @@ int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, * len - size of ip payload * returns: the highest position in the output clat_packet that's filled in */ -int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, - uint32_t old_sum, uint32_t new_sum, size_t len) { +int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum, + uint32_t new_sum, size_t len) { const uint8_t *payload; size_t payload_size, header_size; - if(len < sizeof(struct tcphdr)) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)"); + if (len < sizeof(struct tcphdr)) { + logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/(too small)"); return 0; } - if(tcp->doff < 5) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff); + if (tcp->doff < 5) { + logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/tcp header length set to less than 5: %x", tcp->doff); return 0; } - if((size_t) tcp->doff*4 > len) { - logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff); + if ((size_t)tcp->doff * 4 > len) { + logmsg_dbg(ANDROID_LOG_ERROR, "tcp_packet/tcp header length set too large: %x", tcp->doff); return 0; } - header_size = tcp->doff * 4; - payload = ((const uint8_t *) tcp) + header_size; + header_size = tcp->doff * 4; + payload = ((const uint8_t *)tcp) + header_size; payload_size = len - header_size; return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size); @@ -407,9 +404,9 @@ int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *u memcpy(udp_targ, udp, sizeof(struct udphdr)); - out[pos].iov_len = sizeof(struct udphdr); - out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; + out[pos].iov_len = sizeof(struct udphdr); + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; if (udp_targ->check) { udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum); @@ -442,23 +439,23 @@ int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *u * returns: the highest position in the output clat_packet that's filled in */ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, - size_t header_size, uint32_t old_sum, uint32_t new_sum, - const uint8_t *payload, size_t payload_size) { + size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, + size_t payload_size) { struct tcphdr *tcp_targ = out[pos].iov_base; - out[pos].iov_len = header_size; + out[pos].iov_len = header_size; if (header_size > MAX_TCP_HDR) { // A TCP header cannot be more than MAX_TCP_HDR bytes long because it's a 4-bit field that // counts in 4-byte words. So this can never happen unless there is a bug in the caller. - logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating", - header_size, MAX_TCP_HDR); + logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating", header_size, + MAX_TCP_HDR); header_size = MAX_TCP_HDR; } memcpy(tcp_targ, tcp, header_size); - out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *) payload; - out[CLAT_POS_PAYLOAD].iov_len = payload_size; + out[CLAT_POS_PAYLOAD].iov_base = (uint8_t *)payload; + out[CLAT_POS_PAYLOAD].iov_len = payload_size; tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum); @@ -474,14 +471,13 @@ void send_rawv6(int fd, clat_packet out, int iov_len) { // destination address in the packet header only affects what appears on the wire, not where the // packet is sent to. static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 }; - static struct msghdr msg = { - .msg_name = &sin6, + static struct msghdr msg = { + .msg_name = &sin6, .msg_namelen = sizeof(sin6), }; - msg.msg_iov = out, - msg.msg_iovlen = iov_len, - sin6.sin6_addr = ((struct ip6_hdr *) out[CLAT_POS_IPHDR].iov_base)->ip6_dst; + msg.msg_iov = out, msg.msg_iovlen = iov_len, + sin6.sin6_addr = ((struct ip6_hdr *)out[CLAT_POS_IPHDR].iov_base)->ip6_dst; sendmsg(fd, &msg, 0); } @@ -506,14 +502,14 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets // iovec of the packets we'll send. This gets passed down to the translation functions. clat_packet out = { - { &tun_targ, 0 }, // Tunnel header. - { iphdr, 0 }, // IP header. - { fraghdr, 0 }, // Fragment header. - { transporthdr, 0 }, // Transport layer header. - { icmp_iphdr, 0 }, // ICMP error inner IP header. - { icmp_fraghdr, 0 }, // ICMP error fragmentation header. - { icmp_transporthdr, 0 }, // ICMP error transport layer header. - { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. + { &tun_targ, 0 }, // Tunnel header. + { iphdr, 0 }, // IP header. + { fraghdr, 0 }, // Fragment header. + { transporthdr, 0 }, // Transport layer header. + { icmp_iphdr, 0 }, // ICMP error inner IP header. + { icmp_fraghdr, 0 }, // ICMP error fragmentation header. + { icmp_transporthdr, 0 }, // ICMP error transport layer header. + { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload. }; if (to_ipv6) { diff --git a/translate.h b/translate.h index aa8b736397..692affcb70 100644 --- a/translate.h +++ b/translate.h @@ -18,19 +18,19 @@ #ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include #include "clatd.h" -#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. +#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. // Calculates the checksum over all the packet components starting from pos. uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos); @@ -75,16 +75,15 @@ int icmp6_to_icmp(clat_packet out, clat_packet_index pos, const struct icmp6_hdr int generic_packet(clat_packet out, clat_packet_index pos, const uint8_t *payload, size_t len); // Translate TCP and UDP packets. -int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, - uint32_t old_sum, uint32_t new_sum, size_t len); -int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, size_t len); +int tcp_packet(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, uint32_t old_sum, + uint32_t new_sum, size_t len); +int udp_packet(clat_packet out, clat_packet_index pos, const struct udphdr *udp, uint32_t old_sum, + uint32_t new_sum, size_t len); int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *tcp, - size_t header_size, uint32_t old_sum, uint32_t new_sum, - const uint8_t *payload, size_t payload_size); + size_t header_size, uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, + size_t payload_size); int udp_translate(clat_packet out, clat_packet_index pos, const struct udphdr *udp, - uint32_t old_sum, uint32_t new_sum, - const uint8_t *payload, size_t payload_size); + uint32_t old_sum, uint32_t new_sum, const uint8_t *payload, size_t payload_size); #endif /* __TRANSLATE_H__ */ diff --git a/tun.c b/tun.c index 49f0ea782c..1120bae640 100644 --- a/tun.c +++ b/tun.c @@ -15,14 +15,14 @@ * * tun.c - tun device functions */ -#include -#include -#include #include +#include #include #include +#include #include #include +#include #include "clatd.h" @@ -33,7 +33,7 @@ int tun_open() { int fd; fd = open("/dev/tun", O_RDWR); - if(fd < 0) { + if (fd < 0) { fd = open("/dev/net/tun", O_RDWR); } @@ -51,12 +51,12 @@ int tun_alloc(char *dev, int fd) { memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN; - if( *dev ) { + if (*dev) { strncpy(ifr.ifr_name, dev, IFNAMSIZ); - ifr.ifr_name[IFNAMSIZ-1] = '\0'; + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; } - if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ + if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { close(fd); return err; } @@ -84,6 +84,4 @@ int set_nonblocking(int fd) { * iov_len - the number of entries in the clat_packet * returns: number of bytes read on success, -1 on failure */ -int send_tun(int fd, clat_packet out, int iov_len) { - return writev(fd, out, iov_len); -} +int send_tun(int fd, clat_packet out, int iov_len) { return writev(fd, out, iov_len); } From 78e76f6d2a66e6e65a28c90bf7e96fca6d13de9f Mon Sep 17 00:00:00 2001 From: junyulai Date: Thu, 13 Dec 2018 21:09:58 +0800 Subject: [PATCH 140/261] Refactor OWNERS files for Android Core Networking Bug: 120829091 Test: build/make/tools/checkowners.py -c -v path/OWNERS Change-Id: I1843d9e09731ae10183520f6ded859b9dcccd4b0 --- OWNERS | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index 8f0a28b49f..6c1758be90 100644 --- a/OWNERS +++ b/OWNERS @@ -1,2 +1,6 @@ +include platform/system/netd:/OWNERS + +# TODO: remove this after we make sure first line is working. +# Add one owner as fail-safe. lorenzo@google.com -ek@google.com + From b5e8f977bb53cbec0ef311a0ad85b7d64d228bac Mon Sep 17 00:00:00 2001 From: junyulai Date: Mon, 29 Oct 2018 23:10:15 +0800 Subject: [PATCH 141/261] Count ipv6 tx traffic of clat into clat uid. Currently, 464xlat counts its ipv6 tx traffic into root uid. When user is making ipv4 upload over ipv6-only network, ipv4 tx traffic may sometimes be counted faster then ipv6 tx traffic. Thus, NetworkStatsService may detect non-monotonic values due to 464xlat adjustment. So the solution here is to make ipv6 tx traffic counts into clat uid, and then ignore it in the framework side. Bug: 118602783 Test: 1. manually verify clatd traffic on clat uid. 2. atest clatd_test.cpp Change-Id: I834aef7184a06df89f0db8fee28e2174aa03d817 --- clatd.c | 63 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/clatd.c b/clatd.c index d8df0d815b..18d75621eb 100644 --- a/clatd.c +++ b/clatd.c @@ -154,40 +154,52 @@ void configure_tun_ip(const struct tun_data *tunnel) { } } -/* function: drop_root - * drops root privs but keeps the needed capability +/* function: set_capability + * set the permitted, effective and inheritable capabilities of the current + * thread */ -void drop_root() { +void set_capability(uint64_t target_cap) { + struct __user_cap_header_struct header = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0 // 0 = change myself + }; + struct __user_cap_data_struct cap[_LINUX_CAPABILITY_U32S_3] = {}; + + cap[0].permitted = cap[0].effective = cap[0].inheritable = target_cap; + cap[1].permitted = cap[1].effective = cap[1].inheritable = target_cap >> 32; + + if (capset(&header, cap) < 0) { + logmsg(ANDROID_LOG_FATAL, "capset failed: %s", strerror(errno)); + exit(1); + } +} + +/* function: drop_root_but_keep_caps + * drops root privs but keeps the needed capabilities + */ +void drop_root_but_keep_caps() { gid_t groups[] = { AID_INET, AID_VPN }; if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) < 0) { - logmsg(ANDROID_LOG_FATAL, "drop_root/setgroups failed: %s", strerror(errno)); + logmsg(ANDROID_LOG_FATAL, "setgroups failed: %s", strerror(errno)); exit(1); } - prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + prctl(PR_SET_KEEPCAPS, 1); - if (setgid(AID_CLAT) < 0) { - logmsg(ANDROID_LOG_FATAL, "drop_root/setgid failed: %s", strerror(errno)); + if (setresgid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { + logmsg(ANDROID_LOG_FATAL, "setresgid failed: %s", strerror(errno)); exit(1); } - if (setuid(AID_CLAT) < 0) { - logmsg(ANDROID_LOG_FATAL, "drop_root/setuid failed: %s", strerror(errno)); + if (setresuid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { + logmsg(ANDROID_LOG_FATAL, "setresuid 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); - } + // keep CAP_NET_RAW capability to open raw socket, and CAP_IPC_LOCK for mmap + // to lock memory. + set_capability((1 << CAP_NET_ADMIN) | + (1 << CAP_NET_RAW) | + (1 << CAP_IPC_LOCK)); } /* function: open_sockets @@ -494,11 +506,14 @@ int main(int argc, char **argv) { logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); + // run under a regular user but keep needed capabilities + drop_root_but_keep_caps(); + // open our raw sockets before dropping privs open_sockets(&tunnel, mark); - // run under a regular user - drop_root(); + // keeps only admin capability + set_capability(1 << CAP_NET_ADMIN); // we can create tun devices as non-root because we're in the VPN group. tunnel.fd4 = tun_open(); From eb92f48b1dc1c18d3e194d0634c617cada9cf6ff Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Jan 2019 14:59:11 +0900 Subject: [PATCH 142/261] Move main() out of clatd.c. This allows us to unit test methods that are in clatd.c. Also simplify the build file, adding a defaults stanza and grouping files in a filegroup so that both the code and the unit test have the same source files. Test: atest clatd_test Test: builds, boots, 464xlat works Change-Id: I544c3ee846abd3e38b80a2d9a4db5497fd9beb0c --- Android.bp | 98 +++++++++++++++++------------------ clatd.c | 110 --------------------------------------- clatd.h | 30 +++++------ common.h | 40 +++++++++++++++ main.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++ translate.c | 1 + translate.h | 1 + tun.c | 2 +- tun.h | 2 +- 9 files changed, 250 insertions(+), 178 deletions(-) create mode 100644 common.h create mode 100644 main.c diff --git a/Android.bp b/Android.bp index 2603472b08..7f28a3c69a 100644 --- a/Android.bp +++ b/Android.bp @@ -1,26 +1,5 @@ -// The clat daemon. -cc_binary { - name: "clatd", - - srcs: [ - "clatd.c", - "dump.c", - "checksum.c", - "translate.c", - "icmp.c", - "ipv4.c", - "ipv6.c", - "config.c", - "dns64.c", - "logging.c", - "getaddr.c", - "netlink_callbacks.c", - "netlink_msg.c", - "setif.c", - "mtu.c", - "tun.c", - "ring.c", - ], +cc_defaults { + name: "clatd_defaults", cflags: [ "-Wall", @@ -31,15 +10,51 @@ cc_binary { "-Wno-address-of-packed-member", ], + // For NETID_UNSET and MARK_UNSET. include_dirs: ["bionic/libc/dns/include"], + + // For NETID_USE_LOCAL_NAMESERVERS. header_libs: ["libnetd_client_headers"], +} + +// Code used both by the daemon and by unit tests. +filegroup { + name: "clatd_common", + srcs: [ + "checksum.c", + "config.c", + "clatd.c", + "dns64.c", + "dump.c", + "getaddr.c", + "icmp.c", + "ipv4.c", + "ipv6.c", + "logging.c", + "mtu.c", + "netlink_callbacks.c", + "netlink_msg.c", + "ring.c", + "setif.c", + "tun.c", + "translate.c", + ], +} + +// The clat daemon. +cc_binary { + name: "clatd", + defaults: ["clatd_defaults"], + srcs: [ + ":clatd_common", + "main.c" + ], static_libs: ["libnl"], shared_libs: [ "libcutils", "liblog", "libnetutils", ], - } // The configuration file. @@ -51,28 +66,18 @@ prebuilt_etc { // Unit tests. cc_test { name: "clatd_test", - cflags: [ - "-Wall", - "-Werror", - "-Wunused-parameter", - - // Bug: http://b/33566695 - "-Wno-address-of-packed-member", - ], - + defaults: ["clatd_defaults"], srcs: [ - "clatd_test.cpp", - "checksum.c", - "translate.c", - "icmp.c", - "ipv4.c", - "ipv6.c", - "logging.c", - "config.c", - "tun.c", + ":clatd_common", + "clatd_test.cpp" + ], + static_libs: [ + "libbase", + "libnetd_test_tun_interface", + "libnl", ], - shared_libs: [ + "libcutils", "liblog", "libnetutils", ], @@ -81,12 +86,7 @@ cc_test { // Microbenchmark. cc_test { name: "clatd_microbenchmark", - - cflags: [ - "-Wall", - "-Werror", - "-Wunused-parameter", - ], + defaults: ["clatd_defaults"], srcs: [ "clatd_microbenchmark.c", "checksum.c", diff --git a/clatd.c b/clatd.c index 18d75621eb..2ef10a5f24 100644 --- a/clatd.c +++ b/clatd.c @@ -53,8 +53,6 @@ #include "translate.h" #include "tun.h" -#define DEVICEPREFIX "v4-" - /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 @@ -426,17 +424,6 @@ void event_loop(struct tun_data *tunnel) { } } -/* function: print_help - * in case the user is running this on the command line - */ -void print_help() { - printf("android-clat arguments:\n"); - printf("-i [uplink interface]\n"); - printf("-p [plat prefix]\n"); - printf("-n [NetId]\n"); - printf("-m [socket mark]\n"); -} - /* function: parse_unsigned * parses a string as a decimal/hex/octal unsigned integer * str - the string to parse @@ -447,100 +434,3 @@ int parse_unsigned(const char *str, unsigned *out) { *out = strtoul(str, &end_ptr, 0); return *str && !*end_ptr; } - -/* function: main - * allocate and setup the tun device, then run the event loop - */ -int main(int argc, char **argv) { - struct tun_data tunnel; - int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; - unsigned net_id = NETID_UNSET; - uint32_t mark = MARK_UNSET; - unsigned len; - - while ((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { - switch (opt) { - case 'i': - uplink_interface = optarg; - break; - case 'p': - plat_prefix = optarg; - break; - case 'n': - net_id_str = optarg; - break; - case 'm': - mark_str = optarg; - break; - case 'h': - print_help(); - exit(0); - default: - logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt); - exit(1); - } - } - - if (uplink_interface == NULL) { - logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); - exit(1); - } - - if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { - logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); - exit(1); - } - - if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { - logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); - exit(1); - } - - len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); - if (len >= sizeof(tunnel.device4)) { - logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); - exit(1); - } - - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, - uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); - - // run under a regular user but keep needed capabilities - drop_root_but_keep_caps(); - - // open our raw sockets before dropping privs - open_sockets(&tunnel, mark); - - // keeps only admin capability - set_capability(1 << CAP_NET_ADMIN); - - // we can create tun devices as non-root because we're in the VPN group. - tunnel.fd4 = tun_open(); - if (tunnel.fd4 < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); - exit(1); - } - - // When run from netd, the environment variable ANDROID_DNS_MODE is set to - // "local", but that only works for the netd process itself. Removing the - // following line causes XLAT failure in permissive mode. - unsetenv("ANDROID_DNS_MODE"); - - configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); - - update_clat_ipv6_address(&tunnel, uplink_interface); - - // Loop until someone sends us a signal or brings down the tun interface. - if (signal(SIGTERM, stop_loop) == SIG_ERR) { - logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); - exit(1); - } - - event_loop(&tunnel); - - logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); - del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); - - return 0; -} diff --git a/clatd.h b/clatd.h index 327a948d9c..32f8cd5be0 100644 --- a/clatd.h +++ b/clatd.h @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * clatd.h - main system definitions + * clatd.h - main routines used by clatd */ #ifndef __CLATD_H__ #define __CLATD_H__ #include +struct tun_data; + #define MAXMTU 1500 #define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) #define CLATD_VERSION "1.4" @@ -32,21 +34,15 @@ // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 -// A clat_packet is an array of iovec structures representing a packet that we are translating. -// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain -// specific parts of the packet. The packet_* functions operate on all the packet segments past a -// given position. -typedef enum { - CLAT_POS_TUNHDR, - CLAT_POS_IPHDR, - CLAT_POS_FRAGHDR, - CLAT_POS_TRANSPORTHDR, - CLAT_POS_ICMPERR_IPHDR, - CLAT_POS_ICMPERR_FRAGHDR, - CLAT_POS_ICMPERR_TRANSPORTHDR, - CLAT_POS_PAYLOAD, - CLAT_POS_MAX -} clat_packet_index; -typedef struct iovec clat_packet[CLAT_POS_MAX]; +void stop_loop(); +void set_capability(uint64_t target_cap); +void drop_root_but_keep_caps(); +void open_sockets(struct tun_data *tunnel, uint32_t mark); +int ipv6_address_changed(const char *interface); +int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface); +void configure_interface(const char *uplink_interface, const char *plat_prefix, + struct tun_data *tunnel, unsigned net_id); +void event_loop(struct tun_data *tunnel); +int parse_unsigned(const char *str, unsigned *out); #endif /* __CLATD_H__ */ diff --git a/common.h b/common.h new file mode 100644 index 0000000000..e9551ee211 --- /dev/null +++ b/common.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * common.h - common definitions + */ +#ifndef __CLATD_COMMON_H__ +#define __CLATD_COMMON_H__ + +#include + +// A clat_packet is an array of iovec structures representing a packet that we are translating. +// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain +// specific parts of the packet. The packet_* functions operate on all the packet segments past a +// given position. +typedef enum { + CLAT_POS_TUNHDR, + CLAT_POS_IPHDR, + CLAT_POS_FRAGHDR, + CLAT_POS_TRANSPORTHDR, + CLAT_POS_ICMPERR_IPHDR, + CLAT_POS_ICMPERR_FRAGHDR, + CLAT_POS_ICMPERR_TRANSPORTHDR, + CLAT_POS_PAYLOAD, + CLAT_POS_MAX +} clat_packet_index; +typedef struct iovec clat_packet[CLAT_POS_MAX]; + +#endif /* __CLATD_COMMON_H__ */ diff --git a/main.c b/main.c new file mode 100644 index 0000000000..e717498350 --- /dev/null +++ b/main.c @@ -0,0 +1,144 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * main.c - main function + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "resolv_netid.h" + +#include "clatd.h" +#include "common.h" +#include "config.h" +#include "logging.h" +#include "setif.h" +#include "tun.h" + +#define DEVICEPREFIX "v4-" + +/* function: print_help + * in case the user is running this on the command line + */ +void print_help() { + printf("android-clat arguments:\n"); + printf("-i [uplink interface]\n"); + printf("-p [plat prefix]\n"); + printf("-n [NetId]\n"); + printf("-m [socket mark]\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, *net_id_str = NULL, *mark_str = NULL; + unsigned net_id = NETID_UNSET; + uint32_t mark = MARK_UNSET; + unsigned len; + + while ((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { + switch (opt) { + case 'i': + uplink_interface = optarg; + break; + case 'p': + plat_prefix = optarg; + break; + case 'n': + net_id_str = optarg; + break; + case 'm': + mark_str = optarg; + break; + case 'h': + print_help(); + exit(0); + default: + logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt); + exit(1); + } + } + + if (uplink_interface == NULL) { + logmsg(ANDROID_LOG_FATAL, "clatd called without an interface"); + exit(1); + } + + if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { + logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); + exit(1); + } + + if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { + logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); + exit(1); + } + + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); + if (len >= sizeof(tunnel.device4)) { + logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); + exit(1); + } + + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, + uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); + + // run under a regular user but keep needed capabilities + drop_root_but_keep_caps(); + + // open our raw sockets before dropping privs + open_sockets(&tunnel, mark); + + // keeps only admin capability + set_capability(1 << CAP_NET_ADMIN); + + // we can create tun devices as non-root because we're in the VPN group. + tunnel.fd4 = tun_open(); + if (tunnel.fd4 < 0) { + logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); + exit(1); + } + + // When run from netd, the environment variable ANDROID_DNS_MODE is set to + // "local", but that only works for the netd process itself. Removing the + // following line causes XLAT failure in permissive mode. + unsetenv("ANDROID_DNS_MODE"); + + configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); + + update_clat_ipv6_address(&tunnel, uplink_interface); + + // Loop until someone sends us a signal or brings down the tun interface. + if (signal(SIGTERM, stop_loop) == SIG_ERR) { + logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno)); + exit(1); + } + + event_loop(&tunnel); + + logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); + del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); + + return 0; +} diff --git a/translate.c b/translate.c index 58a7e9d72a..df3d020de4 100644 --- a/translate.c +++ b/translate.c @@ -19,6 +19,7 @@ #include "checksum.h" #include "clatd.h" +#include "common.h" #include "config.h" #include "debug.h" #include "icmp.h" diff --git a/translate.h b/translate.h index 692affcb70..0e520f710b 100644 --- a/translate.h +++ b/translate.h @@ -29,6 +29,7 @@ #include #include "clatd.h" +#include "common.h" #define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words. diff --git a/tun.c b/tun.c index 1120bae640..406fc2fff9 100644 --- a/tun.c +++ b/tun.c @@ -24,7 +24,7 @@ #include #include -#include "clatd.h" +#include "common.h" /* function: tun_open * tries to open the tunnel device diff --git a/tun.h b/tun.h index bcdd10e3b0..f0449b92ac 100644 --- a/tun.h +++ b/tun.h @@ -20,7 +20,7 @@ #include -#include "clatd.h" +#include "common.h" #include "ring.h" struct tun_data { From 72f770c4ad644ad49371531ac030196eff32076b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Jan 2019 15:00:28 +0900 Subject: [PATCH 143/261] Test clatd methods that affect interface IP addresses. Depend on tun_interface so we can add tests to clatd_test that use a real interface instead of mocking everything out. Add a simple test for determining the clat IPv6 address. Test: atest clatd_test Change-Id: I1f10c372388a7c663bebab3c45b206036a264673 --- clatd_test.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 2a10101f89..4f998ea319 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -24,17 +24,22 @@ #include #include +#include "tun_interface.h" extern "C" { #include "checksum.h" #include "clatd.h" #include "config.h" +#include "getaddr.h" #include "translate.h" +#include "tun.h" } // For convenience. #define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0]) +using android::net::TunInterface; + // Default translation parameters. static const char kIPv4LocalAddr[] = "192.0.0.4"; static const char kIPv6LocalAddr[] = "2001:db8:0:b11::464"; @@ -557,15 +562,26 @@ struct clat_config Global_Clatd_Config; class ClatdTest : public ::testing::Test { protected: + static TunInterface sTun; + virtual void SetUp() { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); - inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); Global_Clatd_Config.ipv6_host_id = in6addr_any; Global_Clatd_Config.use_dynamic_iid = 1; + Global_Clatd_Config.default_pdp_interface = const_cast(sTun.name().c_str()); } + + // Static because setting up the tun interface takes about 40ms. + static void SetUpTestCase() { ASSERT_EQ(0, sTun.init()); } + + // Closing the socket removes the interface and IP addresses. + static void TearDownTestCase() { sTun.destroy(); } }; +TunInterface ClatdTest::sTun; + void expect_ipv6_addr_equal(struct in6_addr *expected, struct in6_addr *actual) { if (!IN6_ARE_ADDR_EQUAL(expected, actual)) { char expected_str[INET6_ADDRSTRLEN], actual_str[INET6_ADDRSTRLEN]; @@ -868,6 +884,9 @@ TEST_F(ClatdTest, AdjustChecksum) { } TEST_F(ClatdTest, Translate) { + // This test uses hardcoded packets so the clatd address must be fixed. + inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + uint8_t udp_ipv4[] = { IPV4_UDP_HEADER UDP_HEADER PAYLOAD }; uint8_t udp_ipv6[] = { IPV6_UDP_HEADER UDP_HEADER PAYLOAD }; fix_udp_checksum(udp_ipv4); @@ -886,6 +905,9 @@ TEST_F(ClatdTest, Translate) { } TEST_F(ClatdTest, Fragmentation) { + // This test uses hardcoded packets so the clatd address must be fixed. + inet_pton(AF_INET6, kIPv6LocalAddr, &Global_Clatd_Config.ipv6_local_subnet); + check_fragment_translation(kIPv4Fragments, kIPv4FragLengths, kIPv6Fragments, kIPv6FragLengths, ARRAYSIZE(kIPv4Fragments), "IPv4->IPv6 fragment translation"); @@ -914,7 +936,7 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); - ASSERT_NE((uint32_t)0x00000464, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); // Check that translating UDP packets is checksum-neutral. First, IPv4. @@ -932,3 +954,39 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20, "UDP/IPv4 -> UDP/IPv6 checksum neutral"); } + +TEST_F(ClatdTest, GetInterfaceIp) { + union anyip *ip = getinterface_ip(sTun.name().c_str(), AF_INET6); + ASSERT_NE(nullptr, ip); + in6_addr expected = sTun.srcAddr(); + in6_addr actual = ip->ip6; + expect_ipv6_addr_equal(&expected, &actual); +} + +TEST_F(ClatdTest, UpdateIpv6Address) { + // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. + struct tun_data tunnel = { + .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), + .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), + }; + + // Run update_clat_ipv6_address with no local IID yet picked. + ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); + ASSERT_EQ(1, update_clat_ipv6_address(&tunnel, sTun.name().c_str())); + + // Check that it generated an IID in the same prefix as the address assigned to the interface, + // and that the IID is not the default IID. + in6_addr addr = sTun.srcAddr(); + EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &addr)); + EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&Global_Clatd_Config.ipv6_local_subnet, &addr)); + EXPECT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + EXPECT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + + // Check that the packet socket is bound to the interface. We can't check the socket filter + // because there is no way to fetch it from the kernel. + sockaddr_ll sll; + socklen_t len = sizeof(sll); + ASSERT_EQ(0, getsockname(tunnel.read_fd6, reinterpret_cast(&sll), &len)); + EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); + EXPECT_EQ(sll.sll_ifindex, sTun.ifindex()); +} From 66deecd38f88e4e7944ed5841e10051ef5f0b7d6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Jan 2019 12:27:27 +0900 Subject: [PATCH 144/261] Drop support for updating IPv6 addresses in clatd. Currently, when clatd detects that the IPv6 address on its interface has changed, it updates its configuration accordingly. This complicates the code, and it prevents us from determining the IPv6 address in netd instead of clatd. Stop doing this, and simply exit instead. The framework will notice that the interface is gone and restart clatd, which will then have the correct configuration (and, unlike current code, the correct NAT64 prefix, if that has changed as well). This change also allows us to drop CAP_NET_ADMIN and run clatd as an unprivileged user with no capabilities. Test: atest clatd_test Test: changing IPv6 address on interface causes clatd to exit Test: "adb shell cat /proc/$(adb shell pidof clatd)/status | grep Cap" Change-Id: I296dcb185f603f685223b24ffefdf7a5c22b66e6 --- clatd.c | 57 ++++++++++++++++++++++++++++++-------------------- clatd.h | 2 +- clatd_test.cpp | 37 +++++++++++++++++++++++++++++--- main.c | 3 ++- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/clatd.c b/clatd.c index 2ef10a5f24..bcbd9c5158 100644 --- a/clatd.c +++ b/clatd.c @@ -228,13 +228,36 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { } } -/* function: update_clat_ipv6_address +int ipv6_address_changed(const char *interface) { + union anyip *interface_ip; + + interface_ip = getinterface_ip(interface, AF_INET6); + if (!interface_ip) { + logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); + return 1; + } + + if (!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { + char oldstr[INET6_ADDRSTRLEN]; + char newstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, oldstr, sizeof(oldstr)); + inet_ntop(AF_INET6, &interface_ip->ip6, newstr, sizeof(newstr)); + logmsg(ANDROID_LOG_INFO, "IPv6 prefix on %s changed: %s -> %s", interface, oldstr, newstr); + free(interface_ip); + return 1; + } else { + free(interface_ip); + return 0; + } +} + +/* function: configure_clat_ipv6_address * picks the clat IPv6 address and configures packet translation to use it. * tunnel - tun device data * interface - uplink interface name * returns: 1 on success, 0 on failure */ -int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) { +int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) { union anyip *interface_ip; char addrstr[INET6_ADDRSTRLEN]; @@ -245,27 +268,10 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac return 0; } - // If our prefix hasn't changed, do nothing. (If this is the first time we configure an IPv6 - // address, Global_Clatd_Config.ipv6_local_subnet will be ::, which won't match our new prefix.) - if (ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { - free(interface_ip); - return 1; - } - // Generate an interface ID. config_generate_local_ipv6_subnet(&interface_ip->ip6); inet_ntop(AF_INET6, &interface_ip->ip6, addrstr, sizeof(addrstr)); - - if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)) { - // Startup. - logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface); - } else { - // Prefix change. - char from_addr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, from_addr, sizeof(from_addr)); - logmsg(ANDROID_LOG_INFO, "clat IPv6 address changed from %s to %s", from_addr, addrstr); - del_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet); - } + logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface); // Start translating packets to the new prefix. Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; @@ -276,7 +282,7 @@ int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interfac if (!configure_packet_socket(tunnel->read_fd6)) { // Things aren't going to work. Bail out and hope we have better luck next time. // We don't log an error here because configure_packet_socket has already done so. - exit(1); + return 0; } return 1; @@ -330,6 +336,10 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, } configure_tun_ip(tunnel); + + if (!configure_clat_ipv6_address(tunnel, uplink_interface)) { + exit(1); + } } /* function: read_packet @@ -418,8 +428,9 @@ void event_loop(struct tun_data *tunnel) { time_t now = time(NULL); if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { - update_clat_ipv6_address(tunnel, Global_Clatd_Config.default_pdp_interface); - last_interface_poll = now; + if (ipv6_address_changed(Global_Clatd_Config.default_pdp_interface)) { + break; + } } } } diff --git a/clatd.h b/clatd.h index 32f8cd5be0..67ba99066f 100644 --- a/clatd.h +++ b/clatd.h @@ -39,7 +39,7 @@ void set_capability(uint64_t target_cap); void drop_root_but_keep_caps(); void open_sockets(struct tun_data *tunnel, uint32_t mark); int ipv6_address_changed(const char *interface); -int update_clat_ipv6_address(const struct tun_data *tunnel, const char *interface); +int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface); void configure_interface(const char *uplink_interface, const char *plat_prefix, struct tun_data *tunnel, unsigned net_id); void event_loop(struct tun_data *tunnel); diff --git a/clatd_test.cpp b/clatd_test.cpp index 4f998ea319..81af41d096 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -24,6 +24,8 @@ #include #include + +#include "netutils/ifc.h" #include "tun_interface.h" extern "C" { @@ -963,16 +965,16 @@ TEST_F(ClatdTest, GetInterfaceIp) { expect_ipv6_addr_equal(&expected, &actual); } -TEST_F(ClatdTest, UpdateIpv6Address) { +TEST_F(ClatdTest, ConfigureIpv6Address) { // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. struct tun_data tunnel = { .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), }; - // Run update_clat_ipv6_address with no local IID yet picked. + // Run configure_clat_ipv6_address. ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); - ASSERT_EQ(1, update_clat_ipv6_address(&tunnel, sTun.name().c_str())); + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str())); // Check that it generated an IID in the same prefix as the address assigned to the interface, // and that the IID is not the default IID. @@ -990,3 +992,32 @@ TEST_F(ClatdTest, UpdateIpv6Address) { EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); EXPECT_EQ(sll.sll_ifindex, sTun.ifindex()); } + +TEST_F(ClatdTest, Ipv6AddressChanged) { + // Configure the clat IPv6 address. + struct tun_data tunnel = { + .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), + .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), + }; + const char *ifname = sTun.name().c_str(); + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname)); + EXPECT_EQ(0, ipv6_address_changed(ifname)); + EXPECT_EQ(0, ipv6_address_changed(ifname)); + + // Change the IP address on the tun interface to a new prefix. + char srcaddr[INET6_ADDRSTRLEN]; + char dstaddr[INET6_ADDRSTRLEN]; + ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.srcAddr(), srcaddr, sizeof(srcaddr))); + ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.dstAddr(), dstaddr, sizeof(dstaddr))); + EXPECT_EQ(0, ifc_del_address(ifname, srcaddr, 64)); + EXPECT_EQ(0, ifc_del_address(ifname, dstaddr, 64)); + + // Check that we can tell that the address has changed. + EXPECT_EQ(0, ifc_add_address(ifname, "2001:db8::1:2", 64)); + EXPECT_EQ(1, ipv6_address_changed(ifname)); + EXPECT_EQ(1, ipv6_address_changed(ifname)); + + // Restore the tun interface configuration. + sTun.destroy(); + ASSERT_EQ(0, sTun.init()); +} diff --git a/main.c b/main.c index e717498350..11c51a5ad2 100644 --- a/main.c +++ b/main.c @@ -127,7 +127,8 @@ int main(int argc, char **argv) { configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); - update_clat_ipv6_address(&tunnel, uplink_interface); + // Drop all remaining capabilities. + set_capability(0); // Loop until someone sends us a signal or brings down the tun interface. if (signal(SIGTERM, stop_loop) == SIG_ERR) { From 33351930039785304a283b072251a48cfae30a35 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Jan 2019 19:36:51 +0900 Subject: [PATCH 145/261] Add clatd_test to presubmit. Following the instructions at: https://source.android.com/compatibility/tests/development/test-mapping Test: cd external/android-clat; atest Change-Id: Ifca302f53b38d47200d6a1651280391297f14edc --- Android.bp | 1 + TEST_MAPPING | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 TEST_MAPPING diff --git a/Android.bp b/Android.bp index 7f28a3c69a..82295f3fd3 100644 --- a/Android.bp +++ b/Android.bp @@ -81,6 +81,7 @@ cc_test { "liblog", "libnetutils", ], + test_suites: ["device-tests"], } // Microbenchmark. diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000000..c6d599d63d --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "clatd_test" + } + ] +} From f0fac8631a58ee6f12755e35f0476bef3f9bbb97 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 11 Jan 2019 18:10:11 +0900 Subject: [PATCH 146/261] Allow passing in the IPv4 and IPv6 addresses on the command line. This allows the address to be determined by netd and passed in, which makes it possible for the rest of the OS to know what the clatd IPv6 address is. Bug: 65674744 Test: atest clatd_test Test: IPv4 on IPv6-only networks continues to work Test: clatd continues to avoid existing IPv4 addresses in 192.0.0.0/29 Test: passing NAT64 prefix, IPv4/IPv6 addresses on command line works Change-Id: I8519c2f01b44022ef036c80aa0df32cd76003055 --- clatd.c | 110 ++++++++++++++++++++++++++++++++++++------------ clatd.h | 8 ++-- clatd_test.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++---- main.c | 19 +++++++-- 4 files changed, 207 insertions(+), 42 deletions(-) diff --git a/clatd.c b/clatd.c index bcbd9c5158..3ccceea6fb 100644 --- a/clatd.c +++ b/clatd.c @@ -110,13 +110,11 @@ int configure_packet_socket(int sock) { return 1; } -/* function: configure_tun_ip - * configures the ipv4 and ipv6 addresses on the tunnel interface - * tunnel - tun device data +/* function: ipv4_address_generate + * picks a free IPv4 address from the local subnet or exits if there are no free addresses + * returns: the IPv4 address as an in_addr_t */ -void configure_tun_ip(const struct tun_data *tunnel) { - int status; - +static in_addr_t ipv4_address_generate() { // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically, // there is a race here - if another clatd calls config_select_ipv4_address after we do, but // before we call add_address, it can end up having the same IP address as we do. But the time @@ -131,22 +129,49 @@ void configure_tun_ip(const struct tun_data *tunnel) { Global_Clatd_Config.ipv4_local_prefixlen); exit(1); } - Global_Clatd_Config.ipv4_local_subnet.s_addr = localaddr; + return localaddr; +} - // Configure the interface before bringing it up. As soon as we bring the interface up, the - // framework will be notified and will assume the interface's configuration has been finalized. - status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32, - &Global_Clatd_Config.ipv4_local_subnet); - if (status < 0) { - logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status)); +/* function: ipv4_address_from_cmdline + * configures the IPv4 address specified on the command line, or exits if the address is not valid + * v4_addr - a string, the IPv4 address + * returns: the IPv4 address as an in_addr_t + */ +static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) { + in_addr_t localaddr; + if (!inet_pton(AF_INET, v4_addr, &localaddr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); exit(1); } + return localaddr; +} + +/* function: configure_tun_ip + * configures the ipv4 and ipv6 addresses on the tunnel interface + * tunnel - tun device data + */ +void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr) { + if (v4_addr) { + Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr); + } else { + Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate(); + } char addrstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr)); logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4); - if ((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) { + // Configure the interface before bringing it up. As soon as we bring the interface up, the + // framework will be notified and will assume the interface's configuration has been finalized. + int status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32, + &Global_Clatd_Config.ipv4_local_subnet); + if (status < 0) { + logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status)); + exit(1); + } + + status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu); + if (status < 0) { logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status)); exit(1); } @@ -251,15 +276,13 @@ int ipv6_address_changed(const char *interface) { } } -/* function: configure_clat_ipv6_address - * picks the clat IPv6 address and configures packet translation to use it. - * tunnel - tun device data +/* function: clat_ipv6_address_from_interface + * picks the clat IPv6 address based on the interface address * interface - uplink interface name * returns: 1 on success, 0 on failure */ -int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) { +static int clat_ipv6_address_from_interface(const char *interface) { union anyip *interface_ip; - char addrstr[INET6_ADDRSTRLEN]; // TODO: check that the prefix length is /64. interface_ip = getinterface_ip(interface, AF_INET6); @@ -270,13 +293,48 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter // Generate an interface ID. config_generate_local_ipv6_subnet(&interface_ip->ip6); - inet_ntop(AF_INET6, &interface_ip->ip6, addrstr, sizeof(addrstr)); + + Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; + free(interface_ip); + return 1; +} + +/* function: clat_ipv6_address_from_cmdline + * parses the clat IPv6 address from the command line + * v4_addr - a string, the IPv6 address + * returns: 1 on success, 0 on failure + */ +static int clat_ipv6_address_from_cmdline(const char *v6_addr) { + if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + return 0; + } + + return 1; +} + +/* function: configure_clat_ipv6_address + * picks the clat IPv6 address and configures packet translation to use it. + * tunnel - tun device data + * interface - uplink interface name + * returns: 1 on success, 0 on failure + */ +int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, + const char *v6_addr) { + int ret; + if (v6_addr) { + ret = clat_ipv6_address_from_cmdline(v6_addr); + } else { + ret = clat_ipv6_address_from_interface(interface); + } + if (!ret) return 0; + + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface); // Start translating packets to the new prefix. - Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface); - free(interface_ip); // Update our packet socket filter to reflect the new 464xlat IP address. if (!configure_packet_socket(tunnel->read_fd6)) { @@ -295,8 +353,8 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter * tunnel - tun device data * net_id - NetID to use, NETID_UNSET indicates use of default network */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, - struct tun_data *tunnel, unsigned net_id) { +void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, + const char *v6_addr, struct tun_data *tunnel, unsigned net_id) { int error; if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { @@ -335,9 +393,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, exit(1); } - configure_tun_ip(tunnel); + configure_tun_ip(tunnel, v4_addr); - if (!configure_clat_ipv6_address(tunnel, uplink_interface)) { + if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { exit(1); } } diff --git a/clatd.h b/clatd.h index 67ba99066f..f7f7315ff0 100644 --- a/clatd.h +++ b/clatd.h @@ -35,13 +35,15 @@ struct tun_data; #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 void stop_loop(); +void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr); void set_capability(uint64_t target_cap); void drop_root_but_keep_caps(); void open_sockets(struct tun_data *tunnel, uint32_t mark); int ipv6_address_changed(const char *interface); -int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface); -void configure_interface(const char *uplink_interface, const char *plat_prefix, - struct tun_data *tunnel, unsigned net_id); +int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, + const char *src_addr); +void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, + const char *v6, struct tun_data *tunnel, unsigned net_id); void event_loop(struct tun_data *tunnel); int parse_unsigned(const char *str, unsigned *out); diff --git a/clatd_test.cpp b/clatd_test.cpp index 81af41d096..7705a36bc4 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -294,8 +294,8 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) { void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets, uint8_t *reassembled, size_t *reassembled_len, const char *msg) { - struct iphdr *ip = NULL; - struct ip6_hdr *ip6 = NULL; + struct iphdr *ip = nullptr; + struct ip6_hdr *ip6 = nullptr; size_t total_length, pos = 0; uint8_t protocol = 0; uint8_t version = ip_version(fragments[0]); @@ -560,6 +560,21 @@ int get_transport_checksum(const uint8_t *packet) { } } +static tun_data makeTunData() { + // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. + return { + .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), + .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), + .fd4 = socket(AF_UNIX, SOCK_DGRAM, 0), + }; +} + +void freeTunData(tun_data *tunnel) { + close(tunnel->write_fd6); + close(tunnel->read_fd6); + close(tunnel->fd4); +} + struct clat_config Global_Clatd_Config; class ClatdTest : public ::testing::Test { @@ -757,6 +772,55 @@ TEST_F(ClatdTest, SelectIPv4Address) { EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); } +TEST_F(ClatdTest, ConfigureTunIp) { + addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; + config_is_ipv4_address_free = over6_free; + + Global_Clatd_Config.ipv4_local_prefixlen = 29; + Global_Clatd_Config.ipv4mtu = 1472; + + // Create an interface for configure_tun_ip to configure and bring up. + TunInterface v4Iface; + ASSERT_EQ(0, v4Iface.init()); + struct tun_data tunnel = makeTunData(); + strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); + + configure_tun_ip(&tunnel, nullptr /* v4_addr */); + EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr); + + union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); + EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr); + free(ip); + + config_is_ipv4_address_free = orig_config_is_ipv4_address_free; + v4Iface.destroy(); +} + +TEST_F(ClatdTest, ConfigureTunIpManual) { + addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; + config_is_ipv4_address_free = over6_free; + + Global_Clatd_Config.ipv4_local_prefixlen = 29; + Global_Clatd_Config.ipv4mtu = 1472; + + // Create an interface for configure_tun_ip to configure and bring up. + TunInterface v4Iface; + ASSERT_EQ(0, v4Iface.init()); + struct tun_data tunnel = makeTunData(); + strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); + + configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */); + EXPECT_EQ(inet_addr("192.0.2.1"), Global_Clatd_Config.ipv4_local_subnet.s_addr); + + union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); + ASSERT_NE(nullptr, ip); + EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); + free(ip); + + config_is_ipv4_address_free = orig_config_is_ipv4_address_free; + v4Iface.destroy(); +} + TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks the data. uint8_t v4_header[] = { IPV4_UDP_HEADER }; @@ -965,16 +1029,22 @@ TEST_F(ClatdTest, GetInterfaceIp) { expect_ipv6_addr_equal(&expected, &actual); } +void expectSocketBound(int ifindex, int sock) { + // Check that the packet socket is bound to the interface. We can't check the socket filter + // because there is no way to fetch it from the kernel. + sockaddr_ll sll; + socklen_t len = sizeof(sll); + ASSERT_EQ(0, getsockname(sock, reinterpret_cast(&sll), &len)); + EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); + EXPECT_EQ(ifindex, sll.sll_ifindex); +} + TEST_F(ClatdTest, ConfigureIpv6Address) { - // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. - struct tun_data tunnel = { - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), - .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), - }; + struct tun_data tunnel = makeTunData(); // Run configure_clat_ipv6_address. ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str())); + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), nullptr /* v6_addr */)); // Check that it generated an IID in the same prefix as the address assigned to the interface, // and that the IID is not the default IID. @@ -984,6 +1054,26 @@ TEST_F(ClatdTest, ConfigureIpv6Address) { EXPECT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); EXPECT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + expectSocketBound(sTun.ifindex(), tunnel.read_fd6); + + freeTunData(&tunnel); +} + +TEST_F(ClatdTest, ConfigureIpv6AddressCommandLine) { + struct tun_data tunnel = makeTunData(); + + ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); + + const char *addrStr = "2001:db8::f00"; + in6_addr addr; + ASSERT_EQ(1, inet_pton(AF_INET6, addrStr, &addr)); + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), addrStr)); + + EXPECT_EQ(htonl(0x20010db8), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[0]); + EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[1]); + EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[2]); + EXPECT_EQ(htonl(0x00000f00), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); + // Check that the packet socket is bound to the interface. We can't check the socket filter // because there is no way to fetch it from the kernel. sockaddr_ll sll; @@ -991,6 +1081,10 @@ TEST_F(ClatdTest, ConfigureIpv6Address) { ASSERT_EQ(0, getsockname(tunnel.read_fd6, reinterpret_cast(&sll), &len)); EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); EXPECT_EQ(sll.sll_ifindex, sTun.ifindex()); + + expectSocketBound(sTun.ifindex(), tunnel.read_fd6); + + freeTunData(&tunnel); } TEST_F(ClatdTest, Ipv6AddressChanged) { @@ -1000,7 +1094,7 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), }; const char *ifname = sTun.name().c_str(); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname)); + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr)); EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); diff --git a/main.c b/main.c index 11c51a5ad2..54d12d17c2 100644 --- a/main.c +++ b/main.c @@ -42,6 +42,8 @@ void print_help() { printf("android-clat arguments:\n"); printf("-i [uplink interface]\n"); printf("-p [plat prefix]\n"); + printf("-4 [IPv4 address]\n"); + printf("-6 [IPv6 address]\n"); printf("-n [NetId]\n"); printf("-m [socket mark]\n"); } @@ -53,11 +55,12 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; + char *v4_addr = NULL, *v6_addr = NULL; unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:n:m:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -65,6 +68,12 @@ int main(int argc, char **argv) { case 'p': plat_prefix = optarg; break; + case '4': + v4_addr = optarg; + break; + case '6': + v6_addr = optarg; + break; case 'n': net_id_str = optarg; break; @@ -101,8 +110,10 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION, - uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)"); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s plat=%s v4=%s v6=%s", + CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)", + mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)", + v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); // run under a regular user but keep needed capabilities drop_root_but_keep_caps(); @@ -125,7 +136,7 @@ int main(int argc, char **argv) { // following line causes XLAT failure in permissive mode. unsetenv("ANDROID_DNS_MODE"); - configure_interface(uplink_interface, plat_prefix, &tunnel, net_id); + configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id); // Drop all remaining capabilities. set_capability(0); From a50fb46fcf66e3d8dd1afe544f6ec9077508b83b Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 15 Jan 2019 16:55:14 -0800 Subject: [PATCH 147/261] clatd's .clang-format shouldn't be kept in sync with system/core Test: manual Change-Id: I1d912813fdcf886813595a3eba6af51146640fd7 --- .clang-format | 3 --- 1 file changed, 3 deletions(-) diff --git a/.clang-format b/.clang-format index 6682f77177..f1debbd461 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,3 @@ -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# Please keep this file in sync with system/core/.clang-format-4 -# BasedOnStyle: Google AlignConsecutiveAssignments: true AlignEscapedNewlines: Right From 98de5958cf64f76728edafc19f07a7e01878c1ee Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sun, 20 Jan 2019 11:45:03 +0900 Subject: [PATCH 148/261] Move checksum code from clatd to libnetutils. This allows us to use it elsewhere, for example, in netd. Bug: 65674744 Test: builds, boots Test: atest clatd_test clatd_microbenchmark Change-Id: Ic2cea1cd658ffd23ba9fbe45cb6b28e3b5312005 --- Android.bp | 5 +- checksum.c | 142 ----------------------------------------- checksum.h | 34 ---------- clatd_microbenchmark.c | 2 +- clatd_test.cpp | 2 +- config.c | 2 +- dump.c | 3 +- ipv4.c | 3 +- ipv6.c | 3 +- translate.c | 3 +- 10 files changed, 14 insertions(+), 185 deletions(-) delete mode 100644 checksum.c delete mode 100644 checksum.h diff --git a/Android.bp b/Android.bp index 82295f3fd3..e0eeb2e3a1 100644 --- a/Android.bp +++ b/Android.bp @@ -21,7 +21,6 @@ cc_defaults { filegroup { name: "clatd_common", srcs: [ - "checksum.c", "config.c", "clatd.c", "dns64.c", @@ -90,7 +89,9 @@ cc_test { defaults: ["clatd_defaults"], srcs: [ "clatd_microbenchmark.c", - "checksum.c", "tun.c", ], + shared_libs: [ + "libnetutils", + ], } diff --git a/checksum.c b/checksum.c deleted file mode 100644 index 11b51493a3..0000000000 --- a/checksum.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * checksum.c - ipv4/ipv6 checksum calculation - */ -#include -#include -#include -#include -#include -#include -#include - -#include "checksum.h" - -/* function: ip_checksum_add - * adds data to a checksum - * current - the current checksum (or 0 to start a new checksum) - * data - the data to add to the checksum - * len - length of data - */ -uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { - uint32_t checksum = current; - int left = len; - const uint16_t *data_16 = data; - - while (left > 1) { - checksum += *data_16; - data_16++; - left -= 2; - } - if (left) { - checksum += *(uint8_t *)data_16; - } - - return checksum; -} - -/* function: ip_checksum_fold - * folds a 32-bit partial checksum into 16 bits - * temp_sum - sum from ip_checksum_add - * returns: the folded checksum in network byte order - */ -uint16_t ip_checksum_fold(uint32_t temp_sum) { - while (temp_sum > 0xffff) { - temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); - } - return temp_sum; -} - -/* function: ip_checksum_finish - * folds and closes the checksum - * temp_sum - sum from ip_checksum_add - * returns: a header checksum value in network byte order - */ -uint16_t ip_checksum_finish(uint32_t temp_sum) { return ~ip_checksum_fold(temp_sum); } - -/* function: ip_checksum - * combined ip_checksum_add and ip_checksum_finish - * data - data to checksum - * len - length of data - */ -uint16_t ip_checksum(const void *data, int len) { - uint32_t temp_sum; - - temp_sum = ip_checksum_add(0, data, len); - return ip_checksum_finish(temp_sum); -} - -/* function: ipv6_pseudo_header_checksum - * calculate the pseudo header checksum for use in tcp/udp/icmp headers - * ip6 - the ipv6 header - * len - the transport length (transport header + payload) - * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments - */ -uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { - uint32_t checksum_len, checksum_next; - checksum_len = htonl((uint32_t)len); - checksum_next = htonl(protocol); - - uint32_t current = 0; - - current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); - current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); - current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); - current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next)); - - return current; -} - -/* function: ipv4_pseudo_header_checksum - * calculate the pseudo header checksum for use in tcp/udp headers - * ip - the ipv4 header - * len - the transport length (transport header + payload) - */ -uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { - uint16_t temp_protocol, temp_length; - - temp_protocol = htons(ip->protocol); - temp_length = htons(len); - - uint32_t current = 0; - - current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); - current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); - current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); - current = ip_checksum_add(current, &temp_length, sizeof(uint16_t)); - - return current; -} - -/* function: ip_checksum_adjust - * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums - * checksum - the header checksum in the original packet in network byte order - * old_hdr_sum - the pseudo-header checksum of the original packet - * new_hdr_sum - the pseudo-header checksum of the translated packet - * returns: the new header checksum in network byte order - */ -uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { - // Algorithm suggested in RFC 1624. - // http://tools.ietf.org/html/rfc1624#section-3 - checksum = ~checksum; - uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); - uint16_t folded_old = ip_checksum_fold(old_hdr_sum); - if (folded_sum > folded_old) { - return ~(folded_sum - folded_old); - } else { - return ~(folded_sum - folded_old - 1); // end-around borrow - } -} diff --git a/checksum.h b/checksum.h deleted file mode 100644 index 669b42c505..0000000000 --- a/checksum.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * checksum.h - checksum functions - */ -#ifndef __CHECKSUM_H__ -#define __CHECKSUM_H__ - -#include -#include -#include - -uint32_t ip_checksum_add(uint32_t current, const void *data, int len); -uint16_t ip_checksum_finish(uint32_t temp_sum); -uint16_t ip_checksum(const void *data, int len); - -uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol); -uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len); - -uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum); - -#endif /* __CHECKSUM_H__ */ diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index 83be006d76..91b0996b4d 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -41,7 +41,7 @@ #include #include -#include "checksum.h" +#include "netutils/checksum.h" #include "tun.h" #define DEVICENAME "clat4" diff --git a/clatd_test.cpp b/clatd_test.cpp index 7705a36bc4..8e4982415d 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -29,10 +29,10 @@ #include "tun_interface.h" extern "C" { -#include "checksum.h" #include "clatd.h" #include "config.h" #include "getaddr.h" +#include "netutils/checksum.h" #include "translate.h" #include "tun.h" } diff --git a/config.c b/config.c index 91a65fbe20..5a9d599de9 100644 --- a/config.c +++ b/config.c @@ -25,9 +25,9 @@ #include #include +#include #include -#include "checksum.h" #include "clatd.h" #include "config.h" #include "dns64.h" diff --git a/dump.c b/dump.c index 2c948ffea1..28db13f070 100644 --- a/dump.c +++ b/dump.c @@ -29,7 +29,8 @@ #include #include -#include "checksum.h" +#include "netutils/checksum.h" + #include "clatd.h" #include "debug.h" #include "logging.h" diff --git a/ipv4.c b/ipv4.c index fce400f67e..56d83aca5d 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,7 +17,8 @@ */ #include -#include "checksum.h" +#include "netutils/checksum.h" + #include "debug.h" #include "dump.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index a11dd1ae89..c9b547efcf 100644 --- a/ipv6.c +++ b/ipv6.c @@ -19,7 +19,8 @@ #include -#include "checksum.h" +#include "netutils/checksum.h" + #include "config.h" #include "debug.h" #include "dump.h" diff --git a/translate.c b/translate.c index df3d020de4..16327532cb 100644 --- a/translate.c +++ b/translate.c @@ -17,7 +17,8 @@ */ #include -#include "checksum.h" +#include "netutils/checksum.h" + #include "clatd.h" #include "common.h" #include "config.h" From 6230574aad26ff06b9f19b24a74dc263aae3b6d8 Mon Sep 17 00:00:00 2001 From: junyulai Date: Tue, 12 Feb 2019 13:16:37 +0800 Subject: [PATCH 149/261] Remove the fail-safe owner. Since include statement in OWNERS works as expected. The fail-safe owner could be removed safely. Bug: 120829091 Test: build/make/tools/checkowners.py -c -v path/OWNERS Change-Id: I72ea25d1df96b7de955b69c5e2a9b6ede23adf99 --- OWNERS | 5 ----- 1 file changed, 5 deletions(-) diff --git a/OWNERS b/OWNERS index 6c1758be90..eb9ff18231 100644 --- a/OWNERS +++ b/OWNERS @@ -1,6 +1 @@ include platform/system/netd:/OWNERS - -# TODO: remove this after we make sure first line is working. -# Add one owner as fail-safe. -lorenzo@google.com - From 4e1601e533b4b34d38e4eed229180257a385181b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sat, 30 Mar 2019 02:36:16 -0700 Subject: [PATCH 150/261] clat - turn off spurious log messages caused by IPv6 packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This eliminates log lines of the form: W clatd : read_packet: unknown packet type = 0x86dd Test: stuff is much quieter on device Bug: 129305844 Signed-off-by: Maciej Żenczykowski Change-Id: I8ee7478b4f956265233ba4e2c4237895607d697c --- clatd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clatd.c b/clatd.c index 3ccceea6fb..94a0f06c3a 100644 --- a/clatd.c +++ b/clatd.c @@ -430,6 +430,11 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { } uint16_t proto = ntohs(tun_header->proto); + if (proto == ETH_P_IPV6) { + // kernel IPv6 stack spams us with router/neighbour solication, + // multicast group joins, etc. which otherwise fills the log... + return; + } if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); return; From 60bce378cfa29463f57756fc724d1572747f4fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 9 Apr 2019 01:58:52 -0700 Subject: [PATCH 151/261] Fix some clatd cloexec and file descriptor leaking via missing close() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not terribly important since clatd doesn't exec anything, but was muddying the waters while I was searching for other fd-survives-across-exec leakage in netd. While at it also fix another leaked fd which we forgot to close(). Test: builds and boots Signed-off-by: Maciej Żenczykowski Change-Id: Iceb7d4052dc9be29db5c7bb3fe2ee27da7864379 --- clatd.c | 2 +- config.c | 2 +- mtu.c | 5 ++++- ring.c | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index 94a0f06c3a..06ca7996d2 100644 --- a/clatd.c +++ b/clatd.c @@ -231,7 +231,7 @@ void drop_root_but_keep_caps() { * mark - the socket mark to use for the sending raw socket */ void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); + int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); exit(1); diff --git a/config.c b/config.c index 5a9d599de9..f84a61f047 100644 --- a/config.c +++ b/config.c @@ -227,7 +227,7 @@ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, // Factored out to a separate function for testability. int connect_is_ipv4_address_free(in_addr_t addr) { - int s = socket(AF_INET, SOCK_DGRAM, 0); + int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (s == -1) { return 0; } diff --git a/mtu.c b/mtu.c index 567d177609..472bd4ef2b 100644 --- a/mtu.c +++ b/mtu.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "mtu.h" @@ -33,14 +34,16 @@ int getifmtu(const char *ifname) { int fd; struct ifreq if_mtu; - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { return -1; } strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ); if_mtu.ifr_name[IFNAMSIZ - 1] = '\0'; if (ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { + close(fd); return -1; } + close(fd); return if_mtu.ifr_mtu; } diff --git a/ring.c b/ring.c index 61d40d2e15..e836a55d73 100644 --- a/ring.c +++ b/ring.c @@ -30,7 +30,7 @@ #include "tun.h" int ring_create(struct tun_data *tunnel) { - int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); if (packetsock < 0) { logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); return -1; From 6a095dfa6bd2000f650308cdfa5a69e2635c02ec Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2019 23:22:30 +0900 Subject: [PATCH 152/261] Enable clang-tidy for clatd. Enable the same warnings used elsewhere in the tree, and fix two warnings it found (a safe use of strcpy, and a missing O_CLOEXEC when opening the tun device node. Test: builds, boots, clatd works Test: m clatd clatd_test clatd_microbenchmark && atest clatd_test Change-Id: I9a5ea4de5f31d3c495871250a6493b07535a604b --- Android.bp | 14 ++++++++++++++ clatd.c | 2 +- clatd_microbenchmark.c | 2 +- tun.c | 10 ++++++---- tun.h | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Android.bp b/Android.bp index e0eeb2e3a1..98b4010eeb 100644 --- a/Android.bp +++ b/Android.bp @@ -54,6 +54,20 @@ cc_binary { "liblog", "libnetutils", ], + + // Only enable clang-tidy for the daemon, not the tests, because enabling it for the + // tests substantially increases build/compile cycle times and doesn't really provide a + // security benefit. + tidy: true, + tidy_checks: [ + "-*", + "cert-*", + "clang-analyzer-security*", + "android-*", + ], + tidy_flags: [ + "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", + ], } // The configuration file. diff --git a/clatd.c b/clatd.c index 06ca7996d2..d68dc05210 100644 --- a/clatd.c +++ b/clatd.c @@ -381,7 +381,7 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device4, tunnel->fd4); + error = tun_alloc(tunnel->device4, tunnel->fd4, sizeof(tunnel->device4)); if (error < 0) { logmsg(ANDROID_LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno)); exit(1); diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index 91b0996b4d..15a0376601 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -67,7 +67,7 @@ int setup_tun() { if (fd == -1) die("tun_open"); char dev[IFNAMSIZ] = DEVICENAME; - int ret = tun_alloc(dev, fd); + int ret = tun_alloc(dev, fd, sizeof(dev)); if (ret == -1) die("tun_alloc"); struct ifreq ifr = { .ifr_name = DEVICENAME, diff --git a/tun.c b/tun.c index 406fc2fff9..7ecbf2c090 100644 --- a/tun.c +++ b/tun.c @@ -32,9 +32,9 @@ int tun_open() { int fd; - fd = open("/dev/tun", O_RDWR); + fd = open("/dev/tun", O_RDWR | O_CLOEXEC); if (fd < 0) { - fd = open("/dev/net/tun", O_RDWR); + fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); } return fd; @@ -43,8 +43,10 @@ int tun_open() { /* function: tun_alloc * creates a tun interface and names it * dev - the name for the new tun device + * fd - an open fd to the tun device node + * len - the length of the buffer pointed to by dev */ -int tun_alloc(char *dev, int fd) { +int tun_alloc(char *dev, int fd, size_t len) { struct ifreq ifr; int err; @@ -60,7 +62,7 @@ int tun_alloc(char *dev, int fd) { close(fd); return err; } - strcpy(dev, ifr.ifr_name); + strlcpy(dev, ifr.ifr_name, len); return 0; } diff --git a/tun.h b/tun.h index f0449b92ac..95650fa716 100644 --- a/tun.h +++ b/tun.h @@ -30,7 +30,7 @@ struct tun_data { }; int tun_open(); -int tun_alloc(char *dev, int fd); +int tun_alloc(char *dev, int fd, size_t len); int send_tun(int fd, clat_packet out, int iov_len); int set_nonblocking(int fd); From 35d9175db948861be168f20bf72f928802b85fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 9 Apr 2019 01:58:52 -0700 Subject: [PATCH 153/261] Fix some clatd cloexec and file descriptor leaking via missing close() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not terribly important since clatd doesn't exec anything, but was muddying the waters while I was searching for other fd-survives-across-exec leakage in netd. While at it also fix another leaked fd which we forgot to close(). Test: builds and boots Bug: 131268436 Signed-off-by: Maciej Żenczykowski Change-Id: Iceb7d4052dc9be29db5c7bb3fe2ee27da7864379 Merged-In: Iceb7d4052dc9be29db5c7bb3fe2ee27da7864379 (cherry picked from commit 60bce378cfa29463f57756fc724d1572747f4fc5) --- clatd.c | 2 +- config.c | 2 +- mtu.c | 5 ++++- ring.c | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index 94a0f06c3a..06ca7996d2 100644 --- a/clatd.c +++ b/clatd.c @@ -231,7 +231,7 @@ void drop_root_but_keep_caps() { * mark - the socket mark to use for the sending raw socket */ void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); + int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); if (rawsock < 0) { logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); exit(1); diff --git a/config.c b/config.c index 5a9d599de9..f84a61f047 100644 --- a/config.c +++ b/config.c @@ -227,7 +227,7 @@ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, // Factored out to a separate function for testability. int connect_is_ipv4_address_free(in_addr_t addr) { - int s = socket(AF_INET, SOCK_DGRAM, 0); + int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (s == -1) { return 0; } diff --git a/mtu.c b/mtu.c index 567d177609..472bd4ef2b 100644 --- a/mtu.c +++ b/mtu.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "mtu.h" @@ -33,14 +34,16 @@ int getifmtu(const char *ifname) { int fd; struct ifreq if_mtu; - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { return -1; } strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ); if_mtu.ifr_name[IFNAMSIZ - 1] = '\0'; if (ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { + close(fd); return -1; } + close(fd); return if_mtu.ifr_mtu; } diff --git a/ring.c b/ring.c index 61d40d2e15..e836a55d73 100644 --- a/ring.c +++ b/ring.c @@ -30,7 +30,7 @@ #include "tun.h" int ring_create(struct tun_data *tunnel) { - int packetsock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); + int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); if (packetsock < 0) { logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); return -1; From 3297c7d7bca85db2b178e8838138b71e9d2a86ad Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 Apr 2019 23:22:30 +0900 Subject: [PATCH 154/261] Enable clang-tidy for clatd. Enable the same warnings used elsewhere in the tree, and fix two warnings it found (a safe use of strcpy, and a missing O_CLOEXEC when opening the tun device node. Test: builds, boots, clatd works Test: m clatd clatd_test clatd_microbenchmark && atest clatd_test Bug: 131268436 Change-Id: I9a5ea4de5f31d3c495871250a6493b07535a604b Merged-In: I9a5ea4de5f31d3c495871250a6493b07535a604b (cherry picked from commit 6a095dfa6bd2000f650308cdfa5a69e2635c02ec) --- Android.bp | 14 ++++++++++++++ clatd.c | 2 +- clatd_microbenchmark.c | 2 +- tun.c | 10 ++++++---- tun.h | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Android.bp b/Android.bp index e0eeb2e3a1..98b4010eeb 100644 --- a/Android.bp +++ b/Android.bp @@ -54,6 +54,20 @@ cc_binary { "liblog", "libnetutils", ], + + // Only enable clang-tidy for the daemon, not the tests, because enabling it for the + // tests substantially increases build/compile cycle times and doesn't really provide a + // security benefit. + tidy: true, + tidy_checks: [ + "-*", + "cert-*", + "clang-analyzer-security*", + "android-*", + ], + tidy_flags: [ + "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", + ], } // The configuration file. diff --git a/clatd.c b/clatd.c index 06ca7996d2..d68dc05210 100644 --- a/clatd.c +++ b/clatd.c @@ -381,7 +381,7 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device4, tunnel->fd4); + error = tun_alloc(tunnel->device4, tunnel->fd4, sizeof(tunnel->device4)); if (error < 0) { logmsg(ANDROID_LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno)); exit(1); diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index 91b0996b4d..15a0376601 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -67,7 +67,7 @@ int setup_tun() { if (fd == -1) die("tun_open"); char dev[IFNAMSIZ] = DEVICENAME; - int ret = tun_alloc(dev, fd); + int ret = tun_alloc(dev, fd, sizeof(dev)); if (ret == -1) die("tun_alloc"); struct ifreq ifr = { .ifr_name = DEVICENAME, diff --git a/tun.c b/tun.c index 406fc2fff9..7ecbf2c090 100644 --- a/tun.c +++ b/tun.c @@ -32,9 +32,9 @@ int tun_open() { int fd; - fd = open("/dev/tun", O_RDWR); + fd = open("/dev/tun", O_RDWR | O_CLOEXEC); if (fd < 0) { - fd = open("/dev/net/tun", O_RDWR); + fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); } return fd; @@ -43,8 +43,10 @@ int tun_open() { /* function: tun_alloc * creates a tun interface and names it * dev - the name for the new tun device + * fd - an open fd to the tun device node + * len - the length of the buffer pointed to by dev */ -int tun_alloc(char *dev, int fd) { +int tun_alloc(char *dev, int fd, size_t len) { struct ifreq ifr; int err; @@ -60,7 +62,7 @@ int tun_alloc(char *dev, int fd) { close(fd); return err; } - strcpy(dev, ifr.ifr_name); + strlcpy(dev, ifr.ifr_name, len); return 0; } diff --git a/tun.h b/tun.h index f0449b92ac..95650fa716 100644 --- a/tun.h +++ b/tun.h @@ -30,7 +30,7 @@ struct tun_data { }; int tun_open(); -int tun_alloc(char *dev, int fd); +int tun_alloc(char *dev, int fd, size_t len); int send_tun(int fd, clat_packet out, int iov_len); int set_nonblocking(int fd); From 716518d9b9ca52182498b1d7ed0f0ed8ab42cbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 8 Apr 2019 17:46:48 -0700 Subject: [PATCH 155/261] clatd: change to pass in tun filedescriptor via command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: atest clatd_test, built and installed on aosp_blueline device connected to ipv6-only wifi network: ping 8.8.8.8 still works and it is via v4-wlan0 clat tun interface Bug: 65674744 Signed-off-by: Maciej Żenczykowski Change-Id: I8c9e235e9a5bf1a1436e8dc3af8d0aa86f6dc1a5 --- Android.bp | 1 - clatd.c | 24 ------------------------ clatd.h | 24 +++++++++++++++++++++++- clatd_microbenchmark.c | 2 -- main.c | 24 +++++++++++++++--------- tun.c | 29 +++-------------------------- tun.h | 17 +++++++++++++++-- 7 files changed, 56 insertions(+), 65 deletions(-) diff --git a/Android.bp b/Android.bp index 98b4010eeb..b495dac8cb 100644 --- a/Android.bp +++ b/Android.bp @@ -35,7 +35,6 @@ filegroup { "netlink_msg.c", "ring.c", "setif.c", - "tun.c", "translate.c", ], } diff --git a/clatd.c b/clatd.c index d68dc05210..d332e1eff1 100644 --- a/clatd.c +++ b/clatd.c @@ -355,7 +355,6 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6_addr, struct tun_data *tunnel, unsigned net_id) { - int error; if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { logmsg(ANDROID_LOG_FATAL, "read_config failed"); @@ -381,18 +380,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device4, tunnel->fd4, sizeof(tunnel->device4)); - if (error < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno)); - exit(1); - } - - error = set_nonblocking(tunnel->fd4); - if (error < 0) { - logmsg(ANDROID_LOG_FATAL, "set_nonblocking failed: %s", strerror(errno)); - exit(1); - } - configure_tun_ip(tunnel, v4_addr); if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { @@ -497,14 +484,3 @@ void event_loop(struct tun_data *tunnel) { } } } - -/* function: parse_unsigned - * parses a string as a decimal/hex/octal unsigned integer - * str - the string to parse - * out - the unsigned integer to write to, gets clobbered on failure - */ -int parse_unsigned(const char *str, unsigned *out) { - char *end_ptr; - *out = strtoul(str, &end_ptr, 0); - return *str && !*end_ptr; -} diff --git a/clatd.h b/clatd.h index f7f7315ff0..d3869bfa7b 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include struct tun_data; @@ -45,6 +46,27 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6, struct tun_data *tunnel, unsigned net_id); void event_loop(struct tun_data *tunnel); -int parse_unsigned(const char *str, unsigned *out); + +/* function: parse_int + * parses a string as a decimal/hex/octal signed integer + * str - the string to parse + * out - the signed integer to write to, gets clobbered on failure + */ +static inline int parse_int(const char *str, int *out) { + char *end_ptr; + *out = strtol(str, &end_ptr, 0); + return *str && !*end_ptr; +} + +/* function: parse_unsigned + * parses a string as a decimal/hex/octal unsigned integer + * str - the string to parse + * out - the unsigned integer to write to, gets clobbered on failure + */ +static inline int parse_unsigned(const char *str, unsigned *out) { + char *end_ptr; + *out = strtoul(str, &end_ptr, 0); + return *str && !*end_ptr; +} #endif /* __CLATD_H__ */ diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index 15a0376601..ea58a8a6f1 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -200,12 +200,10 @@ int main() { close(fd); fd = setup_tun(); - set_nonblocking(fd); benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); close(fd); fd = setup_tun(); - set_nonblocking(fd); benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); close(fd); diff --git a/main.c b/main.c index 54d12d17c2..9e796fd8f6 100644 --- a/main.c +++ b/main.c @@ -46,6 +46,7 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-n [NetId]\n"); printf("-m [socket mark]\n"); + printf("-t [tun file descriptor number]\n"); } /* function: main @@ -55,12 +56,12 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:n:m:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:n:m:t:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -80,6 +81,9 @@ int main(int argc, char **argv) { case 'm': mark_str = optarg; break; + case 't': + tunfd_str = optarg; + break; case 'h': print_help(); exit(0); @@ -104,6 +108,15 @@ int main(int argc, char **argv) { exit(1); } + if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) { + logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str); + exit(1); + } + if (!tunnel.fd4) { + logmsg(ANDROID_LOG_FATAL, "no tunfd specified on commandline."); + exit(1); + } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); @@ -124,13 +137,6 @@ int main(int argc, char **argv) { // keeps only admin capability set_capability(1 << CAP_NET_ADMIN); - // we can create tun devices as non-root because we're in the VPN group. - tunnel.fd4 = tun_open(); - if (tunnel.fd4 < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); - exit(1); - } - // When run from netd, the environment variable ANDROID_DNS_MODE is set to // "local", but that only works for the netd process itself. Removing the // following line causes XLAT failure in permissive mode. diff --git a/tun.c b/tun.c index 7ecbf2c090..e2f1342398 100644 --- a/tun.c +++ b/tun.c @@ -21,20 +21,19 @@ #include #include #include -#include #include #include "common.h" /* function: tun_open - * tries to open the tunnel device + * tries to open the tunnel device in non-blocking mode */ int tun_open() { int fd; - fd = open("/dev/tun", O_RDWR | O_CLOEXEC); + fd = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); + fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); } return fd; @@ -65,25 +64,3 @@ int tun_alloc(char *dev, int fd, size_t len) { strlcpy(dev, ifr.ifr_name, len); return 0; } - -/* function: set_nonblocking - * sets a filedescriptor to non-blocking mode - * fd - the filedescriptor - * returns: 0 on success, -1 on failure - */ -int set_nonblocking(int fd) { - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - return flags; - } - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - -/* function: send_tun - * sends a clat_packet to a tun interface - * fd - the tun filedescriptor - * out - the packet to send - * iov_len - the number of entries in the clat_packet - * returns: number of bytes read on success, -1 on failure - */ -int send_tun(int fd, clat_packet out, int iov_len) { return writev(fd, out, iov_len); } diff --git a/tun.h b/tun.h index 95650fa716..05f62c1f60 100644 --- a/tun.h +++ b/tun.h @@ -18,7 +18,10 @@ #ifndef __TUN_H__ #define __TUN_H__ +#include #include +#include +#include #include "common.h" #include "ring.h" @@ -29,9 +32,19 @@ struct tun_data { struct packet_ring ring; }; +// tun_open and tun_alloc are defined in tun.c and only used by clatd_microbenchmark.c int tun_open(); int tun_alloc(char *dev, int fd, size_t len); -int send_tun(int fd, clat_packet out, int iov_len); -int set_nonblocking(int fd); + +/* function: send_tun + * sends a clat_packet to a tun interface + * fd - the tun filedescriptor + * out - the packet to send + * iov_len - the number of entries in the clat_packet + * returns: number of bytes read on success, -1 on failure + */ +static inline int send_tun(int fd, clat_packet out, int iov_len) { + return writev(fd, out, iov_len); +} #endif From 8addcc0f6360cc3762e9ef27437ded250e785da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 8 Apr 2019 17:46:48 -0700 Subject: [PATCH 156/261] clatd: change to pass in tun filedescriptor via command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: atest clatd_test, built and installed on aosp_blueline device connected to ipv6-only wifi network: ping 8.8.8.8 still works and it is via v4-wlan0 clat tun interface Bug: 65674744 Bug: 131268436 Signed-off-by: Maciej Żenczykowski Change-Id: I8c9e235e9a5bf1a1436e8dc3af8d0aa86f6dc1a5 Merged-In: I8c9e235e9a5bf1a1436e8dc3af8d0aa86f6dc1a5 (cherry picked from commit 716518d9b9ca52182498b1d7ed0f0ed8ab42cbe1) --- Android.bp | 1 - clatd.c | 24 ------------------------ clatd.h | 24 +++++++++++++++++++++++- clatd_microbenchmark.c | 2 -- main.c | 24 +++++++++++++++--------- tun.c | 29 +++-------------------------- tun.h | 17 +++++++++++++++-- 7 files changed, 56 insertions(+), 65 deletions(-) diff --git a/Android.bp b/Android.bp index 98b4010eeb..b495dac8cb 100644 --- a/Android.bp +++ b/Android.bp @@ -35,7 +35,6 @@ filegroup { "netlink_msg.c", "ring.c", "setif.c", - "tun.c", "translate.c", ], } diff --git a/clatd.c b/clatd.c index d68dc05210..d332e1eff1 100644 --- a/clatd.c +++ b/clatd.c @@ -355,7 +355,6 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6_addr, struct tun_data *tunnel, unsigned net_id) { - int error; if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { logmsg(ANDROID_LOG_FATAL, "read_config failed"); @@ -381,18 +380,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); } - error = tun_alloc(tunnel->device4, tunnel->fd4, sizeof(tunnel->device4)); - if (error < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_alloc/4 failed: %s", strerror(errno)); - exit(1); - } - - error = set_nonblocking(tunnel->fd4); - if (error < 0) { - logmsg(ANDROID_LOG_FATAL, "set_nonblocking failed: %s", strerror(errno)); - exit(1); - } - configure_tun_ip(tunnel, v4_addr); if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { @@ -497,14 +484,3 @@ void event_loop(struct tun_data *tunnel) { } } } - -/* function: parse_unsigned - * parses a string as a decimal/hex/octal unsigned integer - * str - the string to parse - * out - the unsigned integer to write to, gets clobbered on failure - */ -int parse_unsigned(const char *str, unsigned *out) { - char *end_ptr; - *out = strtoul(str, &end_ptr, 0); - return *str && !*end_ptr; -} diff --git a/clatd.h b/clatd.h index f7f7315ff0..d3869bfa7b 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include struct tun_data; @@ -45,6 +46,27 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6, struct tun_data *tunnel, unsigned net_id); void event_loop(struct tun_data *tunnel); -int parse_unsigned(const char *str, unsigned *out); + +/* function: parse_int + * parses a string as a decimal/hex/octal signed integer + * str - the string to parse + * out - the signed integer to write to, gets clobbered on failure + */ +static inline int parse_int(const char *str, int *out) { + char *end_ptr; + *out = strtol(str, &end_ptr, 0); + return *str && !*end_ptr; +} + +/* function: parse_unsigned + * parses a string as a decimal/hex/octal unsigned integer + * str - the string to parse + * out - the unsigned integer to write to, gets clobbered on failure + */ +static inline int parse_unsigned(const char *str, unsigned *out) { + char *end_ptr; + *out = strtoul(str, &end_ptr, 0); + return *str && !*end_ptr; +} #endif /* __CLATD_H__ */ diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c index 15a0376601..ea58a8a6f1 100644 --- a/clatd_microbenchmark.c +++ b/clatd_microbenchmark.c @@ -200,12 +200,10 @@ int main() { close(fd); fd = setup_tun(); - set_nonblocking(fd); benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); close(fd); fd = setup_tun(); - set_nonblocking(fd); benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); close(fd); diff --git a/main.c b/main.c index 54d12d17c2..9e796fd8f6 100644 --- a/main.c +++ b/main.c @@ -46,6 +46,7 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-n [NetId]\n"); printf("-m [socket mark]\n"); + printf("-t [tun file descriptor number]\n"); } /* function: main @@ -55,12 +56,12 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:n:m:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:n:m:t:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -80,6 +81,9 @@ int main(int argc, char **argv) { case 'm': mark_str = optarg; break; + case 't': + tunfd_str = optarg; + break; case 'h': print_help(); exit(0); @@ -104,6 +108,15 @@ int main(int argc, char **argv) { exit(1); } + if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) { + logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str); + exit(1); + } + if (!tunnel.fd4) { + logmsg(ANDROID_LOG_FATAL, "no tunfd specified on commandline."); + exit(1); + } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); @@ -124,13 +137,6 @@ int main(int argc, char **argv) { // keeps only admin capability set_capability(1 << CAP_NET_ADMIN); - // we can create tun devices as non-root because we're in the VPN group. - tunnel.fd4 = tun_open(); - if (tunnel.fd4 < 0) { - logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno)); - exit(1); - } - // When run from netd, the environment variable ANDROID_DNS_MODE is set to // "local", but that only works for the netd process itself. Removing the // following line causes XLAT failure in permissive mode. diff --git a/tun.c b/tun.c index 7ecbf2c090..e2f1342398 100644 --- a/tun.c +++ b/tun.c @@ -21,20 +21,19 @@ #include #include #include -#include #include #include "common.h" /* function: tun_open - * tries to open the tunnel device + * tries to open the tunnel device in non-blocking mode */ int tun_open() { int fd; - fd = open("/dev/tun", O_RDWR | O_CLOEXEC); + fd = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); + fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); } return fd; @@ -65,25 +64,3 @@ int tun_alloc(char *dev, int fd, size_t len) { strlcpy(dev, ifr.ifr_name, len); return 0; } - -/* function: set_nonblocking - * sets a filedescriptor to non-blocking mode - * fd - the filedescriptor - * returns: 0 on success, -1 on failure - */ -int set_nonblocking(int fd) { - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - return flags; - } - return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - -/* function: send_tun - * sends a clat_packet to a tun interface - * fd - the tun filedescriptor - * out - the packet to send - * iov_len - the number of entries in the clat_packet - * returns: number of bytes read on success, -1 on failure - */ -int send_tun(int fd, clat_packet out, int iov_len) { return writev(fd, out, iov_len); } diff --git a/tun.h b/tun.h index 95650fa716..05f62c1f60 100644 --- a/tun.h +++ b/tun.h @@ -18,7 +18,10 @@ #ifndef __TUN_H__ #define __TUN_H__ +#include #include +#include +#include #include "common.h" #include "ring.h" @@ -29,9 +32,19 @@ struct tun_data { struct packet_ring ring; }; +// tun_open and tun_alloc are defined in tun.c and only used by clatd_microbenchmark.c int tun_open(); int tun_alloc(char *dev, int fd, size_t len); -int send_tun(int fd, clat_packet out, int iov_len); -int set_nonblocking(int fd); + +/* function: send_tun + * sends a clat_packet to a tun interface + * fd - the tun filedescriptor + * out - the packet to send + * iov_len - the number of entries in the clat_packet + * returns: number of bytes read on success, -1 on failure + */ +static inline int send_tun(int fd, clat_packet out, int iov_len) { + return writev(fd, out, iov_len); +} #endif From ba25674f804bb4e1bcf9f130b076373d8cd97dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sat, 11 May 2019 16:35:19 -0700 Subject: [PATCH 157/261] Revert "clat - turn off spurious log messages caused by IPv6 packets" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4e1601e533b4b34d38e4eed229180257a385181b. This would reintroduce log lines of the form: W clatd : read_packet: unknown packet type = 0x86dd if not for the fact that with the movement of the creation of the tun device to netd, it now has sufficient privileges to simply turn off IPv6 on the device, and thus we no longer require this temporary hack. Test: we *continue* to not get clatd complaints about unknown packets Bug: 129305844 Signed-off-by: Maciej Żenczykowski Change-Id: Iafafe27495d1e674afd0185bea6efc305a9e65ce --- clatd.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clatd.c b/clatd.c index d332e1eff1..019ce7cefc 100644 --- a/clatd.c +++ b/clatd.c @@ -417,11 +417,6 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { } uint16_t proto = ntohs(tun_header->proto); - if (proto == ETH_P_IPV6) { - // kernel IPv6 stack spams us with router/neighbour solication, - // multicast group joins, etc. which otherwise fills the log... - return; - } if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); return; From 06c233c03ab9b4380b0e266e582fae7efd665cc0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 4 Jun 2019 10:21:00 +0900 Subject: [PATCH 158/261] Attempt to run netd tests when clat is modified. Bug: 134441670 Test: treehugger Change-Id: I119a75b10fe241cd09885ac32db4a32e3232d134 --- TEST_MAPPING | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index c6d599d63d..873ff2feb0 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,7 +1,11 @@ { "presubmit": [ - { - "name": "clatd_test" - } + { "name": "clatd_test" }, + { "name": "libnetdbpf_test" }, + { "name": "netd_integration_test" }, + { "name": "netd_unit_test" }, + { "name": "netdutils_test" }, + { "name": "resolv_integration_test" }, + { "name": "resolv_unit_test" } ] } From 5f68982d02fcbd582e10b96485034f76140f3785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 20 Aug 2019 17:48:48 -0700 Subject: [PATCH 159/261] clat: add support for ESP, mirroring existing GRE support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This relies on some pretty heavy duty magic on the side of the PLAT nat gateway in order to be useful. It might not even work... but it already doesn't work, so it can't be worse then the current state where we simply discard these packets. Test: builds Bug: 139499742 Signed-off-by: Maciej Żenczykowski Change-Id: I3adfae6251c66125ff31c23c7967feacafb805a0 --- BUGS | 2 +- ipv4.c | 2 +- ipv6.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BUGS b/BUGS index 70aeb9f0f4..24e6639d11 100644 --- a/BUGS +++ b/BUGS @@ -1,5 +1,5 @@ known problems/assumptions: - - does not handle protocols other than ICMP, UDP, TCP and GRE + - does not handle protocols other than ICMP, UDP, TCP and GRE/ESP - assumes the handset has its own (routed) /64 ipv6 subnet - assumes the /128 ipv6 subnet it generates can use the nat64 gateway - assumes the nat64 gateway has the ipv4 address in the last 32 bits of the ipv6 address (that it uses a /96 plat subnet) diff --git a/ipv4.c b/ipv4.c index 56d83aca5d..c052d03e89 100644 --- a/ipv4.c +++ b/ipv4.c @@ -131,7 +131,7 @@ int ipv4_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s } else if (nxthdr == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); - } else if (nxthdr == IPPROTO_GRE) { + } else if (nxthdr == IPPROTO_GRE || nxthdr == IPPROTO_ESP) { iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG diff --git a/ipv6.c b/ipv6.c index c9b547efcf..ca3e4c68a5 100644 --- a/ipv6.c +++ b/ipv6.c @@ -163,7 +163,7 @@ int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, s } else if (protocol == IPPROTO_UDP) { iov_len = udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); - } else if (protocol == IPPROTO_GRE) { + } else if (protocol == IPPROTO_GRE || protocol == IPPROTO_ESP) { iov_len = generic_packet(out, pos + 2, next_header, len_left); } else { #if CLAT_DEBUG From 76f4b74309f4915621bf34e262cfbc0fffd8a98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 19 Sep 2019 14:38:34 -0700 Subject: [PATCH 160/261] clatd_test - add 'require_root: true' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 141272654 Bug: 141307514 Test: now passes Signed-off-by: Maciej Żenczykowski Change-Id: I5598b3f5acdbd098043c6f9513995f288ae4c142 --- Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.bp b/Android.bp index b495dac8cb..1d21c2409f 100644 --- a/Android.bp +++ b/Android.bp @@ -94,6 +94,7 @@ cc_test { "libnetutils", ], test_suites: ["device-tests"], + require_root: true, } // Microbenchmark. From a02523b26245ce3f39cf584ec5f8562f053b6d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 31 Oct 2019 23:53:37 -0700 Subject: [PATCH 161/261] fix sin_port = 53 when it should be network order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (note that it doesn't actually matter because we only use this for a source ip lookup) Test: builds Signed-off-by: Maciej Żenczykowski Change-Id: Id508345a702bfff63d42cadc68355b94988edb5a --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index f84a61f047..c370770c49 100644 --- a/config.c +++ b/config.c @@ -234,7 +234,7 @@ int connect_is_ipv4_address_free(in_addr_t addr) { // Attempt to connect to the address. If the connection succeeds and getsockname returns the same // the address then the address is already assigned to the system and we can't use it. - struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 }; + struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = htons(53) }; socklen_t len = sizeof(sin); int inuse = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 && getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) && From f037e7729688b574e713ef06108781adb349a35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sun, 10 Nov 2019 23:02:13 -0800 Subject: [PATCH 162/261] make clat checksum tests more reliable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow increasing pseudochecksum returned by 0xFFFF, which is another form of 16-bit 1's complement 0 and thus irrelevant. Test: builds, atest Signed-off-by: Maciej Żenczykowski Change-Id: I8cfa04ed1c0bdde2a605cff258a12690748f3bd5 --- clatd_test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 8e4982415d..970989000b 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -903,8 +903,10 @@ TEST_F(ClatdTest, TransportChecksum) { uint32_t ipv6_pseudo_sum = ipv6_pseudo_header_checksum((struct ip6_hdr *)ip6, UDP_LEN, IPPROTO_UDP); - EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum) << "IPv4 pseudo-checksum sanity check\n"; - EXPECT_EQ(0x2644bU, ipv6_pseudo_sum) << "IPv6 pseudo-checksum sanity check\n"; + EXPECT_NE(0, ipv4_pseudo_sum); + EXPECT_NE(0, ipv6_pseudo_sum); + EXPECT_EQ(0x3ad0U, ipv4_pseudo_sum % 0xFFFF) << "IPv4 pseudo-checksum sanity check\n"; + EXPECT_EQ(0x644dU, ipv6_pseudo_sum % 0xFFFF) << "IPv6 pseudo-checksum sanity check\n"; EXPECT_EQ( kUdpV4Checksum, ip_checksum_finish(ipv4_pseudo_sum + kUdpPartialChecksum + kPayloadPartialChecksum)) From 75f4dc6ec766fdcbf38d7ebe67da7b81a1f0f0a0 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Tue, 19 Nov 2019 09:37:42 -0800 Subject: [PATCH 163/261] external: android-clat: fix -Wreorder-init-list C++20 will require members in a designated initializer to be in order unlike C99. Bug: 139945549 Test: mm Change-Id: I5d94739894a5882f571c1e0ddf069725f0c246e8 Signed-off-by: Nick Desaulniers --- clatd_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 970989000b..f01f49d47a 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -563,8 +563,8 @@ int get_transport_checksum(const uint8_t *packet) { static tun_data makeTunData() { // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. return { - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), + .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), .fd4 = socket(AF_UNIX, SOCK_DGRAM, 0), }; } @@ -1092,8 +1092,8 @@ TEST_F(ClatdTest, ConfigureIpv6AddressCommandLine) { TEST_F(ClatdTest, Ipv6AddressChanged) { // Configure the clat IPv6 address. struct tun_data tunnel = { - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), + .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), }; const char *ifname = sTun.name().c_str(); ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr)); From badfbedcadeee85f8b7792abfd58de1ca89ec43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 26 Nov 2019 22:44:08 -0800 Subject: [PATCH 164/261] remove clatd_microbenchmark.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the era of ebpf offload the speed of writing packets via tun and reading via udp socket aren't all that important. Test: builds Bug: 144730808 Signed-off-by: Maciej Żenczykowski Change-Id: Ie2b49c8b109052b9212d3b21fd16784a464e2a1b --- Android.bp | 13 --- clatd_microbenchmark.c | 211 ----------------------------------------- tun.c | 66 ------------- tun.h | 4 - 4 files changed, 294 deletions(-) delete mode 100644 clatd_microbenchmark.c delete mode 100644 tun.c diff --git a/Android.bp b/Android.bp index 1d21c2409f..42fbdaa2d9 100644 --- a/Android.bp +++ b/Android.bp @@ -96,16 +96,3 @@ cc_test { test_suites: ["device-tests"], require_root: true, } - -// Microbenchmark. -cc_test { - name: "clatd_microbenchmark", - defaults: ["clatd_defaults"], - srcs: [ - "clatd_microbenchmark.c", - "tun.c", - ], - shared_libs: [ - "libnetutils", - ], -} diff --git a/clatd_microbenchmark.c b/clatd_microbenchmark.c deleted file mode 100644 index ea58a8a6f1..0000000000 --- a/clatd_microbenchmark.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * clatd_microbenchmark.c - micro-benchmark for clatd tun send path - * - * Run with: - * - * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark - * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netutils/checksum.h" -#include "tun.h" - -#define DEVICENAME "clat4" - -#define PORT 51339 -#define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr)) -#define NUMPACKETS 1000000 -#define SEC_TO_NANOSEC (1000 * 1000 * 1000) - -void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) { - sin->sin_family = AF_INET; - sin->sin_port = 0; - sin->sin_addr.s_addr = inet_addr(addr); -} - -void die(const char *str) { - perror(str); - exit(1); -} - -int setup_tun() { - int fd = tun_open(); - if (fd == -1) die("tun_open"); - - char dev[IFNAMSIZ] = DEVICENAME; - int ret = tun_alloc(dev, fd, sizeof(dev)); - if (ret == -1) die("tun_alloc"); - struct ifreq ifr = { - .ifr_name = DEVICENAME, - }; - - int s = socket(AF_INET, SOCK_DGRAM, 0); - init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "192.0.0.4"); - if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); - init_sockaddr_in((struct sockaddr_in *)&ifr.ifr_addr, "255.255.255.248"); - if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); - if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); - ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); - if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); - return fd; -} - -int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) { - struct tun_pi tun = { 0, htons(ETH_P_IP) }; - struct udphdr udp = { - .source = htons(1234), - .dest = htons(PORT), - .len = htons(len + sizeof(udp)), - .check = 0, - }; - struct iphdr ip = { - .version = 4, - .ihl = 5, - .tot_len = htons(len + sizeof(ip) + sizeof(udp)), - .frag_off = htons(IP_DF), - .ttl = 55, - .protocol = IPPROTO_UDP, - .saddr = htonl(0xc0000006), // 192.0.0.6 - .daddr = htonl(0xc0000004), // 192.0.0.4 - }; - clat_packet out = { - { &tun, sizeof(tun) }, // tun header - { &ip, sizeof(ip) }, // IP header - { NULL, 0 }, // Fragment header - { &udp, sizeof(udp) }, // Transport header - { NULL, 0 }, // ICMP error IP header - { NULL, 0 }, // ICMP error fragment header - { NULL, 0 }, // ICMP error transport header - { payload, len }, // Payload - }; - - ip.check = ip_checksum(&ip, sizeof(ip)); - - uint32_t sum; - sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); - sum = ip_checksum_add(sum, &udp, sizeof(udp)); - sum += payload_checksum; - udp.check = ip_checksum_finish(sum); - - return send_tun(fd, out, sizeof(out) / sizeof(out[0])); -} - -double timedelta(const struct timespec tv1, const struct timespec tv2) { - struct timespec end = tv2; - if (end.tv_nsec < tv1.tv_nsec) { - end.tv_sec -= 1; - end.tv_nsec += SEC_TO_NANOSEC; - } - double seconds = (end.tv_sec - tv1.tv_sec); - seconds += (((double)(end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); - return seconds; -} - -void benchmark(const char *name, int fd, int s, int num, int do_read, uint8_t payload[], int len, - uint32_t payload_sum) { - int i; - char buf[4096]; - struct timespec tv1, tv2; - int write_err = 0, read_err = 0; - clock_gettime(CLOCK_MONOTONIC, &tv1); - for (i = 0; i < num; i++) { - if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; - if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { - read_err++; - if (errno == ETIMEDOUT) { - printf("Timed out after %d packets!\n", i); - break; - } - } - } - clock_gettime(CLOCK_MONOTONIC, &tv2); - double seconds = timedelta(tv1, tv2); - int pps = (int)(i / seconds); - double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); - printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); - printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", read_err, (float)read_err / i * 100, - write_err, (float)write_err / i * 100); -} - -int open_socket() { - int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); - - int on = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); - - struct timeval tv = { 1, 0 }; - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); - - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_port = ntohs(PORT), - .sin_addr = { INADDR_ANY }, - }; - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) die("bind"); - - return sock; -} - -int main() { - int fd = setup_tun(); - int sock = open_socket(); - - int i; - uint8_t payload[PAYLOADSIZE]; - for (i = 0; i < (int)sizeof(payload); i++) { - payload[i] = (uint8_t)i; - } - uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); - - // Check things are working. - char buf[4096]; - if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); - if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); - - benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); - close(fd); - - fd = setup_tun(); - benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); - close(fd); - - fd = setup_tun(); - benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); - close(fd); - - return 0; -} diff --git a/tun.c b/tun.c deleted file mode 100644 index e2f1342398..0000000000 --- a/tun.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * tun.c - tun device functions - */ -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -/* function: tun_open - * tries to open the tunnel device in non-blocking mode - */ -int tun_open() { - int fd; - - fd = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); - if (fd < 0) { - fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC); - } - - return fd; -} - -/* function: tun_alloc - * creates a tun interface and names it - * dev - the name for the new tun device - * fd - an open fd to the tun device node - * len - the length of the buffer pointed to by dev - */ -int tun_alloc(char *dev, int fd, size_t len) { - 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; - } - strlcpy(dev, ifr.ifr_name, len); - return 0; -} diff --git a/tun.h b/tun.h index 05f62c1f60..eaeebc689c 100644 --- a/tun.h +++ b/tun.h @@ -32,10 +32,6 @@ struct tun_data { struct packet_ring ring; }; -// tun_open and tun_alloc are defined in tun.c and only used by clatd_microbenchmark.c -int tun_open(); -int tun_alloc(char *dev, int fd, size_t len); - /* function: send_tun * sends a clat_packet to a tun interface * fd - the tun filedescriptor From 69c840fe5363ead6c0d6e012965b7c1538055584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 18 Nov 2019 12:00:49 -0800 Subject: [PATCH 165/261] clatd - move bind() post bpf filter attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a minor race where the ingress ipv6 raw socket can receive/deliver packets prior to the bpf filter being setup to filter them out. Test: builds, booted on crosshatch, clat continues to work Bug: 144635476 Signed-off-by: Maciej Żenczykowski Change-Id: I47a4f908d1d365937885d36902fa6a92c53d1ebf --- clatd.c | 22 +++++++++++----------- ring.c | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/clatd.c b/clatd.c index 019ce7cefc..8a0d55f981 100644 --- a/clatd.c +++ b/clatd.c @@ -68,17 +68,6 @@ void stop_loop() { running = 0; } * sock - the socket to configure */ int configure_packet_socket(int sock) { - struct sockaddr_ll sll = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), - .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. - }; - if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { - logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); - return 0; - } - uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; // clang-format off @@ -107,6 +96,17 @@ int configure_packet_socket(int sock) { return 0; } + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IPV6), + .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), + .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. + }; + if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { + logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); + return 0; + } + return 1; } diff --git a/ring.c b/ring.c index e836a55d73..96a50ca677 100644 --- a/ring.c +++ b/ring.c @@ -30,7 +30,9 @@ #include "tun.h" int ring_create(struct tun_data *tunnel) { - int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); + // Will eventually be bound to htons(ETH_P_IPV6) protocol, + // but only after appropriate bpf filter is attached. + int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (packetsock < 0) { logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); return -1; From 7c3b5dcb95deb41fda40de7eb682629fcbdd3b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 18 Nov 2019 17:20:01 -0800 Subject: [PATCH 166/261] remove configuration option 'ipv4mtu' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specifying the MTU in a per-device configuration file is in general incorrect because different networks have different MTUs. Carriers or operators that want to lower the MTU can lower the IPv6 MTU (from which the IPv4 MTU is automatically calculated), or set the MTU manually from Nat464Xlat or ConnectivityService. Doing it this way has the benefit of supporting different MTUs for different networks. Test: builds Bug: 144730808 Signed-off-by: Maciej Żenczykowski Change-Id: Ia6005421e81fc0007067d9774e28f9aae64d1160 --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index c370770c49..e820ba0d3b 100644 --- a/config.c +++ b/config.c @@ -330,7 +330,7 @@ int read_config(const char *file, const char *uplink_interface, const char *plat 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; + Global_Clatd_Config.ipv4mtu = -1; if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) From 3cedd1d5dd99119068aeed218769bf73369b9f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 18 Nov 2019 17:21:05 -0800 Subject: [PATCH 167/261] remove configuration option 'mtu' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't useful because anyone wanting to set the IPv6 MTU can simply set that by setting the IPv6 MTU on the interface. Test: builds Bug: 144730808 Signed-off-by: Maciej Żenczykowski Change-Id: Id140dc9865d33b66b120b932cb16e4d4fa70fc47 --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index e820ba0d3b..a8590920bd 100644 --- a/config.c +++ b/config.c @@ -328,7 +328,7 @@ int read_config(const char *file, const char *uplink_interface, const char *plat Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); if (!Global_Clatd_Config.default_pdp_interface) goto failed; - if (!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu)) goto failed; + Global_Clatd_Config.mtu = -1; Global_Clatd_Config.ipv4mtu = -1; From b6b5104bab33df9ce505234da1e08cb1b58010db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 18 Nov 2019 16:41:44 -0800 Subject: [PATCH 168/261] remove configuration option 'plat_from_dns64_hostname' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 7050 has been a standard for so long, and is likely used by so many other implementations, that it doesn't seem to make sense to use anything else then 'ipv4only.arpa' (also that's what netd uses, and this code here is really dead, since in Q we moved the detection out of clatd and into netd). Test: builds, 'git grep free_config' comes up empty 'git grep plat_from_dns64_hostname' comes up empty Bug: 144730808 Signed-off-by: Maciej Żenczykowski Change-Id: Ibf0c8d5e2cec6359fba1f7dd8867bd2742f362b4 --- clatd.conf | 2 -- config.c | 16 +--------------- config.h | 3 +-- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/clatd.conf b/clatd.conf index ff80975ea6..aef9c757fd 100644 --- a/clatd.conf +++ b/clatd.conf @@ -14,8 +14,6 @@ ipv4_local_prefixlen 29 # get the plat_subnet from dns lookups (requires DNS64) plat_from_dns64 yes -# hostname to use to lookup plat subnet. must contain only A records -plat_from_dns64_hostname ipv4only.arpa # plat subnet to send ipv4 traffic to. This is a /96 subnet. # This setting only makes sense with: plat_from_dns64 no diff --git a/config.c b/config.c index a8590920bd..3ea510d096 100644 --- a/config.c +++ b/config.c @@ -150,16 +150,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char return ret_val_ptr; } -/* function: free_config - * frees the memory used by the global config variable - */ -void free_config() { - if (Global_Clatd_Config.plat_from_dns64_hostname) { - free(Global_Clatd_Config.plat_from_dns64_hostname); - Global_Clatd_Config.plat_from_dns64_hostname = NULL; - } -} - /* function: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address @@ -180,7 +170,7 @@ void dns64_detection(unsigned net_id) { backoff_sleep = 1; while (1) { - status = plat_prefix(Global_Clatd_Config.plat_from_dns64_hostname, net_id, &tmp_ptr); + status = plat_prefix(DNS64_DETECTION_HOSTNAME, net_id, &tmp_ptr); if (status > 0) { memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); return; @@ -357,9 +347,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat } else { free(tmp_ptr); - if (!(Global_Clatd_Config.plat_from_dns64_hostname = - config_item_str(root, "plat_from_dns64_hostname", DEFAULT_DNS64_DETECTION_HOSTNAME))) - goto failed; dns64_detection(net_id); } } @@ -381,7 +368,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat failed: free(root); - free_config(); return 0; } diff --git a/config.h b/config.h index e521b74250..88c632aa9f 100644 --- a/config.h +++ b/config.h @@ -23,7 +23,7 @@ #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" -#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4only.arpa" +#define DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { int16_t mtu, ipv4mtu; @@ -33,7 +33,6 @@ struct clat_config { int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; char *default_pdp_interface; - char *plat_from_dns64_hostname; int use_dynamic_iid; }; From b00113f49a8128a47ae5d908c024cf122b725585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Sat, 18 Jan 2020 23:56:14 -0800 Subject: [PATCH 169/261] Derive mtu from ipv6 route to ipv4 /96 prefix instead of device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: builds, atest, still needs testing on real network x86 clatd_test -------------- clatd_test (19 Tests) ... [6/19] ClatdTest#DetectMtu: PASSED (0ms) ... Bug: 147935930 Signed-off-by: Maciej Żenczykowski Change-Id: I3d11ba082dabf70089867146acd84f6436953663 --- Android.bp | 1 - clatd.c | 81 +++++++++++++++++++++++++++++++++++--------------- clatd.h | 5 ++-- clatd_test.cpp | 11 ++++--- config.c | 6 ---- config.h | 1 - main.c | 2 +- mtu.c | 49 ------------------------------ mtu.h | 24 --------------- 9 files changed, 68 insertions(+), 112 deletions(-) delete mode 100644 mtu.c delete mode 100644 mtu.h diff --git a/Android.bp b/Android.bp index 42fbdaa2d9..ae4bf1da03 100644 --- a/Android.bp +++ b/Android.bp @@ -30,7 +30,6 @@ filegroup { "ipv4.c", "ipv6.c", "logging.c", - "mtu.c", "netlink_callbacks.c", "netlink_msg.c", "ring.c", diff --git a/clatd.c b/clatd.c index 8a0d55f981..82df63db30 100644 --- a/clatd.c +++ b/clatd.c @@ -46,7 +46,6 @@ #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "mtu.h" #include "resolv_netid.h" #include "ring.h" #include "setif.h" @@ -149,8 +148,9 @@ static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) { /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface * tunnel - tun device data + * mtu - mtu of tun device */ -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr) { +void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { if (v4_addr) { Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr); } else { @@ -170,7 +170,7 @@ void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr) { exit(1); } - status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu); + status = if_up(tunnel->device4, mtu); if (status < 0) { logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status)); exit(1); @@ -346,41 +346,74 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter return 1; } +int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark) { + // Create an IPv6 UDP socket. + int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (s < 0) { + logmsg(ANDROID_LOG_FATAL, "socket(AF_INET6, SOCK_DGRAM, 0) failed"); + exit(1); + } + + // Socket's mark affects routing decisions (network selection) + if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) { + logmsg(ANDROID_LOG_FATAL, "setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno)); + exit(1); + } + + // Try to connect udp socket to plat_subnet(96 bits):plat_suffix(32 bits) + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_addr = *plat_subnet, + }; + dst.sin6_addr.s6_addr32[3] = plat_suffix; + if (connect(s, (struct sockaddr *)&dst, sizeof(dst))) { + logmsg(ANDROID_LOG_FATAL, "connect() failed: %s", strerror(errno)); + exit(1); + } + + // Fetch the socket's IPv6 mtu - this is effectively fetching mtu from routing table + int mtu; + socklen_t sz_mtu = sizeof(mtu); + if (getsockopt(s, SOL_IPV6, IPV6_MTU, &mtu, &sz_mtu)) { + logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) failed: %s", strerror(errno)); + exit(1); + } + if (sz_mtu != sizeof(mtu)) { + logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) returned unexpected size: %d", + sz_mtu); + exit(1); + } + close(s); + + return mtu; +} + /* function: configure_interface * reads the configuration and applies it to the interface * uplink_interface - network interface to use to reach the ipv6 internet * plat_prefix - PLAT prefix to use * tunnel - tun device data * net_id - NetID to use, NETID_UNSET indicates use of default network + * mark - the socket mark to use for the sending raw socket */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6_addr, struct tun_data *tunnel, unsigned net_id) { - + const char *v6_addr, struct tun_data *tunnel, unsigned net_id, + uint32_t mark) { if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { logmsg(ANDROID_LOG_FATAL, "read_config failed"); exit(1); } - if (Global_Clatd_Config.mtu > MAXMTU) { - logmsg(ANDROID_LOG_WARN, "Max MTU is %d, requested %d", MAXMTU, Global_Clatd_Config.mtu); - Global_Clatd_Config.mtu = MAXMTU; - } - if (Global_Clatd_Config.mtu <= 0) { - Global_Clatd_Config.mtu = getifmtu(Global_Clatd_Config.default_pdp_interface); - logmsg(ANDROID_LOG_WARN, "ifmtu=%d", Global_Clatd_Config.mtu); - } - if (Global_Clatd_Config.mtu < 1280) { - logmsg(ANDROID_LOG_WARN, "mtu too small = %d", Global_Clatd_Config.mtu); - Global_Clatd_Config.mtu = 1280; - } + int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark); + // clamp to minimum ipv6 mtu - this probably cannot ever trigger + if (mtu < 1280) mtu = 1280; + // clamp to buffer size + if (mtu > MAXMTU) mtu = MAXMTU; + // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes + mtu -= MTU_DELTA; + logmsg(ANDROID_LOG_WARN, "ipv4 mtu is %d", mtu); - if (Global_Clatd_Config.ipv4mtu <= 0 || - Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) { - Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA; - logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu); - } - - configure_tun_ip(tunnel, v4_addr); + configure_tun_ip(tunnel, v4_addr, mtu); if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { exit(1); diff --git a/clatd.h b/clatd.h index d3869bfa7b..e1d28a7194 100644 --- a/clatd.h +++ b/clatd.h @@ -36,15 +36,16 @@ struct tun_data; #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 void stop_loop(); -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr); +void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_but_keep_caps(); void open_sockets(struct tun_data *tunnel, uint32_t mark); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); +int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark); void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6, struct tun_data *tunnel, unsigned net_id); + const char *v6, struct tun_data *tunnel, unsigned net_id, uint32_t mark); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/clatd_test.cpp b/clatd_test.cpp index f01f49d47a..08b444c77d 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -772,12 +772,16 @@ TEST_F(ClatdTest, SelectIPv4Address) { EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); } +TEST_F(ClatdTest, DetectMtu) { + // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB + ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); +} + TEST_F(ClatdTest, ConfigureTunIp) { addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; config_is_ipv4_address_free = over6_free; Global_Clatd_Config.ipv4_local_prefixlen = 29; - Global_Clatd_Config.ipv4mtu = 1472; // Create an interface for configure_tun_ip to configure and bring up. TunInterface v4Iface; @@ -785,7 +789,7 @@ TEST_F(ClatdTest, ConfigureTunIp) { struct tun_data tunnel = makeTunData(); strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - configure_tun_ip(&tunnel, nullptr /* v4_addr */); + configure_tun_ip(&tunnel, nullptr /* v4_addr */, 1472); EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr); union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); @@ -801,7 +805,6 @@ TEST_F(ClatdTest, ConfigureTunIpManual) { config_is_ipv4_address_free = over6_free; Global_Clatd_Config.ipv4_local_prefixlen = 29; - Global_Clatd_Config.ipv4mtu = 1472; // Create an interface for configure_tun_ip to configure and bring up. TunInterface v4Iface; @@ -809,7 +812,7 @@ TEST_F(ClatdTest, ConfigureTunIpManual) { struct tun_data tunnel = makeTunData(); strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */); + configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */, 1472); EXPECT_EQ(inet_addr("192.0.2.1"), Global_Clatd_Config.ipv4_local_subnet.s_addr); union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); diff --git a/config.c b/config.c index 3ea510d096..1c1a1f7955 100644 --- a/config.c +++ b/config.c @@ -318,10 +318,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); if (!Global_Clatd_Config.default_pdp_interface) goto failed; - Global_Clatd_Config.mtu = -1; - - Global_Clatd_Config.ipv4mtu = -1; - if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet)) goto failed; @@ -377,8 +373,6 @@ failed: 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))); diff --git a/config.h b/config.h index 88c632aa9f..d5c43990ec 100644 --- a/config.h +++ b/config.h @@ -26,7 +26,6 @@ #define DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { - int16_t mtu, ipv4mtu; struct in6_addr ipv6_local_subnet; struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; diff --git a/main.c b/main.c index 9e796fd8f6..e973bf3d07 100644 --- a/main.c +++ b/main.c @@ -142,7 +142,7 @@ int main(int argc, char **argv) { // following line causes XLAT failure in permissive mode. unsetenv("ANDROID_DNS_MODE"); - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id); + configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id, mark); // Drop all remaining capabilities. set_capability(0); diff --git a/mtu.c b/mtu.c deleted file mode 100644 index 472bd4ef2b..0000000000 --- a/mtu.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * mtu.c - get interface mtu - */ - -#include -#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 | SOCK_CLOEXEC, 0); - if (fd < 0) { - return -1; - } - strncpy(if_mtu.ifr_name, ifname, IFNAMSIZ); - if_mtu.ifr_name[IFNAMSIZ - 1] = '\0'; - if (ioctl(fd, SIOCGIFMTU, &if_mtu) < 0) { - close(fd); - return -1; - } - close(fd); - return if_mtu.ifr_mtu; -} diff --git a/mtu.h b/mtu.h deleted file mode 100644 index c330c24906..0000000000 --- a/mtu.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * mtu.c - get interface mtu - */ - -#ifndef __MTU_H__ -#define __MTU_H__ - -int getifmtu(const char *ifname); - -#endif From 5e5d29d88b2eab8bb4206e9ff61a02fb555a2462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 26 Nov 2019 23:10:57 -0800 Subject: [PATCH 170/261] remove ClatdTest.ConfigureIpv6Address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test isn't useful, because we always select the ipv6 address within netd and pass it in via cli. (and then we rename ClatdTest.ConfigureIpv6AddressCommandLine to ClatdTest.ConfigureIpv6Address) Test: builds Bug: 144730808 Signed-off-by: Maciej Żenczykowski Change-Id: I5dbf1cd2a253a48de187faafa7c259ceab7dba67 --- clatd_test.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 08b444c77d..5de7b63429 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -1047,26 +1047,6 @@ void expectSocketBound(int ifindex, int sock) { TEST_F(ClatdTest, ConfigureIpv6Address) { struct tun_data tunnel = makeTunData(); - // Run configure_clat_ipv6_address. - ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), nullptr /* v6_addr */)); - - // Check that it generated an IID in the same prefix as the address assigned to the interface, - // and that the IID is not the default IID. - in6_addr addr = sTun.srcAddr(); - EXPECT_TRUE(ipv6_prefix_equal(&Global_Clatd_Config.ipv6_local_subnet, &addr)); - EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&Global_Clatd_Config.ipv6_local_subnet, &addr)); - EXPECT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); - EXPECT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); - - expectSocketBound(sTun.ifindex(), tunnel.read_fd6); - - freeTunData(&tunnel); -} - -TEST_F(ClatdTest, ConfigureIpv6AddressCommandLine) { - struct tun_data tunnel = makeTunData(); - ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); const char *addrStr = "2001:db8::f00"; From fc38d9b1ea857bf7e3abed2396e67d35a02dbd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 3 Mar 2020 17:36:52 -0800 Subject: [PATCH 171/261] clatd: don't 'pretend' to disable IPV6_CHECKSUM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code is not needed. The comment doesn't match the functionality as implemented: 0 means *ON* at offset 0 - not clear when this would make sense. 2 means on at offset 2 - this would be correct for IPPROTO_ICMPV6 4 means on at offset 4 - thus would be correct for IPPROTO_MH -1 would actually mean off, but that's also the socket creation default. In practice this doesn't even matter, because this socket is used only for transmit and sockets like this one which are created via socket(AF_INET6, SOCK_RAW, IPPROTO_RAW) default to having IPV6_HDRINCL enabled which doesn't even support transmit checksum calculation. This is because on a header inclusive RAW6 socket rawv6_sendmsg (which is the core implementation for all write operations on AF_INET6 raw sockets) calls into rawv6_send_hdrinc instead of ip6_append_data and ip6_flush_pending_frames/rawv6_push_pending_frames and simply never even references the socket field that the IPV6_CHECKSUM socket option changes (easy to check by checking accesses to raw6_sk(sk)->checksum). Also: it would be hard to make it support it due to checksum offset being protocol dependent and thus not even a constant on all protocol capable (ie. IPPROTO_RAW) sockets. Test: built, flashed to flame verified ipv4 clat functionality with no offload Bug: 150730674 Signed-off-by: Maciej Żenczykowski Change-Id: I62990f427c24cf62a38bd63d5c9d9dea4f0dc2f6 --- clatd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clatd.c b/clatd.c index 82df63db30..54fc2f749f 100644 --- a/clatd.c +++ b/clatd.c @@ -237,10 +237,6 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { exit(1); } - int off = 0; - if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { - logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); - } if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); } From 073e80cb39fb1408edacdd413d8c5f3f889d69fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 1 Apr 2020 21:09:13 +0000 Subject: [PATCH 172/261] clatd: don't 'pretend' to disable IPV6_CHECKSUM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code is not needed. The comment doesn't match the functionality as implemented: 0 means *ON* at offset 0 - not clear when this would make sense. 2 means on at offset 2 - this would be correct for IPPROTO_ICMPV6 4 means on at offset 4 - thus would be correct for IPPROTO_MH -1 would actually mean off, but that's also the socket creation default. In practice this doesn't even matter, because this socket is used only for transmit and sockets like this one which are created via socket(AF_INET6, SOCK_RAW, IPPROTO_RAW) default to having IPV6_HDRINCL enabled which doesn't even support transmit checksum calculation. This is because on a header inclusive RAW6 socket rawv6_sendmsg (which is the core implementation for all write operations on AF_INET6 raw sockets) calls into rawv6_send_hdrinc instead of ip6_append_data and ip6_flush_pending_frames/rawv6_push_pending_frames and simply never even references the socket field that the IPV6_CHECKSUM socket option changes (easy to check by checking accesses to raw6_sk(sk)->checksum). Also: it would be hard to make it support it due to checksum offset being protocol dependent and thus not even a constant on all protocol capable (ie. IPPROTO_RAW) sockets. Test: built, flashed to flame verified ipv4 clat functionality with no offload Bug: 150730674 Signed-off-by: Maciej Żenczykowski Change-Id: I62990f427c24cf62a38bd63d5c9d9dea4f0dc2f6 Merged-In: I62990f427c24cf62a38bd63d5c9d9dea4f0dc2f6 --- clatd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clatd.c b/clatd.c index 82df63db30..54fc2f749f 100644 --- a/clatd.c +++ b/clatd.c @@ -237,10 +237,6 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { exit(1); } - int off = 0; - if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) { - logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno)); - } if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); } From da74b8d0383307d462336e350569575bde5bf7a4 Mon Sep 17 00:00:00 2001 From: Bob Badour Date: Tue, 28 Apr 2020 10:43:07 -0700 Subject: [PATCH 173/261] Add METADATA to android-clat: Apache2=NOTICE Bug: 68860345 Bug: 69058154 Bug: 151953481 Test: no code changes Change-Id: I259500f0b10bbc7ef0c404bcc91ca40c46bce445 --- METADATA | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 METADATA diff --git a/METADATA b/METADATA new file mode 100644 index 0000000000..d97975ca3b --- /dev/null +++ b/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} From 12a1f2d8b5397993f8b5264b6b11def4a54ee011 Mon Sep 17 00:00:00 2001 From: Luke Huang Date: Thu, 28 May 2020 15:39:52 +0800 Subject: [PATCH 174/261] Use dnsproxyd_protocol_headers instead of libnetd_client_headers for NETID_USE_LOCAL_NAMESERVERS NETID_USE_LOCAL_NAMESERVERS is no longer in libnetd_client_headers Bug: 151895202 Test: build Change-Id: I064f60fa4edf5a1b2ad7f8a127ab531f0ab0c52c --- Android.bp | 2 +- dns64.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index ae4bf1da03..8585780263 100644 --- a/Android.bp +++ b/Android.bp @@ -14,7 +14,7 @@ cc_defaults { include_dirs: ["bionic/libc/dns/include"], // For NETID_USE_LOCAL_NAMESERVERS. - header_libs: ["libnetd_client_headers"], + header_libs: ["dnsproxyd_protocol_headers"], } // Code used both by the daemon and by unit tests. diff --git a/dns64.c b/dns64.c index 5ca85d999b..66cb7dc52c 100644 --- a/dns64.c +++ b/dns64.c @@ -25,7 +25,7 @@ #include #include -#include "NetdClient.h" +#include // NETID_USE_LOCAL_NAMESERVERS #include "dns64.h" #include "logging.h" #include "resolv_netid.h" From c12af098cdebb1ba34e9e1b262145f8091d44260 Mon Sep 17 00:00:00 2001 From: Luke Huang Date: Thu, 28 May 2020 15:39:52 +0800 Subject: [PATCH 175/261] Use dnsproxyd_protocol_headers instead of libnetd_client_headers for NETID_USE_LOCAL_NAMESERVERS NETID_USE_LOCAL_NAMESERVERS is no longer in libnetd_client_headers Bug: 151895202 Test: build Change-Id: I064f60fa4edf5a1b2ad7f8a127ab531f0ab0c52c Merged-In: I064f60fa4edf5a1b2ad7f8a127ab531f0ab0c52c --- Android.bp | 2 +- dns64.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index ae4bf1da03..8585780263 100644 --- a/Android.bp +++ b/Android.bp @@ -14,7 +14,7 @@ cc_defaults { include_dirs: ["bionic/libc/dns/include"], // For NETID_USE_LOCAL_NAMESERVERS. - header_libs: ["libnetd_client_headers"], + header_libs: ["dnsproxyd_protocol_headers"], } // Code used both by the daemon and by unit tests. diff --git a/dns64.c b/dns64.c index 5ca85d999b..66cb7dc52c 100644 --- a/dns64.c +++ b/dns64.c @@ -25,7 +25,7 @@ #include #include -#include "NetdClient.h" +#include // NETID_USE_LOCAL_NAMESERVERS #include "dns64.h" #include "logging.h" #include "resolv_netid.h" From 27da0ad045ce9e386997bcd3d063ced897521c76 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 1 Jun 2020 12:15:20 +0900 Subject: [PATCH 176/261] Remove DNS64 detection code from clatd. This has been unused since Q, when the NAT64 prefix started being detected and passed in by netd instead. Many things depend on the prefix being correct, including clat BPF offload and DNS64 synthesis, and doing DNS64 detection in clatd provides no way to make these work. Additionally, R now supports getting the NAT64 prefix via the RA, which makes DNS64 detection in clatd even less useful. This CL removes all the DNS64 detection code. The new code parses the prefix in main.c, and if the prefix is not valid, refuses to start. This also lets us remove the -n parameter which was only used for DNS64 detection. It also removes a dependency on dnsproxyd_protocol_headers, which should be depended on by as few parts of the system as possible. Test: m Bug: 144730808 Bug: 151895202 Test: IPv6-only wifi continues to work Change-Id: I892f364d6cbfe76277686387e35c04d3d6eb5ecc --- Android.bp | 10 +++---- clatd.c | 17 ++++++++---- clatd.conf | 7 ----- clatd.h | 2 +- config.c | 57 +------------------------------------- config.h | 4 +-- dns64.c | 80 ------------------------------------------------------ dns64.h | 23 ---------------- main.c | 32 ++++++---------------- 9 files changed, 27 insertions(+), 205 deletions(-) delete mode 100644 dns64.c delete mode 100644 dns64.h diff --git a/Android.bp b/Android.bp index 8585780263..bff97d3338 100644 --- a/Android.bp +++ b/Android.bp @@ -10,11 +10,10 @@ cc_defaults { "-Wno-address-of-packed-member", ], - // For NETID_UNSET and MARK_UNSET. - include_dirs: ["bionic/libc/dns/include"], - - // For NETID_USE_LOCAL_NAMESERVERS. - header_libs: ["dnsproxyd_protocol_headers"], + // For MARK_UNSET. + header_libs: [ + "libnetd_client_headers" + ], } // Code used both by the daemon and by unit tests. @@ -23,7 +22,6 @@ filegroup { srcs: [ "config.c", "clatd.c", - "dns64.c", "dump.c", "getaddr.c", "icmp.c", diff --git a/clatd.c b/clatd.c index 54fc2f749f..3f9dc71c72 100644 --- a/clatd.c +++ b/clatd.c @@ -39,14 +39,14 @@ #include #include -#include +#include // For MARK_UNSET. +#include // For AID_CLAT. #include "clatd.h" #include "config.h" #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "resolv_netid.h" #include "ring.h" #include "setif.h" #include "translate.h" @@ -388,18 +388,23 @@ int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_ * 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 + * v4_addr - the v4 address to use on the tunnel interface + * v6_addr - the v6 address to use on the native interface * tunnel - tun device data - * net_id - NetID to use, NETID_UNSET indicates use of default network * mark - the socket mark to use for the sending raw socket */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6_addr, struct tun_data *tunnel, unsigned net_id, - uint32_t mark) { - if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { + const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { + if (!read_config("/system/etc/clatd.conf", uplink_interface)) { logmsg(ANDROID_LOG_FATAL, "read_config failed"); exit(1); } + if (!plat_prefix || 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); + exit(1); + } + int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark); // clamp to minimum ipv6 mtu - this probably cannot ever trigger if (mtu < 1280) mtu = 1280; diff --git a/clatd.conf b/clatd.conf index aef9c757fd..1025fb25e6 100644 --- a/clatd.conf +++ b/clatd.conf @@ -11,10 +11,3 @@ ipv6_host_id :: # which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). ipv4_local_subnet 192.0.0.4 ipv4_local_prefixlen 29 - -# get the plat_subnet from dns lookups (requires DNS64) -plat_from_dns64 yes - -# 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 index e1d28a7194..a3f932651a 100644 --- a/clatd.h +++ b/clatd.h @@ -45,7 +45,7 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter const char *src_addr); int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark); void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6, struct tun_data *tunnel, unsigned net_id, uint32_t mark); + const char *v6, struct tun_data *tunnel, uint32_t mark); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/config.c b/config.c index 1c1a1f7955..db0abc8671 100644 --- a/config.c +++ b/config.c @@ -30,7 +30,6 @@ #include "clatd.h" #include "config.h" -#include "dns64.h" #include "getaddr.h" #include "logging.h" @@ -158,35 +157,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } -/* function: dns64_detection - * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response - * with a query backoff timer - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network - */ -void dns64_detection(unsigned net_id) { - int backoff_sleep, status; - struct in6_addr tmp_ptr; - - backoff_sleep = 1; - - while (1) { - status = plat_prefix(DNS64_DETECTION_HOSTNAME, net_id, &tmp_ptr); - if (status > 0) { - memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); - return; - } - logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); - sleep(backoff_sleep); - backoff_sleep *= 2; - if (backoff_sleep >= 1800) { - // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the - // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only - // to IPv6 with NAT64). - backoff_sleep = 1800; - } - } -} - /* function: gen_random_iid * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix * myaddr - IPv6 address to write to @@ -293,13 +263,9 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { * failure, 1 on success * file - filename to parse * uplink_interface - interface to use to reach the internet and supplier of address space - * plat_prefix - (optional) plat prefix to use, otherwise follow config file - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id) { +int read_config(const char *file, const char *uplink_interface) { cnode *root = config_node("", ""); - void *tmp_ptr = NULL; unsigned flags; if (!root) { @@ -326,27 +292,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; - if (plat_prefix) { // plat subnet is coming from the command line - if (inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) { - logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix); - goto failed; - } - } else { - tmp_ptr = (void *)config_item_str(root, "plat_from_dns64", "yes"); - if (!tmp_ptr || strcmp(tmp_ptr, "no") == 0) { - free(tmp_ptr); - - if (!config_item_ip6(root, "plat_subnet", NULL, &Global_Clatd_Config.plat_subnet)) { - logmsg(ANDROID_LOG_FATAL, "plat_from_dns64 disabled, but no plat_subnet specified"); - goto failed; - } - } else { - free(tmp_ptr); - - dns64_detection(net_id); - } - } - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; /* In order to prevent multiple devices attempting to use the same clat address, never use a diff --git a/config.h b/config.h index d5c43990ec..c47fc88f54 100644 --- a/config.h +++ b/config.h @@ -23,7 +23,6 @@ #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" -#define DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { struct in6_addr ipv6_local_subnet; @@ -37,8 +36,7 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id); +int read_config(const char *file, const char *uplink_interface); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); diff --git a/dns64.c b/dns64.c deleted file mode 100644 index 66cb7dc52c..0000000000 --- a/dns64.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * dns64.c - find the nat64 prefix with a dns64 lookup - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include // NETID_USE_LOCAL_NAMESERVERS -#include "dns64.h" -#include "logging.h" -#include "resolv_netid.h" - -/* function: plat_prefix - * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on - * failure - * ipv4_name - name to lookup - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network - * prefix - the plat /96 prefix - */ -int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) { - const struct addrinfo hints = { - .ai_family = AF_INET6, - }; - int status; - struct addrinfo *result = NULL; - struct in6_addr plat_addr; - char plat_addr_str[INET6_ADDRSTRLEN]; - - logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); - - // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). - if (net_id != NETID_UNSET) { - net_id |= NETID_USE_LOCAL_NAMESERVERS; - } - - status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); - if (status != 0 || result == NULL) { - logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", ipv4_name, status, - gai_strerror(status)); - return 0; - } - - // Use only the first result. If other records are present, possibly with - // differing DNS64 prefixes they are ignored (there is very little sensible - // that could be done with them at this time anyway). - - if (result->ai_family != AF_INET6) { - logmsg(ANDROID_LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family); - return 0; - } - plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr; - // Only /96 DNS64 prefixes are supported at this time. - plat_addr.s6_addr32[3] = 0; - freeaddrinfo(result); - - logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", - inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str))); - *prefix = plat_addr; - return 1; -} diff --git a/dns64.h b/dns64.h deleted file mode 100644 index f5eaea8bc5..0000000000 --- a/dns64.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * dns64.h - find the nat64 prefix with a dns64 lookup - */ -#ifndef __DNS64_H__ -#define __DNS64_H__ - -int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix); - -#endif diff --git a/main.c b/main.c index e973bf3d07..c4834d9ce4 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ * main.c - main function */ +#include #include #include #include @@ -24,7 +25,7 @@ #include #include -#include "resolv_netid.h" +#include // For MARK_UNSET. #include "clatd.h" #include "common.h" @@ -44,7 +45,6 @@ void print_help() { printf("-p [plat prefix]\n"); printf("-4 [IPv4 address]\n"); printf("-6 [IPv6 address]\n"); - printf("-n [NetId]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); } @@ -55,13 +55,12 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; - unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:n:m:t:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -75,9 +74,6 @@ int main(int argc, char **argv) { case '6': v6_addr = optarg; break; - case 'n': - net_id_str = optarg; - break; case 'm': mark_str = optarg; break; @@ -98,11 +94,6 @@ int main(int argc, char **argv) { exit(1); } - if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { - logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); - exit(1); - } - if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); exit(1); @@ -123,10 +114,10 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s plat=%s v4=%s v6=%s", - CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)", - mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)", - v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", + CLATD_VERSION, uplink_interface, mark_str ? mark_str : "(none)", + plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", + v6_addr ? v6_addr : "(none)"); // run under a regular user but keep needed capabilities drop_root_but_keep_caps(); @@ -137,12 +128,7 @@ int main(int argc, char **argv) { // keeps only admin capability set_capability(1 << CAP_NET_ADMIN); - // When run from netd, the environment variable ANDROID_DNS_MODE is set to - // "local", but that only works for the netd process itself. Removing the - // following line causes XLAT failure in permissive mode. - unsetenv("ANDROID_DNS_MODE"); - - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id, mark); + configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); // Drop all remaining capabilities. set_capability(0); From baa3c6aefa73bec1ea193c001cc8f516ecb6ef3f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 2 Jun 2020 01:55:12 +0900 Subject: [PATCH 177/261] Delete the clatd code that assigns IPv4 and IPv6 addresses. The IPv4 and IPv6 address assignment code, and its tests, were migrated to ClatdController in Q, and this code has been unused since then. This code is not only unused, it also *should* never be used, because if it were, many parts of 464xlat on Android would not work as expected. This includes 464xlat BPF offload and data usage accounting, both of which require netd to know exactly which IPv4 and IPv6 addresses are used by clatd at any given time. Most of the code is deleted, but the function to generate random checksum-neutral IIDs was moved to the tests instead. This change does not delete the configuration file parsing code. That will be done in a follow-up CL. Bug: 144730808 Bug: 150738490 Test: atest clatd_test Test: atest netd_integration_test Test: IPv6-only wifi continues to work Change-Id: I8265e9bb04d44ed1dda445fa91881fcd1601dc47 --- clatd.c | 89 ++------------------- clatd.conf | 14 +--- clatd_test.cpp | 208 ++++++++----------------------------------------- config.c | 101 ------------------------ config.h | 4 - 5 files changed, 41 insertions(+), 375 deletions(-) diff --git a/clatd.c b/clatd.c index 3f9dc71c72..50693f5358 100644 --- a/clatd.c +++ b/clatd.c @@ -109,52 +109,15 @@ int configure_packet_socket(int sock) { return 1; } -/* function: ipv4_address_generate - * picks a free IPv4 address from the local subnet or exits if there are no free addresses - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_generate() { - // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically, - // there is a race here - if another clatd calls config_select_ipv4_address after we do, but - // before we call add_address, it can end up having the same IP address as we do. But the time - // window in which this can happen is extremely small, and even if we end up with a duplicate - // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go - // down. - in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet, - Global_Clatd_Config.ipv4_local_prefixlen); - if (localaddr == INADDR_NONE) { - logmsg(ANDROID_LOG_FATAL, "No free IPv4 address in %s/%d", - inet_ntoa(Global_Clatd_Config.ipv4_local_subnet), - Global_Clatd_Config.ipv4_local_prefixlen); - exit(1); - } - return localaddr; -} - -/* function: ipv4_address_from_cmdline - * configures the IPv4 address specified on the command line, or exits if the address is not valid - * v4_addr - a string, the IPv4 address - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) { - in_addr_t localaddr; - if (!inet_pton(AF_INET, v4_addr, &localaddr)) { - logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); - exit(1); - } - return localaddr; -} - /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface * tunnel - tun device data * mtu - mtu of tun device */ void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { - if (v4_addr) { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr); - } else { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate(); + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); + exit(1); } char addrstr[INET_ADDRSTRLEN]; @@ -272,43 +235,6 @@ int ipv6_address_changed(const char *interface) { } } -/* function: clat_ipv6_address_from_interface - * picks the clat IPv6 address based on the interface address - * interface - uplink interface name - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_interface(const char *interface) { - union anyip *interface_ip; - - // TODO: check that the prefix length is /64. - interface_ip = getinterface_ip(interface, AF_INET6); - if (!interface_ip) { - logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); - return 0; - } - - // Generate an interface ID. - config_generate_local_ipv6_subnet(&interface_ip->ip6); - - Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; - free(interface_ip); - return 1; -} - -/* function: clat_ipv6_address_from_cmdline - * parses the clat IPv6 address from the command line - * v4_addr - a string, the IPv6 address - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_cmdline(const char *v6_addr) { - if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { - logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); - return 0; - } - - return 1; -} - /* function: configure_clat_ipv6_address * picks the clat IPv6 address and configures packet translation to use it. * tunnel - tun device data @@ -317,13 +243,10 @@ static int clat_ipv6_address_from_cmdline(const char *v6_addr) { */ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *v6_addr) { - int ret; - if (v6_addr) { - ret = clat_ipv6_address_from_cmdline(v6_addr); - } else { - ret = clat_ipv6_address_from_interface(interface); + if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + return 0; } - if (!ret) return 0; char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); diff --git a/clatd.conf b/clatd.conf index 1025fb25e6..36fcafd64c 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1,13 +1 @@ -# Host IID to use as the source of CLAT traffic. -# This is a /128 taken out of the /64 on the parent interface. -# A host IID of :: means to generate a checksum-neutral, random IID. -ipv6_host_id :: - -# IPv4 address configuration to use when selecting a host address. The first -# clat daemon started will use the address specified by ipv4_local_subnet. If -# more than one daemon is run at the same time, subsequent daemons will use -# other addresses in the prefix of length ipv4_local prefixlen that contains -# ipv4_local_subnet. The default is to use the IANA-assigned range 192.0.0.0/29, -# which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). -ipv4_local_subnet 192.0.0.4 -ipv4_local_prefixlen 29 +temporary_placeholder 0 diff --git a/clatd_test.cpp b/clatd_test.cpp index 5de7b63429..8f10e32898 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -627,183 +627,12 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -int count_onebits(const void *data, size_t size) { - int onebits = 0; - for (size_t pos = 0; pos < size; pos++) { - uint8_t *byte = ((uint8_t *)data) + pos; - for (int shift = 0; shift < 8; shift++) { - onebits += (*byte >> shift) & 1; - } - } - return onebits; -} - -TEST_F(ClatdTest, TestCountOnebits) { - uint64_t i; - i = 1; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i <<= 61; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i |= ((uint64_t)1 << 33); - ASSERT_EQ(2, count_onebits(&i, sizeof(i))); - i = 0xf1000202020000f0; - ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i))); -} - -TEST_F(ClatdTest, TestGenIIDConfigured) { - struct in6_addr myaddr, expected; - Global_Clatd_Config.use_dynamic_iid = 0; - ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr)); - config_generate_local_ipv6_subnet(&myaddr); - expect_ipv6_addr_equal(&expected, &myaddr); - - Global_Clatd_Config.use_dynamic_iid = 1; - config_generate_local_ipv6_subnet(&myaddr); - EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&expected, &myaddr)); -} - -TEST_F(ClatdTest, TestGenIIDRandom) { - struct in6_addr interface_ipv6; - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &interface_ipv6)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - - // Generate a boatload of random IIDs. - int onebits = 0; - uint64_t prev_iid = 0; - for (int i = 0; i < 100000; i++) { - struct in6_addr myaddr = interface_ipv6; - - config_generate_local_ipv6_subnet(&myaddr); - - // Check the generated IP address is in the same prefix as the interface IPv6 address. - EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr)); - - // Check that consecutive IIDs are not the same. - uint64_t iid = *(uint64_t *)(&myaddr.s6_addr[8]); - ASSERT_TRUE(iid != prev_iid) - << "Two consecutive random IIDs are the same: " - << std::showbase << std::hex - << iid << "\n"; - prev_iid = iid; - - // Check that the IID is checksum-neutral with the NAT64 prefix and the - // local prefix. - struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; - struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet; - - uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr))); - uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, &myaddr, sizeof(myaddr))); - - if (c1 != c2) { - char myaddr_str[INET6_ADDRSTRLEN], plat_str[INET6_ADDRSTRLEN], ipv4_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &myaddr, myaddr_str, sizeof(myaddr_str)); - inet_ntop(AF_INET6, plat_subnet, plat_str, sizeof(plat_str)); - inet_ntop(AF_INET, ipv4addr, ipv4_str, sizeof(ipv4_str)); - FAIL() - << "Bad IID: " << myaddr_str - << " not checksum-neutral with " << ipv4_str << " and " << plat_str - << std::showbase << std::hex - << "\n IPv4 checksum: " << c1 - << "\n IPv6 checksum: " << c2 - << "\n"; - } - - // Check that IIDs are roughly random and use all the bits by counting the - // total number of bits set to 1 in a random sample of 100000 generated IIDs. - onebits += count_onebits(&iid, sizeof(iid)); - } - EXPECT_LE(3190000, onebits); - EXPECT_GE(3210000, onebits); -} - -extern "C" addr_free_func config_is_ipv4_address_free; -int never_free(in_addr_t /* addr */) { return 0; } -int always_free(in_addr_t /* addr */) { return 1; } -int only2_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 2; } -int over6_free(in_addr_t addr) { return (ntohl(addr) & 0xff) >= 6; } -int only10_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 10; } - -TEST_F(ClatdTest, SelectIPv4Address) { - struct in_addr addr; - - inet_pton(AF_INET, kIPv4LocalAddr, &addr); - - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - - // If no addresses are free, return INADDR_NONE. - config_is_ipv4_address_free = never_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 16)); - - // If the configured address is free, pick that. But a prefix that's too big is invalid. - config_is_ipv4_address_free = always_free; - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 20)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 15)); - - // A prefix length of 32 works, but anything above it is invalid. - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 32)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 33)); - - // If another address is free, pick it. - config_is_ipv4_address_free = over6_free; - EXPECT_EQ(inet_addr("192.0.0.6"), config_select_ipv4_address(&addr, 29)); - - // Check that we wrap around to addresses that are lower than the first address. - config_is_ipv4_address_free = only2_free; - EXPECT_EQ(inet_addr("192.0.0.2"), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 30)); - - // If a free address exists outside the prefix, we don't pick it. - config_is_ipv4_address_free = only10_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr("192.0.0.10"), config_select_ipv4_address(&addr, 24)); - - // Now try using the real function which sees if IP addresses are free using bind(). - // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - addr.s_addr = inet_addr("8.8.8.8"); - EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29)); - - addr.s_addr = inet_addr("127.0.0.1"); - EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); -} - TEST_F(ClatdTest, DetectMtu) { // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); } -TEST_F(ClatdTest, ConfigureTunIp) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - - Global_Clatd_Config.ipv4_local_prefixlen = 29; - - // Create an interface for configure_tun_ip to configure and bring up. - TunInterface v4Iface; - ASSERT_EQ(0, v4Iface.init()); - struct tun_data tunnel = makeTunData(); - strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - - configure_tun_ip(&tunnel, nullptr /* v4_addr */, 1472); - EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr); - - union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); - EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr); - free(ip); - - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - v4Iface.destroy(); -} - TEST_F(ClatdTest, ConfigureTunIpManual) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - Global_Clatd_Config.ipv4_local_prefixlen = 29; // Create an interface for configure_tun_ip to configure and bring up. @@ -820,7 +649,6 @@ TEST_F(ClatdTest, ConfigureTunIpManual) { EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); free(ip); - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; v4Iface.destroy(); } @@ -986,6 +814,29 @@ TEST_F(ClatdTest, Fragmentation) { ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); } +// picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix +void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, + struct in6_addr *plat_subnet) { + // Fill last 8 bytes of IPv6 address with random bits. + arc4random_buf(&myaddr->s6_addr[8], 8); + + // Make the IID checksum-neutral. That is, make it so that: + // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) + // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): + // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) + // Do this by adjusting the two bytes in the middle of the IID. + + uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; + + uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); + uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + + ip_checksum_add(0, myaddr, sizeof(*myaddr)); + + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + myaddr->s6_addr[11] = delta >> 8; + myaddr->s6_addr[12] = delta & 0xff; +} + void check_translate_checksum_neutral(const uint8_t *original, size_t original_len, size_t expected_len, const char *msg) { uint8_t translated[MAXMTU]; @@ -1006,7 +857,10 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { Global_Clatd_Config.ipv6_host_id = in6addr_any; ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); - config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); + + gen_random_iid(&Global_Clatd_Config.ipv6_local_subnet, &Global_Clatd_Config.ipv4_local_subnet, + &Global_Clatd_Config.plat_subnet); + ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); @@ -1079,7 +933,13 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), }; const char *ifname = sTun.name().c_str(); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr)); + + in6_addr myaddr = sTun.srcAddr(); + gen_random_iid(&myaddr, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet); + char addrstr[INET6_ADDRSTRLEN]; + ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); + + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, addrstr)); EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); diff --git a/config.c b/config.c index db0abc8671..d4cc9af4d2 100644 --- a/config.c +++ b/config.c @@ -157,107 +157,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } -/* function: gen_random_iid - * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix - * myaddr - IPv6 address to write to - * ipv4_local_subnet - clat IPv4 address - * plat_subnet - NAT64 prefix - */ -void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, - struct in6_addr *plat_subnet) { - // Fill last 8 bytes of IPv6 address with random bits. - arc4random_buf(&myaddr->s6_addr[8], 8); - - // Make the IID checksum-neutral. That is, make it so that: - // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) - // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): - // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) - // Do this by adjusting the two bytes in the middle of the IID. - - uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; - - uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); - uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, myaddr, sizeof(*myaddr)); - - uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); - myaddr->s6_addr[11] = delta >> 8; - myaddr->s6_addr[12] = delta & 0xff; -} - -// Factored out to a separate function for testability. -int connect_is_ipv4_address_free(in_addr_t addr) { - int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (s == -1) { - return 0; - } - - // Attempt to connect to the address. If the connection succeeds and getsockname returns the same - // the address then the address is already assigned to the system and we can't use it. - struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = htons(53) }; - socklen_t len = sizeof(sin); - int inuse = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 && - getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) && - sin.sin_addr.s_addr == addr; - - close(s); - return !inuse; -} - -addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free; - -/* function: config_select_ipv4_address - * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order - * ip - the IP address from the configuration file - * prefixlen - the length of the prefix from which addresses may be selected. - * returns: the IPv4 address, or INADDR_NONE if no addresses were available - */ -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) { - in_addr_t chosen = INADDR_NONE; - - // Don't accept prefixes that are too large because we scan addresses one by one. - if (prefixlen < 16 || prefixlen > 32) { - return chosen; - } - - // All these are in host byte order. - in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); - in_addr_t ipv4 = ntohl(ip->s_addr); - in_addr_t first_ipv4 = ipv4; - in_addr_t prefix = ipv4 & mask; - - // Pick the first IPv4 address in the pool, wrapping around if necessary. - // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0. - do { - if (config_is_ipv4_address_free(htonl(ipv4))) { - chosen = htonl(ipv4); - break; - } - ipv4 = prefix | ((ipv4 + 1) & ~mask); - } while (ipv4 != first_ipv4); - - return chosen; -} - -/* function: config_generate_local_ipv6_subnet - * generates the local ipv6 subnet when given the interface ip requires config.ipv6_host_id - * interface_ip - in: interface ip, out: local ipv6 host address - */ -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { - int i; - - if (Global_Clatd_Config.use_dynamic_iid) { - /* Generate a random interface ID. */ - gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet, - &Global_Clatd_Config.plat_subnet); - } else { - /* Use the specified interface ID. */ - for (i = 2; i < 4; i++) { - interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i]; - } - } -} - /* function: read_config * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on * failure, 1 on success diff --git a/config.h b/config.h index c47fc88f54..50efeeba21 100644 --- a/config.h +++ b/config.h @@ -37,10 +37,6 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface); -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); -typedef int (*addr_free_func)(in_addr_t addr); - #endif /* __CONFIG_H__ */ From df61a34d3c702485f8fe4266f0753c439f8a0682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:22:22 -0700 Subject: [PATCH 178/261] cleanup - dump_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep dump_config comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I781bbb0523211349893d947a500edf0eb12dedfd --- config.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/config.c b/config.c index d4cc9af4d2..1c380dcc79 100644 --- a/config.c +++ b/config.c @@ -210,22 +210,3 @@ failed: free(root); return 0; } - -/* function; dump_config - * prints the current config - */ -void dump_config() { - char charbuffer[INET6_ADDRSTRLEN]; - - logmsg( - ANDROID_LOG_DEBUG, "ipv6_local_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg( - ANDROID_LOG_DEBUG, "ipv4_local_subnet = %s", - inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); - logmsg(ANDROID_LOG_DEBUG, "plat_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "default_pdp_interface = %s", - Global_Clatd_Config.default_pdp_interface); -} From 6b759e57d992012e2b30718ba9403f4947c60b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:24:08 -0700 Subject: [PATCH 179/261] cleanup - ipv6_host_id and is_dynamic_iid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'ipv6_host_id|is_dynamic_iid' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: Ie3ed4583a95f15165a05412a556bae99dc07706b --- clatd_test.cpp | 3 --- config.c | 14 -------------- config.h | 2 -- 3 files changed, 19 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 8f10e32898..e24b03665b 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -585,8 +585,6 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - Global_Clatd_Config.use_dynamic_iid = 1; Global_Clatd_Config.default_pdp_interface = const_cast(sTun.name().c_str()); } @@ -854,7 +852,6 @@ void check_translate_checksum_neutral(const uint8_t *original, size_t original_l TEST_F(ClatdTest, TranslateChecksumNeutral) { // Generate a random clat IPv6 address and check that translation is checksum-neutral. - Global_Clatd_Config.ipv6_host_id = in6addr_any; ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); diff --git a/config.c b/config.c index 1c380dcc79..eea12f78ca 100644 --- a/config.c +++ b/config.c @@ -165,7 +165,6 @@ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp */ int read_config(const char *file, const char *uplink_interface) { cnode *root = config_node("", ""); - unsigned flags; if (!root) { logmsg(ANDROID_LOG_FATAL, "out of memory"); @@ -191,19 +190,6 @@ int read_config(const char *file, const char *uplink_interface) { &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; - - /* In order to prevent multiple devices attempting to use the same clat address, never use a - statically-configured interface ID on a broadcast interface such as wifi. */ - if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { - ifc_init(); - ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags); - ifc_close(); - Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0; - } else { - Global_Clatd_Config.use_dynamic_iid = 1; - } - return 1; failed: diff --git a/config.h b/config.h index 50efeeba21..9231906e1a 100644 --- a/config.h +++ b/config.h @@ -26,12 +26,10 @@ struct clat_config { struct in6_addr ipv6_local_subnet; - struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; char *default_pdp_interface; - int use_dynamic_iid; }; extern struct clat_config Global_Clatd_Config; From 606c53fb611dd137264cb4540da9686a606b4bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:25:14 -0700 Subject: [PATCH 180/261] cleanup - config_item_str() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep config_item_str comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I1a8f21e55534457718ea58cdab15266b9f232fd3 --- config.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/config.c b/config.c index eea12f78ca..4be61a7652 100644 --- a/config.c +++ b/config.c @@ -35,23 +35,6 @@ 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 From 649581fdd939538fa5ca155bd109edd270729e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:26:52 -0700 Subject: [PATCH 181/261] cleanup - config_item_int16() & DEFAULT_IPV4_LOCAL_PREFIXLEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config_item_int16|DEFAULT_IPV4_LOCAL_PREFIXLEN' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I71716cdcba382bc5fb1efa5000189f63a57015c9 --- config.c | 47 +---------------------------------------------- config.h | 1 - 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/config.c b/config.c index 4be61a7652..bcf77bad7c 100644 --- a/config.c +++ b/config.c @@ -35,49 +35,6 @@ struct clat_config Global_Clatd_Config; -/* 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 @@ -169,9 +126,7 @@ int read_config(const char *file, const char *uplink_interface) { &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if (!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, - &Global_Clatd_Config.ipv4_local_prefixlen)) - goto failed; + Global_Clatd_Config.ipv4_local_prefixlen = 29; return 1; diff --git a/config.h b/config.h index 9231906e1a..555b9681ae 100644 --- a/config.h +++ b/config.h @@ -22,7 +22,6 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" -#define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" struct clat_config { struct in6_addr ipv6_local_subnet; From c9b0e6d1ab7e6ea200d4767689fb30f20fec92b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:27:32 -0700 Subject: [PATCH 182/261] cleanup - config_item_ip6() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep config_item_ip6 comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I0338f7ac29fdb0de56bd4a500e2a827cab2c47f7 --- config.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/config.c b/config.c index bcf77bad7c..554db0e158 100644 --- a/config.c +++ b/config.c @@ -62,33 +62,6 @@ struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *d 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: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address From c29e1785c18cdf993db74107143f8a3878cb462d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 1 Jun 2020 08:48:07 +0000 Subject: [PATCH 183/261] Remove DNS64 detection code from clatd. This has been unused since Q, when the NAT64 prefix started being detected and passed in by netd instead. Many things depend on the prefix being correct, including clat BPF offload and DNS64 synthesis, and doing DNS64 detection in clatd provides no way to make these work. Additionally, R now supports getting the NAT64 prefix via the RA, which makes DNS64 detection in clatd even less useful. This CL removes all the DNS64 detection code. The new code parses the prefix in main.c, and if the prefix is not valid, refuses to start. This also lets us remove the -n parameter which was only used for DNS64 detection. It also removes a dependency on dnsproxyd_protocol_headers, which should be depended on by as few parts of the system as possible. Test: m Bug: 144730808 Bug: 151895202 Test: IPv6-only wifi continues to work Original-Change: https://android-review.googlesource.com/1320917 Merged-In: I892f364d6cbfe76277686387e35c04d3d6eb5ecc Change-Id: I892f364d6cbfe76277686387e35c04d3d6eb5ecc --- Android.bp | 10 +++---- clatd.c | 17 ++++++++---- clatd.conf | 7 ----- clatd.h | 2 +- config.c | 57 +------------------------------------- config.h | 4 +-- dns64.c | 80 ------------------------------------------------------ dns64.h | 23 ---------------- main.c | 32 ++++++---------------- 9 files changed, 27 insertions(+), 205 deletions(-) delete mode 100644 dns64.c delete mode 100644 dns64.h diff --git a/Android.bp b/Android.bp index 8585780263..bff97d3338 100644 --- a/Android.bp +++ b/Android.bp @@ -10,11 +10,10 @@ cc_defaults { "-Wno-address-of-packed-member", ], - // For NETID_UNSET and MARK_UNSET. - include_dirs: ["bionic/libc/dns/include"], - - // For NETID_USE_LOCAL_NAMESERVERS. - header_libs: ["dnsproxyd_protocol_headers"], + // For MARK_UNSET. + header_libs: [ + "libnetd_client_headers" + ], } // Code used both by the daemon and by unit tests. @@ -23,7 +22,6 @@ filegroup { srcs: [ "config.c", "clatd.c", - "dns64.c", "dump.c", "getaddr.c", "icmp.c", diff --git a/clatd.c b/clatd.c index 54fc2f749f..3f9dc71c72 100644 --- a/clatd.c +++ b/clatd.c @@ -39,14 +39,14 @@ #include #include -#include +#include // For MARK_UNSET. +#include // For AID_CLAT. #include "clatd.h" #include "config.h" #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "resolv_netid.h" #include "ring.h" #include "setif.h" #include "translate.h" @@ -388,18 +388,23 @@ int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_ * 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 + * v4_addr - the v4 address to use on the tunnel interface + * v6_addr - the v6 address to use on the native interface * tunnel - tun device data - * net_id - NetID to use, NETID_UNSET indicates use of default network * mark - the socket mark to use for the sending raw socket */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6_addr, struct tun_data *tunnel, unsigned net_id, - uint32_t mark) { - if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) { + const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { + if (!read_config("/system/etc/clatd.conf", uplink_interface)) { logmsg(ANDROID_LOG_FATAL, "read_config failed"); exit(1); } + if (!plat_prefix || 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); + exit(1); + } + int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark); // clamp to minimum ipv6 mtu - this probably cannot ever trigger if (mtu < 1280) mtu = 1280; diff --git a/clatd.conf b/clatd.conf index aef9c757fd..1025fb25e6 100644 --- a/clatd.conf +++ b/clatd.conf @@ -11,10 +11,3 @@ ipv6_host_id :: # which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). ipv4_local_subnet 192.0.0.4 ipv4_local_prefixlen 29 - -# get the plat_subnet from dns lookups (requires DNS64) -plat_from_dns64 yes - -# 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 index e1d28a7194..a3f932651a 100644 --- a/clatd.h +++ b/clatd.h @@ -45,7 +45,7 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter const char *src_addr); int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark); void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6, struct tun_data *tunnel, unsigned net_id, uint32_t mark); + const char *v6, struct tun_data *tunnel, uint32_t mark); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/config.c b/config.c index 1c1a1f7955..db0abc8671 100644 --- a/config.c +++ b/config.c @@ -30,7 +30,6 @@ #include "clatd.h" #include "config.h" -#include "dns64.h" #include "getaddr.h" #include "logging.h" @@ -158,35 +157,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } -/* function: dns64_detection - * does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response - * with a query backoff timer - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network - */ -void dns64_detection(unsigned net_id) { - int backoff_sleep, status; - struct in6_addr tmp_ptr; - - backoff_sleep = 1; - - while (1) { - status = plat_prefix(DNS64_DETECTION_HOSTNAME, net_id, &tmp_ptr); - if (status > 0) { - memcpy(&Global_Clatd_Config.plat_subnet, &tmp_ptr, sizeof(struct in6_addr)); - return; - } - logmsg(ANDROID_LOG_WARN, "dns64_detection -- error, sleeping for %d seconds", backoff_sleep); - sleep(backoff_sleep); - backoff_sleep *= 2; - if (backoff_sleep >= 1800) { - // Scale down to one DNS query per half hour. Unnecessary DNS queries waste power, and the - // benefit is minimal (basically, only limited to the case where a network goes from IPv6-only - // to IPv6 with NAT64). - backoff_sleep = 1800; - } - } -} - /* function: gen_random_iid * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix * myaddr - IPv6 address to write to @@ -293,13 +263,9 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { * failure, 1 on success * file - filename to parse * uplink_interface - interface to use to reach the internet and supplier of address space - * plat_prefix - (optional) plat prefix to use, otherwise follow config file - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network */ -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id) { +int read_config(const char *file, const char *uplink_interface) { cnode *root = config_node("", ""); - void *tmp_ptr = NULL; unsigned flags; if (!root) { @@ -326,27 +292,6 @@ int read_config(const char *file, const char *uplink_interface, const char *plat &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; - if (plat_prefix) { // plat subnet is coming from the command line - if (inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) { - logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix); - goto failed; - } - } else { - tmp_ptr = (void *)config_item_str(root, "plat_from_dns64", "yes"); - if (!tmp_ptr || strcmp(tmp_ptr, "no") == 0) { - free(tmp_ptr); - - if (!config_item_ip6(root, "plat_subnet", NULL, &Global_Clatd_Config.plat_subnet)) { - logmsg(ANDROID_LOG_FATAL, "plat_from_dns64 disabled, but no plat_subnet specified"); - goto failed; - } - } else { - free(tmp_ptr); - - dns64_detection(net_id); - } - } - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; /* In order to prevent multiple devices attempting to use the same clat address, never use a diff --git a/config.h b/config.h index d5c43990ec..c47fc88f54 100644 --- a/config.h +++ b/config.h @@ -23,7 +23,6 @@ #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" #define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" -#define DNS64_DETECTION_HOSTNAME "ipv4only.arpa" struct clat_config { struct in6_addr ipv6_local_subnet; @@ -37,8 +36,7 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface, const char *plat_prefix, - unsigned net_id); +int read_config(const char *file, const char *uplink_interface); void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); diff --git a/dns64.c b/dns64.c deleted file mode 100644 index 66cb7dc52c..0000000000 --- a/dns64.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * dns64.c - find the nat64 prefix with a dns64 lookup - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include // NETID_USE_LOCAL_NAMESERVERS -#include "dns64.h" -#include "logging.h" -#include "resolv_netid.h" - -/* function: plat_prefix - * looks up an ipv4-only hostname and looks for a nat64 /96 prefix, returns 1 on success, 0 on - * failure - * ipv4_name - name to lookup - * net_id - (optional) netId to use, NETID_UNSET indicates use of default network - * prefix - the plat /96 prefix - */ -int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix) { - const struct addrinfo hints = { - .ai_family = AF_INET6, - }; - int status; - struct addrinfo *result = NULL; - struct in6_addr plat_addr; - char plat_addr_str[INET6_ADDRSTRLEN]; - - logmsg(ANDROID_LOG_INFO, "Detecting NAT64 prefix from DNS..."); - - // Be sure to query local DNS64 servers, bypassing Private DNS (if enabled). - if (net_id != NETID_UNSET) { - net_id |= NETID_USE_LOCAL_NAMESERVERS; - } - - status = android_getaddrinfofornet(ipv4_name, NULL, &hints, net_id, MARK_UNSET, &result); - if (status != 0 || result == NULL) { - logmsg(ANDROID_LOG_ERROR, "plat_prefix/dns(%s) status = %d/%s", ipv4_name, status, - gai_strerror(status)); - return 0; - } - - // Use only the first result. If other records are present, possibly with - // differing DNS64 prefixes they are ignored (there is very little sensible - // that could be done with them at this time anyway). - - if (result->ai_family != AF_INET6) { - logmsg(ANDROID_LOG_WARN, "plat_prefix/unexpected address family: %d", result->ai_family); - return 0; - } - plat_addr = ((struct sockaddr_in6 *)result->ai_addr)->sin6_addr; - // Only /96 DNS64 prefixes are supported at this time. - plat_addr.s6_addr32[3] = 0; - freeaddrinfo(result); - - logmsg(ANDROID_LOG_INFO, "Detected NAT64 prefix %s/96", - inet_ntop(AF_INET6, &plat_addr, plat_addr_str, sizeof(plat_addr_str))); - *prefix = plat_addr; - return 1; -} diff --git a/dns64.h b/dns64.h deleted file mode 100644 index f5eaea8bc5..0000000000 --- a/dns64.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * dns64.h - find the nat64 prefix with a dns64 lookup - */ -#ifndef __DNS64_H__ -#define __DNS64_H__ - -int plat_prefix(const char *ipv4_name, unsigned net_id, struct in6_addr *prefix); - -#endif diff --git a/main.c b/main.c index e973bf3d07..c4834d9ce4 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ * main.c - main function */ +#include #include #include #include @@ -24,7 +25,7 @@ #include #include -#include "resolv_netid.h" +#include // For MARK_UNSET. #include "clatd.h" #include "common.h" @@ -44,7 +45,6 @@ void print_help() { printf("-p [plat prefix]\n"); printf("-4 [IPv4 address]\n"); printf("-6 [IPv6 address]\n"); - printf("-n [NetId]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); } @@ -55,13 +55,12 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; - unsigned net_id = NETID_UNSET; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:n:m:t:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -75,9 +74,6 @@ int main(int argc, char **argv) { case '6': v6_addr = optarg; break; - case 'n': - net_id_str = optarg; - break; case 'm': mark_str = optarg; break; @@ -98,11 +94,6 @@ int main(int argc, char **argv) { exit(1); } - if (net_id_str != NULL && !parse_unsigned(net_id_str, &net_id)) { - logmsg(ANDROID_LOG_FATAL, "invalid NetID %s", net_id_str); - exit(1); - } - if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); exit(1); @@ -123,10 +114,10 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s plat=%s v4=%s v6=%s", - CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)", - mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)", - v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", + CLATD_VERSION, uplink_interface, mark_str ? mark_str : "(none)", + plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", + v6_addr ? v6_addr : "(none)"); // run under a regular user but keep needed capabilities drop_root_but_keep_caps(); @@ -137,12 +128,7 @@ int main(int argc, char **argv) { // keeps only admin capability set_capability(1 << CAP_NET_ADMIN); - // When run from netd, the environment variable ANDROID_DNS_MODE is set to - // "local", but that only works for the netd process itself. Removing the - // following line causes XLAT failure in permissive mode. - unsetenv("ANDROID_DNS_MODE"); - - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id, mark); + configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); // Drop all remaining capabilities. set_capability(0); From f13d56995f56b4c538578ed1040260e4d8da4521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 06:22:22 +0000 Subject: [PATCH 184/261] Delete the clatd code that assigns IPv4 and IPv6 addresses. The IPv4 and IPv6 address assignment code, and its tests, were migrated to ClatdController in Q, and this code has been unused since then. This code is not only unused, it also *should* never be used, because if it were, many parts of 464xlat on Android would not work as expected. This includes 464xlat BPF offload and data usage accounting, both of which require netd to know exactly which IPv4 and IPv6 addresses are used by clatd at any given time. Most of the code is deleted, but the function to generate random checksum-neutral IIDs was moved to the tests instead. This change does not delete the configuration file parsing code. That will be done in a follow-up CL. Bug: 144730808 Bug: 150738490 Test: atest clatd_test Test: atest netd_integration_test Test: IPv6-only wifi continues to work Original-Change: https://android-review.googlesource.com/1320923 Merged-In: I8265e9bb04d44ed1dda445fa91881fcd1601dc47 Change-Id: I8265e9bb04d44ed1dda445fa91881fcd1601dc47 --- clatd.c | 89 ++------------------- clatd.conf | 14 +--- clatd_test.cpp | 208 ++++++++----------------------------------------- config.c | 101 ------------------------ config.h | 4 - 5 files changed, 41 insertions(+), 375 deletions(-) diff --git a/clatd.c b/clatd.c index 3f9dc71c72..50693f5358 100644 --- a/clatd.c +++ b/clatd.c @@ -109,52 +109,15 @@ int configure_packet_socket(int sock) { return 1; } -/* function: ipv4_address_generate - * picks a free IPv4 address from the local subnet or exits if there are no free addresses - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_generate() { - // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically, - // there is a race here - if another clatd calls config_select_ipv4_address after we do, but - // before we call add_address, it can end up having the same IP address as we do. But the time - // window in which this can happen is extremely small, and even if we end up with a duplicate - // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go - // down. - in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet, - Global_Clatd_Config.ipv4_local_prefixlen); - if (localaddr == INADDR_NONE) { - logmsg(ANDROID_LOG_FATAL, "No free IPv4 address in %s/%d", - inet_ntoa(Global_Clatd_Config.ipv4_local_subnet), - Global_Clatd_Config.ipv4_local_prefixlen); - exit(1); - } - return localaddr; -} - -/* function: ipv4_address_from_cmdline - * configures the IPv4 address specified on the command line, or exits if the address is not valid - * v4_addr - a string, the IPv4 address - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) { - in_addr_t localaddr; - if (!inet_pton(AF_INET, v4_addr, &localaddr)) { - logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); - exit(1); - } - return localaddr; -} - /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface * tunnel - tun device data * mtu - mtu of tun device */ void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { - if (v4_addr) { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr); - } else { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate(); + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); + exit(1); } char addrstr[INET_ADDRSTRLEN]; @@ -272,43 +235,6 @@ int ipv6_address_changed(const char *interface) { } } -/* function: clat_ipv6_address_from_interface - * picks the clat IPv6 address based on the interface address - * interface - uplink interface name - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_interface(const char *interface) { - union anyip *interface_ip; - - // TODO: check that the prefix length is /64. - interface_ip = getinterface_ip(interface, AF_INET6); - if (!interface_ip) { - logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); - return 0; - } - - // Generate an interface ID. - config_generate_local_ipv6_subnet(&interface_ip->ip6); - - Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; - free(interface_ip); - return 1; -} - -/* function: clat_ipv6_address_from_cmdline - * parses the clat IPv6 address from the command line - * v4_addr - a string, the IPv6 address - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_cmdline(const char *v6_addr) { - if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { - logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); - return 0; - } - - return 1; -} - /* function: configure_clat_ipv6_address * picks the clat IPv6 address and configures packet translation to use it. * tunnel - tun device data @@ -317,13 +243,10 @@ static int clat_ipv6_address_from_cmdline(const char *v6_addr) { */ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *v6_addr) { - int ret; - if (v6_addr) { - ret = clat_ipv6_address_from_cmdline(v6_addr); - } else { - ret = clat_ipv6_address_from_interface(interface); + if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + return 0; } - if (!ret) return 0; char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); diff --git a/clatd.conf b/clatd.conf index 1025fb25e6..36fcafd64c 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1,13 +1 @@ -# Host IID to use as the source of CLAT traffic. -# This is a /128 taken out of the /64 on the parent interface. -# A host IID of :: means to generate a checksum-neutral, random IID. -ipv6_host_id :: - -# IPv4 address configuration to use when selecting a host address. The first -# clat daemon started will use the address specified by ipv4_local_subnet. If -# more than one daemon is run at the same time, subsequent daemons will use -# other addresses in the prefix of length ipv4_local prefixlen that contains -# ipv4_local_subnet. The default is to use the IANA-assigned range 192.0.0.0/29, -# which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). -ipv4_local_subnet 192.0.0.4 -ipv4_local_prefixlen 29 +temporary_placeholder 0 diff --git a/clatd_test.cpp b/clatd_test.cpp index 5de7b63429..8f10e32898 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -627,183 +627,12 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -int count_onebits(const void *data, size_t size) { - int onebits = 0; - for (size_t pos = 0; pos < size; pos++) { - uint8_t *byte = ((uint8_t *)data) + pos; - for (int shift = 0; shift < 8; shift++) { - onebits += (*byte >> shift) & 1; - } - } - return onebits; -} - -TEST_F(ClatdTest, TestCountOnebits) { - uint64_t i; - i = 1; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i <<= 61; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i |= ((uint64_t)1 << 33); - ASSERT_EQ(2, count_onebits(&i, sizeof(i))); - i = 0xf1000202020000f0; - ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i))); -} - -TEST_F(ClatdTest, TestGenIIDConfigured) { - struct in6_addr myaddr, expected; - Global_Clatd_Config.use_dynamic_iid = 0; - ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr)); - config_generate_local_ipv6_subnet(&myaddr); - expect_ipv6_addr_equal(&expected, &myaddr); - - Global_Clatd_Config.use_dynamic_iid = 1; - config_generate_local_ipv6_subnet(&myaddr); - EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&expected, &myaddr)); -} - -TEST_F(ClatdTest, TestGenIIDRandom) { - struct in6_addr interface_ipv6; - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &interface_ipv6)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - - // Generate a boatload of random IIDs. - int onebits = 0; - uint64_t prev_iid = 0; - for (int i = 0; i < 100000; i++) { - struct in6_addr myaddr = interface_ipv6; - - config_generate_local_ipv6_subnet(&myaddr); - - // Check the generated IP address is in the same prefix as the interface IPv6 address. - EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr)); - - // Check that consecutive IIDs are not the same. - uint64_t iid = *(uint64_t *)(&myaddr.s6_addr[8]); - ASSERT_TRUE(iid != prev_iid) - << "Two consecutive random IIDs are the same: " - << std::showbase << std::hex - << iid << "\n"; - prev_iid = iid; - - // Check that the IID is checksum-neutral with the NAT64 prefix and the - // local prefix. - struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; - struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet; - - uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr))); - uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, &myaddr, sizeof(myaddr))); - - if (c1 != c2) { - char myaddr_str[INET6_ADDRSTRLEN], plat_str[INET6_ADDRSTRLEN], ipv4_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &myaddr, myaddr_str, sizeof(myaddr_str)); - inet_ntop(AF_INET6, plat_subnet, plat_str, sizeof(plat_str)); - inet_ntop(AF_INET, ipv4addr, ipv4_str, sizeof(ipv4_str)); - FAIL() - << "Bad IID: " << myaddr_str - << " not checksum-neutral with " << ipv4_str << " and " << plat_str - << std::showbase << std::hex - << "\n IPv4 checksum: " << c1 - << "\n IPv6 checksum: " << c2 - << "\n"; - } - - // Check that IIDs are roughly random and use all the bits by counting the - // total number of bits set to 1 in a random sample of 100000 generated IIDs. - onebits += count_onebits(&iid, sizeof(iid)); - } - EXPECT_LE(3190000, onebits); - EXPECT_GE(3210000, onebits); -} - -extern "C" addr_free_func config_is_ipv4_address_free; -int never_free(in_addr_t /* addr */) { return 0; } -int always_free(in_addr_t /* addr */) { return 1; } -int only2_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 2; } -int over6_free(in_addr_t addr) { return (ntohl(addr) & 0xff) >= 6; } -int only10_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 10; } - -TEST_F(ClatdTest, SelectIPv4Address) { - struct in_addr addr; - - inet_pton(AF_INET, kIPv4LocalAddr, &addr); - - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - - // If no addresses are free, return INADDR_NONE. - config_is_ipv4_address_free = never_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 16)); - - // If the configured address is free, pick that. But a prefix that's too big is invalid. - config_is_ipv4_address_free = always_free; - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 20)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 15)); - - // A prefix length of 32 works, but anything above it is invalid. - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 32)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 33)); - - // If another address is free, pick it. - config_is_ipv4_address_free = over6_free; - EXPECT_EQ(inet_addr("192.0.0.6"), config_select_ipv4_address(&addr, 29)); - - // Check that we wrap around to addresses that are lower than the first address. - config_is_ipv4_address_free = only2_free; - EXPECT_EQ(inet_addr("192.0.0.2"), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 30)); - - // If a free address exists outside the prefix, we don't pick it. - config_is_ipv4_address_free = only10_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr("192.0.0.10"), config_select_ipv4_address(&addr, 24)); - - // Now try using the real function which sees if IP addresses are free using bind(). - // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - addr.s_addr = inet_addr("8.8.8.8"); - EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29)); - - addr.s_addr = inet_addr("127.0.0.1"); - EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); -} - TEST_F(ClatdTest, DetectMtu) { // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); } -TEST_F(ClatdTest, ConfigureTunIp) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - - Global_Clatd_Config.ipv4_local_prefixlen = 29; - - // Create an interface for configure_tun_ip to configure and bring up. - TunInterface v4Iface; - ASSERT_EQ(0, v4Iface.init()); - struct tun_data tunnel = makeTunData(); - strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - - configure_tun_ip(&tunnel, nullptr /* v4_addr */, 1472); - EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr); - - union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); - EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr); - free(ip); - - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - v4Iface.destroy(); -} - TEST_F(ClatdTest, ConfigureTunIpManual) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - Global_Clatd_Config.ipv4_local_prefixlen = 29; // Create an interface for configure_tun_ip to configure and bring up. @@ -820,7 +649,6 @@ TEST_F(ClatdTest, ConfigureTunIpManual) { EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); free(ip); - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; v4Iface.destroy(); } @@ -986,6 +814,29 @@ TEST_F(ClatdTest, Fragmentation) { ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); } +// picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix +void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, + struct in6_addr *plat_subnet) { + // Fill last 8 bytes of IPv6 address with random bits. + arc4random_buf(&myaddr->s6_addr[8], 8); + + // Make the IID checksum-neutral. That is, make it so that: + // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) + // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): + // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) + // Do this by adjusting the two bytes in the middle of the IID. + + uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; + + uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); + uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + + ip_checksum_add(0, myaddr, sizeof(*myaddr)); + + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + myaddr->s6_addr[11] = delta >> 8; + myaddr->s6_addr[12] = delta & 0xff; +} + void check_translate_checksum_neutral(const uint8_t *original, size_t original_len, size_t expected_len, const char *msg) { uint8_t translated[MAXMTU]; @@ -1006,7 +857,10 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { Global_Clatd_Config.ipv6_host_id = in6addr_any; ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); - config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); + + gen_random_iid(&Global_Clatd_Config.ipv6_local_subnet, &Global_Clatd_Config.ipv4_local_subnet, + &Global_Clatd_Config.plat_subnet); + ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); @@ -1079,7 +933,13 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), }; const char *ifname = sTun.name().c_str(); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr)); + + in6_addr myaddr = sTun.srcAddr(); + gen_random_iid(&myaddr, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet); + char addrstr[INET6_ADDRSTRLEN]; + ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); + + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, addrstr)); EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); diff --git a/config.c b/config.c index db0abc8671..d4cc9af4d2 100644 --- a/config.c +++ b/config.c @@ -157,107 +157,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } -/* function: gen_random_iid - * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix - * myaddr - IPv6 address to write to - * ipv4_local_subnet - clat IPv4 address - * plat_subnet - NAT64 prefix - */ -void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, - struct in6_addr *plat_subnet) { - // Fill last 8 bytes of IPv6 address with random bits. - arc4random_buf(&myaddr->s6_addr[8], 8); - - // Make the IID checksum-neutral. That is, make it so that: - // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) - // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): - // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) - // Do this by adjusting the two bytes in the middle of the IID. - - uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; - - uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); - uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, myaddr, sizeof(*myaddr)); - - uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); - myaddr->s6_addr[11] = delta >> 8; - myaddr->s6_addr[12] = delta & 0xff; -} - -// Factored out to a separate function for testability. -int connect_is_ipv4_address_free(in_addr_t addr) { - int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (s == -1) { - return 0; - } - - // Attempt to connect to the address. If the connection succeeds and getsockname returns the same - // the address then the address is already assigned to the system and we can't use it. - struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = htons(53) }; - socklen_t len = sizeof(sin); - int inuse = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 && - getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) && - sin.sin_addr.s_addr == addr; - - close(s); - return !inuse; -} - -addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free; - -/* function: config_select_ipv4_address - * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order - * ip - the IP address from the configuration file - * prefixlen - the length of the prefix from which addresses may be selected. - * returns: the IPv4 address, or INADDR_NONE if no addresses were available - */ -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) { - in_addr_t chosen = INADDR_NONE; - - // Don't accept prefixes that are too large because we scan addresses one by one. - if (prefixlen < 16 || prefixlen > 32) { - return chosen; - } - - // All these are in host byte order. - in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); - in_addr_t ipv4 = ntohl(ip->s_addr); - in_addr_t first_ipv4 = ipv4; - in_addr_t prefix = ipv4 & mask; - - // Pick the first IPv4 address in the pool, wrapping around if necessary. - // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0. - do { - if (config_is_ipv4_address_free(htonl(ipv4))) { - chosen = htonl(ipv4); - break; - } - ipv4 = prefix | ((ipv4 + 1) & ~mask); - } while (ipv4 != first_ipv4); - - return chosen; -} - -/* function: config_generate_local_ipv6_subnet - * generates the local ipv6 subnet when given the interface ip requires config.ipv6_host_id - * interface_ip - in: interface ip, out: local ipv6 host address - */ -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { - int i; - - if (Global_Clatd_Config.use_dynamic_iid) { - /* Generate a random interface ID. */ - gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet, - &Global_Clatd_Config.plat_subnet); - } else { - /* Use the specified interface ID. */ - for (i = 2; i < 4; i++) { - interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i]; - } - } -} - /* function: read_config * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on * failure, 1 on success diff --git a/config.h b/config.h index c47fc88f54..50efeeba21 100644 --- a/config.h +++ b/config.h @@ -37,10 +37,6 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface); -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); -typedef int (*addr_free_func)(in_addr_t addr); - #endif /* __CONFIG_H__ */ From 98864456ede004f9877e3c46f471390b1442dd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:30:33 -0700 Subject: [PATCH 185/261] cleanup - config_item_ip() and DEFAULT_IPV4_LOCAL_SUBNET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config_item_ip|DEFAULT_IPV4_LOCAL_SUBNET' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I0cb3fe5bae5f663d07a8cb63f2dd9ef9ca1a6dd8 --- config.c | 31 ------------------------------- config.h | 2 -- 2 files changed, 33 deletions(-) diff --git a/config.c b/config.c index 554db0e158..f7c97241e4 100644 --- a/config.c +++ b/config.c @@ -35,33 +35,6 @@ struct clat_config Global_Clatd_Config; -/* 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: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address @@ -95,10 +68,6 @@ int read_config(const char *file, const char *uplink_interface) { Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); if (!Global_Clatd_Config.default_pdp_interface) goto failed; - if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, - &Global_Clatd_Config.ipv4_local_subnet)) - goto failed; - Global_Clatd_Config.ipv4_local_prefixlen = 29; return 1; diff --git a/config.h b/config.h index 555b9681ae..32558ca0e8 100644 --- a/config.h +++ b/config.h @@ -21,8 +21,6 @@ #include #include -#define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" - struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; From ba667df1c1f52c77c8bfefa55957c7e270b6e842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 01:41:54 -0700 Subject: [PATCH 186/261] cleanup - read_config() and ipv4_local_prefixlen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'read_config|ipv4_local_prefixlen|default_pdp_interface' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I67b70fe298f69ac51dfbc5daf4078b842fb62808 --- clatd.c | 10 +++------- clatd.conf | 1 - clatd_test.cpp | 4 +--- config.c | 34 ---------------------------------- config.h | 4 +--- 5 files changed, 5 insertions(+), 48 deletions(-) diff --git a/clatd.c b/clatd.c index 50693f5358..edae7618f2 100644 --- a/clatd.c +++ b/clatd.c @@ -98,7 +98,7 @@ int configure_packet_socket(int sock) { struct sockaddr_ll sll = { .sll_family = AF_PACKET, .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), + .sll_ifindex = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. }; if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { @@ -318,11 +318,7 @@ int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_ */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { - if (!read_config("/system/etc/clatd.conf", uplink_interface)) { - logmsg(ANDROID_LOG_FATAL, "read_config failed"); - exit(1); - } - + Global_Clatd_Config.native_ipv6_interface = uplink_interface; if (!plat_prefix || 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); exit(1); @@ -430,7 +426,7 @@ void event_loop(struct tun_data *tunnel) { time_t now = time(NULL); if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { - if (ipv6_address_changed(Global_Clatd_Config.default_pdp_interface)) { + if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) { break; } } diff --git a/clatd.conf b/clatd.conf index 36fcafd64c..e69de29bb2 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1 +0,0 @@ -temporary_placeholder 0 diff --git a/clatd_test.cpp b/clatd_test.cpp index e24b03665b..514aaa8017 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -585,7 +585,7 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); - Global_Clatd_Config.default_pdp_interface = const_cast(sTun.name().c_str()); + Global_Clatd_Config.native_ipv6_interface = const_cast(sTun.name().c_str()); } // Static because setting up the tun interface takes about 40ms. @@ -631,8 +631,6 @@ TEST_F(ClatdTest, DetectMtu) { } TEST_F(ClatdTest, ConfigureTunIpManual) { - Global_Clatd_Config.ipv4_local_prefixlen = 29; - // Create an interface for configure_tun_ip to configure and bring up. TunInterface v4Iface; ASSERT_EQ(0, v4Iface.init()); diff --git a/config.c b/config.c index f7c97241e4..d83b958fcb 100644 --- a/config.c +++ b/config.c @@ -42,37 +42,3 @@ struct clat_config Global_Clatd_Config; * returns: 0 if the subnets are different, 1 if they are the same. */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } - -/* function: 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 - */ -int read_config(const char *file, const char *uplink_interface) { - cnode *root = config_node("", ""); - - if (!root) { - logmsg(ANDROID_LOG_FATAL, "out of memory"); - return 0; - } - - memset(&Global_Clatd_Config, '\0', sizeof(Global_Clatd_Config)); - - config_load_file(root, file); - if (root->first_child == NULL) { - logmsg(ANDROID_LOG_FATAL, "Could not read config file %s", file); - goto failed; - } - - Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); - if (!Global_Clatd_Config.default_pdp_interface) goto failed; - - Global_Clatd_Config.ipv4_local_prefixlen = 29; - - return 1; - -failed: - free(root); - return 0; -} diff --git a/config.h b/config.h index 32558ca0e8..0152df05e4 100644 --- a/config.h +++ b/config.h @@ -24,14 +24,12 @@ struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; - int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; - char *default_pdp_interface; + const char *native_ipv6_interface; }; extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); #endif /* __CONFIG_H__ */ From 069b0c7427407ba7fdeefc34b10948c2fde07d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 03:45:34 -0700 Subject: [PATCH 187/261] cleanup - remove clatd.conf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep clatd.conf comes up empty (this needs changes in other projects though) Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I0d8a24f80c07ca95eb0aae4f4f67ac291ac78c1f --- Android.bp | 6 ------ clatd.conf | 0 2 files changed, 6 deletions(-) delete mode 100644 clatd.conf diff --git a/Android.bp b/Android.bp index bff97d3338..ac9606be42 100644 --- a/Android.bp +++ b/Android.bp @@ -66,12 +66,6 @@ cc_binary { ], } -// The configuration file. -prebuilt_etc { - name: "clatd.conf", - src: "clatd.conf", -} - // Unit tests. cc_test { name: "clatd_test", diff --git a/clatd.conf b/clatd.conf deleted file mode 100644 index e69de29bb2..0000000000 From 65de290172b41e1b349865e1f61ce0f9acefee9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:02:37 -0700 Subject: [PATCH 188/261] cleanup - remove send_tun() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep send_tun comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: If81ca18bf5a33549b532de0d6406e32f4560c93a --- translate.c | 2 +- tun.h | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/translate.c b/translate.c index 16327532cb..f7597268ef 100644 --- a/translate.c +++ b/translate.c @@ -524,7 +524,7 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets if (iov_len > 0) { fill_tun_header(&tun_targ, ETH_P_IP); out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ); - send_tun(fd, out, iov_len); + writev(fd, out, iov_len); } } } diff --git a/tun.h b/tun.h index eaeebc689c..1dc9136ba3 100644 --- a/tun.h +++ b/tun.h @@ -32,15 +32,4 @@ struct tun_data { struct packet_ring ring; }; -/* function: send_tun - * sends a clat_packet to a tun interface - * fd - the tun filedescriptor - * out - the packet to send - * iov_len - the number of entries in the clat_packet - * returns: number of bytes read on success, -1 on failure - */ -static inline int send_tun(int fd, clat_packet out, int iov_len) { - return writev(fd, out, iov_len); -} - #endif From 6fafed183b398fb5158158d7c0db2fc879bf0a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 14:31:55 -0700 Subject: [PATCH 189/261] cleanup - remove tun.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Longer term we should probably just merge the clat_config and tun_data structs, but not just yet. Test: git grep '(^|[^_])tun[.]h' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I7c3a986584aa3d02519545fb928457b61247ed14 --- clatd.c | 1 - clatd_test.cpp | 1 - config.h | 8 ++++++++ main.c | 1 - ring.c | 2 +- translate.c | 1 - tun.h | 35 ----------------------------------- 7 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 tun.h diff --git a/clatd.c b/clatd.c index edae7618f2..d29b7ed7f0 100644 --- a/clatd.c +++ b/clatd.c @@ -50,7 +50,6 @@ #include "ring.h" #include "setif.h" #include "translate.h" -#include "tun.h" /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 diff --git a/clatd_test.cpp b/clatd_test.cpp index 514aaa8017..c16a4dd783 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -34,7 +34,6 @@ extern "C" { #include "getaddr.h" #include "netutils/checksum.h" #include "translate.h" -#include "tun.h" } // For convenience. diff --git a/config.h b/config.h index 0152df05e4..95cdefa7ea 100644 --- a/config.h +++ b/config.h @@ -21,6 +21,14 @@ #include #include +#include "ring.h" + +struct tun_data { + char device4[IFNAMSIZ]; + int read_fd6, write_fd6, fd4; + struct packet_ring ring; +}; + struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; diff --git a/main.c b/main.c index c4834d9ce4..683b50752d 100644 --- a/main.c +++ b/main.c @@ -32,7 +32,6 @@ #include "config.h" #include "logging.h" #include "setif.h" -#include "tun.h" #define DEVICEPREFIX "v4-" diff --git a/ring.c b/ring.c index 96a50ca677..7626c6de45 100644 --- a/ring.c +++ b/ring.c @@ -24,10 +24,10 @@ #include #include +#include "config.h" #include "logging.h" #include "ring.h" #include "translate.h" -#include "tun.h" int ring_create(struct tun_data *tunnel) { // Will eventually be bound to htons(ETH_P_IPV6) protocol, diff --git a/translate.c b/translate.c index f7597268ef..728acc3045 100644 --- a/translate.c +++ b/translate.c @@ -26,7 +26,6 @@ #include "icmp.h" #include "logging.h" #include "translate.h" -#include "tun.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos diff --git a/tun.h b/tun.h deleted file mode 100644 index 1dc9136ba3..0000000000 --- a/tun.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * tun.h - tun device functions - */ -#ifndef __TUN_H__ -#define __TUN_H__ - -#include -#include -#include -#include - -#include "common.h" -#include "ring.h" - -struct tun_data { - char device4[IFNAMSIZ]; - int read_fd6, write_fd6, fd4; - struct packet_ring ring; -}; - -#endif From 5ce6cdae6d6b0c166f155a96903a6ba01f816327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 14:39:33 -0700 Subject: [PATCH 190/261] cleanup - remove config.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config[.]c' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I8500881852f1255a69b49eba6354777883fe438f --- Android.bp | 1 - clatd.c | 2 ++ config.c | 44 -------------------------------------------- config.h | 10 +++++++++- 4 files changed, 11 insertions(+), 46 deletions(-) delete mode 100644 config.c diff --git a/Android.bp b/Android.bp index ac9606be42..ac24a003fe 100644 --- a/Android.bp +++ b/Android.bp @@ -20,7 +20,6 @@ cc_defaults { filegroup { name: "clatd_common", srcs: [ - "config.c", "clatd.c", "dump.c", "getaddr.c", diff --git a/clatd.c b/clatd.c index d29b7ed7f0..70632156e2 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,8 @@ #include "setif.h" #include "translate.h" +struct clat_config Global_Clatd_Config; + /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 diff --git a/config.c b/config.c deleted file mode 100644 index d83b958fcb..0000000000 --- a/config.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * config.c - configuration settings - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "clatd.h" -#include "config.h" -#include "getaddr.h" -#include "logging.h" - -struct clat_config Global_Clatd_Config; - -/* function: ipv6_prefix_equal - * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. - * a1 - first address - * a2 - second address - * returns: 0 if the subnets are different, 1 if they are the same. - */ -int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } diff --git a/config.h b/config.h index 95cdefa7ea..1ba68504dc 100644 --- a/config.h +++ b/config.h @@ -38,6 +38,14 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); +/* function: ipv6_prefix_equal + * compares the /64 prefixes of two ipv6 addresses. + * a1 - first address + * a2 - second address + * returns: 0 if the subnets are different, 1 if they are the same. + */ +static inline int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { + return !memcmp(a1, a2, 8); +} #endif /* __CONFIG_H__ */ From e6bf18509a15e43d2c532504b585b07e8fb656c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:56:34 +0000 Subject: [PATCH 191/261] cleanup - dump_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep dump_config comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322220 Merged-In: I781bbb0523211349893d947a500edf0eb12dedfd Change-Id: I781bbb0523211349893d947a500edf0eb12dedfd --- config.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/config.c b/config.c index d4cc9af4d2..1c380dcc79 100644 --- a/config.c +++ b/config.c @@ -210,22 +210,3 @@ failed: free(root); return 0; } - -/* function; dump_config - * prints the current config - */ -void dump_config() { - char charbuffer[INET6_ADDRSTRLEN]; - - logmsg( - ANDROID_LOG_DEBUG, "ipv6_local_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg( - ANDROID_LOG_DEBUG, "ipv4_local_subnet = %s", - inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); - logmsg(ANDROID_LOG_DEBUG, "plat_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "default_pdp_interface = %s", - Global_Clatd_Config.default_pdp_interface); -} From 2f45839780ca4d6e7cb4ef308edf72cf5564dcd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:56:35 +0000 Subject: [PATCH 192/261] cleanup - ipv6_host_id and is_dynamic_iid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'ipv6_host_id|is_dynamic_iid' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322221 Merged-In: Ie3ed4583a95f15165a05412a556bae99dc07706b Change-Id: Ie3ed4583a95f15165a05412a556bae99dc07706b --- clatd_test.cpp | 3 --- config.c | 14 -------------- config.h | 2 -- 3 files changed, 19 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 8f10e32898..e24b03665b 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -585,8 +585,6 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - Global_Clatd_Config.use_dynamic_iid = 1; Global_Clatd_Config.default_pdp_interface = const_cast(sTun.name().c_str()); } @@ -854,7 +852,6 @@ void check_translate_checksum_neutral(const uint8_t *original, size_t original_l TEST_F(ClatdTest, TranslateChecksumNeutral) { // Generate a random clat IPv6 address and check that translation is checksum-neutral. - Global_Clatd_Config.ipv6_host_id = in6addr_any; ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); diff --git a/config.c b/config.c index 1c380dcc79..eea12f78ca 100644 --- a/config.c +++ b/config.c @@ -165,7 +165,6 @@ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp */ int read_config(const char *file, const char *uplink_interface) { cnode *root = config_node("", ""); - unsigned flags; if (!root) { logmsg(ANDROID_LOG_FATAL, "out of memory"); @@ -191,19 +190,6 @@ int read_config(const char *file, const char *uplink_interface) { &Global_Clatd_Config.ipv4_local_prefixlen)) goto failed; - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; - - /* In order to prevent multiple devices attempting to use the same clat address, never use a - statically-configured interface ID on a broadcast interface such as wifi. */ - if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { - ifc_init(); - ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags); - ifc_close(); - Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0; - } else { - Global_Clatd_Config.use_dynamic_iid = 1; - } - return 1; failed: diff --git a/config.h b/config.h index 50efeeba21..9231906e1a 100644 --- a/config.h +++ b/config.h @@ -26,12 +26,10 @@ struct clat_config { struct in6_addr ipv6_local_subnet; - struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; char *default_pdp_interface; - int use_dynamic_iid; }; extern struct clat_config Global_Clatd_Config; From 2c286b07025be273e99f8d8c2aa05ec35f0cc90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:56:37 +0000 Subject: [PATCH 193/261] cleanup - config_item_str() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep config_item_str comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322222 Merged-In: I1a8f21e55534457718ea58cdab15266b9f232fd3 Change-Id: I1a8f21e55534457718ea58cdab15266b9f232fd3 --- config.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/config.c b/config.c index eea12f78ca..4be61a7652 100644 --- a/config.c +++ b/config.c @@ -35,23 +35,6 @@ 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 From 6be24d8214cf00faa279deba4e690058d84f6458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:56:40 +0000 Subject: [PATCH 194/261] cleanup - config_item_int16() & DEFAULT_IPV4_LOCAL_PREFIXLEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config_item_int16|DEFAULT_IPV4_LOCAL_PREFIXLEN' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322223 Merged-In: I71716cdcba382bc5fb1efa5000189f63a57015c9 Change-Id: I71716cdcba382bc5fb1efa5000189f63a57015c9 --- config.c | 47 +---------------------------------------------- config.h | 1 - 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/config.c b/config.c index 4be61a7652..bcf77bad7c 100644 --- a/config.c +++ b/config.c @@ -35,49 +35,6 @@ struct clat_config Global_Clatd_Config; -/* 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 @@ -169,9 +126,7 @@ int read_config(const char *file, const char *uplink_interface) { &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if (!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, - &Global_Clatd_Config.ipv4_local_prefixlen)) - goto failed; + Global_Clatd_Config.ipv4_local_prefixlen = 29; return 1; diff --git a/config.h b/config.h index 9231906e1a..555b9681ae 100644 --- a/config.h +++ b/config.h @@ -22,7 +22,6 @@ #include #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" -#define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" struct clat_config { struct in6_addr ipv6_local_subnet; From 533e31f4ea4853cd512d5c128838289dbef4758a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 12:56:42 +0000 Subject: [PATCH 195/261] cleanup - config_item_ip6() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep config_item_ip6 comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322224 Merged-In: I0338f7ac29fdb0de56bd4a500e2a827cab2c47f7 Change-Id: I0338f7ac29fdb0de56bd4a500e2a827cab2c47f7 --- config.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/config.c b/config.c index bcf77bad7c..554db0e158 100644 --- a/config.c +++ b/config.c @@ -62,33 +62,6 @@ struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *d 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: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address From 36f4665ab429bcb2a698cb49acf541f202747730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Jun 2020 02:31:44 +0000 Subject: [PATCH 196/261] cleanup - config_item_ip() and DEFAULT_IPV4_LOCAL_SUBNET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config_item_ip|DEFAULT_IPV4_LOCAL_SUBNET' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322225 Merged-In: I0cb3fe5bae5f663d07a8cb63f2dd9ef9ca1a6dd8 Change-Id: I0cb3fe5bae5f663d07a8cb63f2dd9ef9ca1a6dd8 --- config.c | 31 ------------------------------- config.h | 2 -- 2 files changed, 33 deletions(-) diff --git a/config.c b/config.c index 554db0e158..f7c97241e4 100644 --- a/config.c +++ b/config.c @@ -35,33 +35,6 @@ struct clat_config Global_Clatd_Config; -/* 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: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address @@ -95,10 +68,6 @@ int read_config(const char *file, const char *uplink_interface) { Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); if (!Global_Clatd_Config.default_pdp_interface) goto failed; - if (!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, - &Global_Clatd_Config.ipv4_local_subnet)) - goto failed; - Global_Clatd_Config.ipv4_local_prefixlen = 29; return 1; diff --git a/config.h b/config.h index 555b9681ae..32558ca0e8 100644 --- a/config.h +++ b/config.h @@ -21,8 +21,6 @@ #include #include -#define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" - struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; From 8452f3f65229a25fb99da23019e732f4b169b566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Jun 2020 02:31:51 +0000 Subject: [PATCH 197/261] cleanup - read_config() and ipv4_local_prefixlen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'read_config|ipv4_local_prefixlen|default_pdp_interface' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322226 Merged-In: I67b70fe298f69ac51dfbc5daf4078b842fb62808 Change-Id: I67b70fe298f69ac51dfbc5daf4078b842fb62808 --- clatd.c | 10 +++------- clatd.conf | 1 - clatd_test.cpp | 4 +--- config.c | 34 ---------------------------------- config.h | 4 +--- 5 files changed, 5 insertions(+), 48 deletions(-) diff --git a/clatd.c b/clatd.c index 50693f5358..edae7618f2 100644 --- a/clatd.c +++ b/clatd.c @@ -98,7 +98,7 @@ int configure_packet_socket(int sock) { struct sockaddr_ll sll = { .sll_family = AF_PACKET, .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex(Global_Clatd_Config.default_pdp_interface), + .sll_ifindex = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. }; if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { @@ -318,11 +318,7 @@ int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_ */ void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { - if (!read_config("/system/etc/clatd.conf", uplink_interface)) { - logmsg(ANDROID_LOG_FATAL, "read_config failed"); - exit(1); - } - + Global_Clatd_Config.native_ipv6_interface = uplink_interface; if (!plat_prefix || 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); exit(1); @@ -430,7 +426,7 @@ void event_loop(struct tun_data *tunnel) { time_t now = time(NULL); if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { - if (ipv6_address_changed(Global_Clatd_Config.default_pdp_interface)) { + if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) { break; } } diff --git a/clatd.conf b/clatd.conf index 36fcafd64c..e69de29bb2 100644 --- a/clatd.conf +++ b/clatd.conf @@ -1 +0,0 @@ -temporary_placeholder 0 diff --git a/clatd_test.cpp b/clatd_test.cpp index e24b03665b..514aaa8017 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -585,7 +585,7 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); - Global_Clatd_Config.default_pdp_interface = const_cast(sTun.name().c_str()); + Global_Clatd_Config.native_ipv6_interface = const_cast(sTun.name().c_str()); } // Static because setting up the tun interface takes about 40ms. @@ -631,8 +631,6 @@ TEST_F(ClatdTest, DetectMtu) { } TEST_F(ClatdTest, ConfigureTunIpManual) { - Global_Clatd_Config.ipv4_local_prefixlen = 29; - // Create an interface for configure_tun_ip to configure and bring up. TunInterface v4Iface; ASSERT_EQ(0, v4Iface.init()); diff --git a/config.c b/config.c index f7c97241e4..d83b958fcb 100644 --- a/config.c +++ b/config.c @@ -42,37 +42,3 @@ struct clat_config Global_Clatd_Config; * returns: 0 if the subnets are different, 1 if they are the same. */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } - -/* function: 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 - */ -int read_config(const char *file, const char *uplink_interface) { - cnode *root = config_node("", ""); - - if (!root) { - logmsg(ANDROID_LOG_FATAL, "out of memory"); - return 0; - } - - memset(&Global_Clatd_Config, '\0', sizeof(Global_Clatd_Config)); - - config_load_file(root, file); - if (root->first_child == NULL) { - logmsg(ANDROID_LOG_FATAL, "Could not read config file %s", file); - goto failed; - } - - Global_Clatd_Config.default_pdp_interface = strdup(uplink_interface); - if (!Global_Clatd_Config.default_pdp_interface) goto failed; - - Global_Clatd_Config.ipv4_local_prefixlen = 29; - - return 1; - -failed: - free(root); - return 0; -} diff --git a/config.h b/config.h index 32558ca0e8..0152df05e4 100644 --- a/config.h +++ b/config.h @@ -24,14 +24,12 @@ struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; - int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; - char *default_pdp_interface; + const char *native_ipv6_interface; }; extern struct clat_config Global_Clatd_Config; -int read_config(const char *file, const char *uplink_interface); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); #endif /* __CONFIG_H__ */ From 73a5f2036069d7295abc55d60829ffd614362fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Jun 2020 08:08:20 +0000 Subject: [PATCH 198/261] cleanup - remove send_tun() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep send_tun comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1322978 Merged-In: If81ca18bf5a33549b532de0d6406e32f4560c93a Change-Id: If81ca18bf5a33549b532de0d6406e32f4560c93a --- translate.c | 2 +- tun.h | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/translate.c b/translate.c index 16327532cb..f7597268ef 100644 --- a/translate.c +++ b/translate.c @@ -524,7 +524,7 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets if (iov_len > 0) { fill_tun_header(&tun_targ, ETH_P_IP); out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ); - send_tun(fd, out, iov_len); + writev(fd, out, iov_len); } } } diff --git a/tun.h b/tun.h index eaeebc689c..1dc9136ba3 100644 --- a/tun.h +++ b/tun.h @@ -32,15 +32,4 @@ struct tun_data { struct packet_ring ring; }; -/* function: send_tun - * sends a clat_packet to a tun interface - * fd - the tun filedescriptor - * out - the packet to send - * iov_len - the number of entries in the clat_packet - * returns: number of bytes read on success, -1 on failure - */ -static inline int send_tun(int fd, clat_packet out, int iov_len) { - return writev(fd, out, iov_len); -} - #endif From 551367e946fce14a5985ddeaa607f1f44c3dadb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Jun 2020 08:08:21 +0000 Subject: [PATCH 199/261] cleanup - remove tun.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Longer term we should probably just merge the clat_config and tun_data structs, but not just yet. Test: git grep '(^|[^_])tun[.]h' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1323171 Merged-In: I7c3a986584aa3d02519545fb928457b61247ed14 Change-Id: I7c3a986584aa3d02519545fb928457b61247ed14 --- clatd.c | 1 - clatd_test.cpp | 1 - config.h | 8 ++++++++ main.c | 1 - ring.c | 2 +- translate.c | 1 - tun.h | 35 ----------------------------------- 7 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 tun.h diff --git a/clatd.c b/clatd.c index edae7618f2..d29b7ed7f0 100644 --- a/clatd.c +++ b/clatd.c @@ -50,7 +50,6 @@ #include "ring.h" #include "setif.h" #include "translate.h" -#include "tun.h" /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 diff --git a/clatd_test.cpp b/clatd_test.cpp index 514aaa8017..c16a4dd783 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -34,7 +34,6 @@ extern "C" { #include "getaddr.h" #include "netutils/checksum.h" #include "translate.h" -#include "tun.h" } // For convenience. diff --git a/config.h b/config.h index 0152df05e4..95cdefa7ea 100644 --- a/config.h +++ b/config.h @@ -21,6 +21,14 @@ #include #include +#include "ring.h" + +struct tun_data { + char device4[IFNAMSIZ]; + int read_fd6, write_fd6, fd4; + struct packet_ring ring; +}; + struct clat_config { struct in6_addr ipv6_local_subnet; struct in_addr ipv4_local_subnet; diff --git a/main.c b/main.c index c4834d9ce4..683b50752d 100644 --- a/main.c +++ b/main.c @@ -32,7 +32,6 @@ #include "config.h" #include "logging.h" #include "setif.h" -#include "tun.h" #define DEVICEPREFIX "v4-" diff --git a/ring.c b/ring.c index 96a50ca677..7626c6de45 100644 --- a/ring.c +++ b/ring.c @@ -24,10 +24,10 @@ #include #include +#include "config.h" #include "logging.h" #include "ring.h" #include "translate.h" -#include "tun.h" int ring_create(struct tun_data *tunnel) { // Will eventually be bound to htons(ETH_P_IPV6) protocol, diff --git a/translate.c b/translate.c index f7597268ef..728acc3045 100644 --- a/translate.c +++ b/translate.c @@ -26,7 +26,6 @@ #include "icmp.h" #include "logging.h" #include "translate.h" -#include "tun.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos diff --git a/tun.h b/tun.h deleted file mode 100644 index 1dc9136ba3..0000000000 --- a/tun.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * tun.h - tun device functions - */ -#ifndef __TUN_H__ -#define __TUN_H__ - -#include -#include -#include -#include - -#include "common.h" -#include "ring.h" - -struct tun_data { - char device4[IFNAMSIZ]; - int read_fd6, write_fd6, fd4; - struct packet_ring ring; -}; - -#endif From 2d35cdbd89f59011f4340617ec2b206dc3446b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Jun 2020 08:08:49 +0000 Subject: [PATCH 200/261] cleanup - remove config.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: git grep 'config[.]c' comes up empty Bug: 144730808 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Original-Change: https://android-review.googlesource.com/1323172 Merged-In: I8500881852f1255a69b49eba6354777883fe438f Change-Id: I8500881852f1255a69b49eba6354777883fe438f --- Android.bp | 1 - clatd.c | 2 ++ config.c | 44 -------------------------------------------- config.h | 10 +++++++++- 4 files changed, 11 insertions(+), 46 deletions(-) delete mode 100644 config.c diff --git a/Android.bp b/Android.bp index bff97d3338..7c1c1df410 100644 --- a/Android.bp +++ b/Android.bp @@ -20,7 +20,6 @@ cc_defaults { filegroup { name: "clatd_common", srcs: [ - "config.c", "clatd.c", "dump.c", "getaddr.c", diff --git a/clatd.c b/clatd.c index d29b7ed7f0..70632156e2 100644 --- a/clatd.c +++ b/clatd.c @@ -51,6 +51,8 @@ #include "setif.h" #include "translate.h" +struct clat_config Global_Clatd_Config; + /* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ #define MTU_DELTA 28 diff --git a/config.c b/config.c deleted file mode 100644 index d83b958fcb..0000000000 --- a/config.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * config.c - configuration settings - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "clatd.h" -#include "config.h" -#include "getaddr.h" -#include "logging.h" - -struct clat_config Global_Clatd_Config; - -/* function: ipv6_prefix_equal - * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. - * a1 - first address - * a2 - second address - * returns: 0 if the subnets are different, 1 if they are the same. - */ -int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } diff --git a/config.h b/config.h index 95cdefa7ea..1ba68504dc 100644 --- a/config.h +++ b/config.h @@ -38,6 +38,14 @@ struct clat_config { extern struct clat_config Global_Clatd_Config; -int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); +/* function: ipv6_prefix_equal + * compares the /64 prefixes of two ipv6 addresses. + * a1 - first address + * a2 - second address + * returns: 0 if the subnets are different, 1 if they are the same. + */ +static inline int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { + return !memcmp(a1, a2, 8); +} #endif /* __CONFIG_H__ */ From e4b7da622c75b85743d81a730e474db02231b7c8 Mon Sep 17 00:00:00 2001 From: Rocco Yue Date: Wed, 2 Sep 2020 15:21:41 +0800 Subject: [PATCH 201/261] update last poll time when the interface poll frequency is reached In the IPv6-only network, when the download time exceeds 30 seconds, compared with REF, the IPv4 download peak rate of DUT begins to decay with time. The reason is: during the period from "reading ipv6 packets through packet socket to "sending the translated ipv4 packets to tun device through raw socket", clatd consumes more time than normal. And clatd spent most of it time on the function: ipv6_address_changed(). Test: builds and boots, phone call, internet under wifi and cellular data, lab FTP download test under 464xlat Bug: 167648019 Signed-off-by: Rocco Yue Change-Id: I815361b799c444a1094601e83a9b6a2d9fabf521 --- clatd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 70632156e2..422cded9ea 100644 --- a/clatd.c +++ b/clatd.c @@ -426,7 +426,8 @@ void event_loop(struct tun_data *tunnel) { } time_t now = time(NULL); - if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { + if (now >= (last_interface_poll + INTERFACE_POLL_FREQUENCY)) { + last_interface_poll = now; if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) { break; } From 276fdd023cbe79ca1cc47cdddf2b35bc10350b76 Mon Sep 17 00:00:00 2001 From: Rocco Yue Date: Thu, 3 Sep 2020 20:56:52 +0000 Subject: [PATCH 202/261] update last poll time when the interface poll frequency is reached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the IPv6-only network, when the download time exceeds 30 seconds, compared with REF, the IPv4 download peak rate of DUT begins to decay with time. The reason is: during the period from "reading ipv6 packets through packet socket to "sending the translated ipv4 packets to tun device through raw socket", clatd consumes more time than normal. And clatd spent most of it time on the function: ipv6_address_changed(). Test: builds and boots, phone call, internet under wifi and cellular data, lab FTP download test under 464xlat Bug: 167648019 Signed-off-by: Rocco Yue Signed-off-by: Maciej Żenczykowski Original change: https://android-review.googlesource.com/c/platform/external/android-clat/+/1419090 Change-Id: I815361b799c444a1094601e83a9b6a2d9fabf521 Merged-In: I815361b799c444a1094601e83a9b6a2d9fabf521 --- clatd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 70632156e2..422cded9ea 100644 --- a/clatd.c +++ b/clatd.c @@ -426,7 +426,8 @@ void event_loop(struct tun_data *tunnel) { } time_t now = time(NULL); - if (last_interface_poll < (now - INTERFACE_POLL_FREQUENCY)) { + if (now >= (last_interface_poll + INTERFACE_POLL_FREQUENCY)) { + last_interface_poll = now; if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) { break; } From df338699853d02d2a562b9a88beab6f34ced5f83 Mon Sep 17 00:00:00 2001 From: Rocco Yue Date: Mon, 19 Oct 2020 19:59:38 +0800 Subject: [PATCH 203/261] Fix some build errors when CLAT_DEBUG is set to 1 After seteing CLAT_DEBUG to 1 to enable debug logging, there were some build errors, as shown below: external/android-clat/dump.c:205:6: error: conflicting types for 'dump_tcp' external/android-clat/dump.c:214:6: error: conflicting types for 'dump_tcp6' external/android-clat/dump.c:229:30: error: invalid application of 'sizeof' to an incomplete type 'struct tun_pi' for (i = 0; i < len && i < PACKETLEN; i++) { ^~~~~~~~~ Bug: 171277941 Test: builds and boots Test: connect to ipv6-only wifi network: ping 8.8.8.8 still works Change-Id: Ifb5d84951c4832ae4e3eb8a24329df0243f6ee55 --- dump.c | 2 ++ dump.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dump.c b/dump.c index 28db13f070..7332d6c652 100644 --- a/dump.c +++ b/dump.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "clatd.h" #include "debug.h" +#include "dump.h" #include "logging.h" #if CLAT_DEBUG diff --git a/dump.h b/dump.h index c3f04777d1..6b96cd298d 100644 --- a/dump.h +++ b/dump.h @@ -31,14 +31,14 @@ void dump_icmp(struct icmphdr *icmp); void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const uint8_t *payload, size_t payload_size); void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const uint8_t *payload, - size_t payload_size, const char *options, size_t options_size); + size_t payload_size, const uint8_t *options, size_t options_size); void dump_ip6(struct ip6_hdr *header); void dump_icmp6(struct icmp6_hdr *icmp6); void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const uint8_t *payload, size_t payload_size); void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_t *payload, - size_t payload_size, const char *options, size_t options_size); + size_t payload_size, const uint8_t *options, size_t options_size); void logcat_hexdump(const char *info, const uint8_t *data, size_t len); void dump_iovec(const struct iovec *iov, int iov_len); From 8ab7e133323376d79d280ba60f2d994bfd9a9ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 3 Feb 2021 17:15:41 -0800 Subject: [PATCH 204/261] clatd: move stop_loop() into main.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New clang-tidy gives more warnings: .../external/android-clat/main.c:136:23: error: 'stop_loop' may not be asynchronous-safe; calling it from a signal handler may be dangerous [cert-sig30-c,-warnings-as-errors] if (signal(SIGTERM, stop_loop) == SIG_ERR) { ^ .../external/android-clat/main.c:136:7: note: signal handler registered here if (signal(SIGTERM, stop_loop) == SIG_ERR) { ^ external/android-clat/clatd.h:38:1: note: handler function declared here void stop_loop(); ^ Bug: 179304856 Test: builds, atest, TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I06bad709df2ddded97829833f5e86d72ac282fa3 --- clatd.c | 5 ----- clatd.h | 4 +++- main.c | 5 +++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clatd.c b/clatd.c index 422cded9ea..3ffe934749 100644 --- a/clatd.c +++ b/clatd.c @@ -58,11 +58,6 @@ struct clat_config Global_Clatd_Config; volatile sig_atomic_t running = 1; -/* function: stop_loop - * signal handler: stop the event loop - */ -void stop_loop() { running = 0; } - /* function: configure_packet_socket * Binds the packet socket and attaches the receive filter to it. * sock - the socket to configure diff --git a/clatd.h b/clatd.h index a3f932651a..899458c723 100644 --- a/clatd.h +++ b/clatd.h @@ -18,6 +18,7 @@ #ifndef __CLATD_H__ #define __CLATD_H__ +#include #include #include @@ -35,7 +36,8 @@ struct tun_data; // how frequently (in seconds) to poll for an address change while there is no traffic #define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 -void stop_loop(); +extern volatile sig_atomic_t running; + void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_but_keep_caps(); diff --git a/main.c b/main.c index 683b50752d..bfcad48e06 100644 --- a/main.c +++ b/main.c @@ -35,6 +35,11 @@ #define DEVICEPREFIX "v4-" +/* function: stop_loop + * signal handler: stop the event loop + */ +static void stop_loop() { running = 0; }; + /* function: print_help * in case the user is running this on the command line */ From 370808d2698e94f0361c1e18626cd2c729390d48 Mon Sep 17 00:00:00 2001 From: Bob Badour Date: Fri, 12 Feb 2021 14:17:41 -0800 Subject: [PATCH 205/261] [LSC] Add LOCAL_LICENSE_KINDS to external/android-clat Added SPDX-license-identifier-Apache-2.0 to: Android.bp Bug: 68860345 Bug: 151177513 Bug: 151953481 Test: m all Exempt-From-Owner-Approval: janitorial work Change-Id: Ib768b2a4d3461e352c0f8577a0d7cb43eb5e3d89 --- Android.bp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Android.bp b/Android.bp index ac24a003fe..adc36e7b1e 100644 --- a/Android.bp +++ b/Android.bp @@ -1,3 +1,26 @@ +package { + default_applicable_licenses: ["external_android-clat_license"], +} + +// Added automatically by a large-scale-change +// +// large-scale-change included anything that looked like it might be a license +// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. +// +// Please consider removing redundant or irrelevant files from 'license_text:'. +// See: http://go/android-license-faq +license { + name: "external_android-clat_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "LICENSE", + "NOTICE", + ], +} + cc_defaults { name: "clatd_defaults", From 929287213dc673ef870eb194423872315e38695e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 18 Jan 2021 17:40:34 +0900 Subject: [PATCH 206/261] Copy the 464xlat vendor property to an AOSP property. Test: manual Bug: 182333299 Change-Id: I88d570911d4afd0c4123ca7f50c6e7cf3b40b42b --- Android.bp | 4 ++++ vendor-464xlat.rc | 15 +++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 vendor-464xlat.rc diff --git a/Android.bp b/Android.bp index ac24a003fe..4ab797f8ce 100644 --- a/Android.bp +++ b/Android.bp @@ -63,6 +63,10 @@ cc_binary { tidy_flags: [ "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", ], + // Actually not required for clatd itself. See comments in the rc file. + init_rc: [ + "vendor-464xlat.rc", + ], } // Unit tests. diff --git a/vendor-464xlat.rc b/vendor-464xlat.rc new file mode 100644 index 0000000000..609531d3f9 --- /dev/null +++ b/vendor-464xlat.rc @@ -0,0 +1,15 @@ +# Certain vendors disable 464xlat by setting a vendor property. +# The connectivity code in the Tethering APEX needs to disable +# 464xlat when the property is set, but it is only allowed to +# access non-vendor system properties. So copy the property to +# a property available to system APIs in android.sysprop. +# +# Arguably this script should live close to the code that uses +# it, but scrips in APEXes are not allowed to use "on property". +# So put it here close to clatd, which is at least related to +# 464xlat. +on property:persist.vendor.net.doxlat=true + setprop net.464xlat.cellular.enabled true + +on property:persist.vendor.net.doxlat=false + setprop net.464xlat.cellular.enabled false From 05b0541b73590cd6a4fd85bd5fe422c573f9ffbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 1 Apr 2021 05:06:14 -0700 Subject: [PATCH 207/261] clatd - wait for SIGTERM before terminating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example log from tests: ... 04-01 04:12:22.246 4699 4699 I clatd : Starting clat version 1.4 on netdd54bd mark=0xfffdd plat=2001:db8:cafe:f00d:1:2:: v4=192.0.0.4 v6=2001:db8:1:2:841b:2681:290a:d5d8 04-01 04:12:22.247 4699 4699 I clatd : Using ring buffer with 656 frames (1048576 bytes) at 0xe9883000 04-01 04:12:22.248 4699 4699 W clatd : ipv4 mtu is 1472 04-01 04:12:22.248 4699 4699 I clatd : Using IPv4 address 192.0.0.4 on v4-netdd54bd 04-01 04:12:22.268 322 322 E ClatdController: clatd pid=4699 already started on netdd54bd 04-01 04:12:22.270 4699 4699 I clatd : Using IPv6 address 2001:db8:1:2:841b:2681:290a:d5d8 on netdd54bd 04-01 04:12:22.404 4699 4699 I clatd : Shutting down clat on netdd54bd 04-01 04:12:22.427 4699 4699 I clatd : Clatd on netdd54bd already received SIGTERM ... Bug: 181601458 Test: atest, TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: If6458fb4fd1d611992774e9ae5ec324162ab0629 --- main.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/main.c b/main.c index bfcad48e06..52e22acbc9 100644 --- a/main.c +++ b/main.c @@ -148,5 +148,16 @@ int main(int argc, char **argv) { logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); + close(tunnel.write_fd6); + close(tunnel.read_fd6); + close(tunnel.fd4); + + if (running) { + logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface); + while (running) sleep(60); + logmsg(ANDROID_LOG_INFO, "Clatd on %s received SIGTERM", uplink_interface); + } else { + logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface); + } return 0; } From afe05e1fdf9cc176617438c87f20c7cbb6fe90b9 Mon Sep 17 00:00:00 2001 From: Evgenii Stepanov Date: Fri, 21 May 2021 16:33:45 -0700 Subject: [PATCH 208/261] Enable Async MTE in android-clat. This change enables Async mode ARM MTE in clatd on compatible hardware and has no effect otherwise. Bug: 181603044 Test: atest under external/android-clat Change-Id: Ib3c23381e2c7ef84499b26672347468b30c6209a --- Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Android.bp b/Android.bp index 4eda2adb38..bdddd7a91e 100644 --- a/Android.bp +++ b/Android.bp @@ -90,6 +90,9 @@ cc_binary { init_rc: [ "vendor-464xlat.rc", ], + sanitize: { + memtag_heap: true, + }, } // Unit tests. From a8d862c80a818b2d045a572d62b6e0dc3ee08537 Mon Sep 17 00:00:00 2001 From: Evgenii Stepanov Date: Fri, 21 May 2021 16:33:45 -0700 Subject: [PATCH 209/261] Enable Async MTE in android-clat. This change enables Async mode ARM MTE in clatd on compatible hardware and has no effect otherwise. Bug: 181603044 Test: atest under external/android-clat Change-Id: Ib3c23381e2c7ef84499b26672347468b30c6209a Merged-In: Ib3c23381e2c7ef84499b26672347468b30c6209a (cherry picked from commit afe05e1fdf9cc176617438c87f20c7cbb6fe90b9) --- Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Android.bp b/Android.bp index 4eda2adb38..bdddd7a91e 100644 --- a/Android.bp +++ b/Android.bp @@ -90,6 +90,9 @@ cc_binary { init_rc: [ "vendor-464xlat.rc", ], + sanitize: { + memtag_heap: true, + }, } // Unit tests. From 50303534ceaef9d0a71811cccb9b444f1e695a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 2 Jun 2020 14:46:45 -0700 Subject: [PATCH 210/261] switch from using packet ring to normal read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With clat ebpf offload of the receive path on 4.9+ P+ devices this just doesn't matter any more. Additionally: - this is *much* simpler, - reduces memory consumption, - eliminates the need for memory locking privs, - and allows us to increment max supported packet size, which fixes a long outstanding terrible bug wrt. GRO'ed packets being oversize and simply dropped. So, win, win, win. Test: git grep '(^|[^t])ring[.][ch]' git grep 'TP_FRAME_SIZE|TP_BLOCK_SIZE|TP_FRAMES|TP_FRAME_GAP|TP_NUM_BLOCKS|packet_ring|ring_create|ring_read' come up empty Verified ipv4 ping works on a flame device on an ipv6 only wireless network. Bug: 130253220 Test: atest clatd_test netd_integration_test Signed-off-by: Maciej Żenczykowski Change-Id: I676dfd16ffd2961f14a4e8fcd707c6f9f484e024 --- Android.bp | 1 - clatd.c | 43 +++++++---------- clatd.h | 4 +- clatd_test.cpp | 1 + config.h | 3 -- ring.c | 128 ------------------------------------------------- ring.h | 55 --------------------- 7 files changed, 20 insertions(+), 215 deletions(-) delete mode 100644 ring.c delete mode 100644 ring.h diff --git a/Android.bp b/Android.bp index bdddd7a91e..c6961de0aa 100644 --- a/Android.bp +++ b/Android.bp @@ -52,7 +52,6 @@ filegroup { "logging.c", "netlink_callbacks.c", "netlink_msg.c", - "ring.c", "setif.c", "translate.c", ], diff --git a/clatd.c b/clatd.c index 3ffe934749..86850b0bb7 100644 --- a/clatd.c +++ b/clatd.c @@ -47,7 +47,6 @@ #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "ring.h" #include "setif.h" #include "translate.h" @@ -177,11 +176,8 @@ void drop_root_but_keep_caps() { exit(1); } - // keep CAP_NET_RAW capability to open raw socket, and CAP_IPC_LOCK for mmap - // to lock memory. - set_capability((1 << CAP_NET_ADMIN) | - (1 << CAP_NET_RAW) | - (1 << CAP_IPC_LOCK)); + // keep CAP_NET_RAW capability to open raw socket. + set_capability((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW)); } /* function: open_sockets @@ -202,8 +198,11 @@ void open_sockets(struct tun_data *tunnel, uint32_t mark) { tunnel->write_fd6 = rawsock; - tunnel->read_fd6 = ring_create(tunnel); + // Will eventually be bound to htons(ETH_P_IPV6) protocol, + // but only after appropriate bpf filter is attached. + tunnel->read_fd6 = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (tunnel->read_fd6 < 0) { + logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); exit(1); } } @@ -343,10 +342,8 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 */ void read_packet(int read_fd, int write_fd, int to_ipv6) { - ssize_t readlen; - uint8_t buf[PACKETLEN], *packet; - - readlen = read(read_fd, buf, PACKETLEN); + uint8_t buf[PACKETLEN]; + ssize_t readlen = read(read_fd, buf, PACKETLEN); if (readlen < 0) { if (errno != EAGAIN) { @@ -359,6 +356,11 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { return; } + if (!to_ipv6) { + translate_packet(write_fd, 0 /* to_ipv6 */, buf, readlen); + return; + } + struct tun_pi *tun_header = (struct tun_pi *)buf; if (readlen < (ssize_t)sizeof(*tun_header)) { logmsg(ANDROID_LOG_WARN, "read_packet/short read: got %ld bytes", readlen); @@ -375,9 +377,9 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); } - packet = (uint8_t *)(tun_header + 1); + uint8_t *packet = (uint8_t *)(tun_header + 1); readlen -= sizeof(*tun_header); - translate_packet(write_fd, to_ipv6, packet, readlen); + translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen); } /* function: event_loop @@ -400,24 +402,13 @@ void event_loop(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno)); } } else { - if (wait_fd[0].revents & POLLIN) { - ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); - } - // If any other bit is set, assume it's due to an error (i.e. POLLERR). - if (wait_fd[0].revents & ~POLLIN) { - // ring_read doesn't clear the error indication on the socket. - recv(tunnel->read_fd6, NULL, 0, MSG_PEEK); - logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", strerror(errno)); - } - // Call read_packet if the socket has data to be read, but also if an // error is waiting. If we don't call read() after getting POLLERR, a // subsequent poll() will return immediately with POLLERR again, // causing this code to spin in a loop. Calling read() will clear the // socket error flag instead. - if (wait_fd[1].revents) { - read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); - } + if (wait_fd[0].revents) read_packet(tunnel->read_fd6, tunnel->fd4, 0 /* to_ipv6 */); + if (wait_fd[1].revents) read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); } time_t now = time(NULL); diff --git a/clatd.h b/clatd.h index 899458c723..34fa885334 100644 --- a/clatd.h +++ b/clatd.h @@ -24,9 +24,9 @@ struct tun_data; -#define MAXMTU 1500 +#define MAXMTU 65536 #define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) -#define CLATD_VERSION "1.4" +#define CLATD_VERSION "1.5" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/clatd_test.cpp b/clatd_test.cpp index c16a4dd783..9b7a3e7554 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/config.h b/config.h index 1ba68504dc..9612192be1 100644 --- a/config.h +++ b/config.h @@ -21,12 +21,9 @@ #include #include -#include "ring.h" - struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; - struct packet_ring ring; }; struct clat_config { diff --git a/ring.c b/ring.c deleted file mode 100644 index 7626c6de45..0000000000 --- a/ring.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ring.c - packet ring buffer functions - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "logging.h" -#include "ring.h" -#include "translate.h" - -int ring_create(struct tun_data *tunnel) { - // Will eventually be bound to htons(ETH_P_IPV6) protocol, - // but only after appropriate bpf filter is attached. - int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (packetsock < 0) { - logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); - return -1; - } - - int ver = TPACKET_V2; - if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *)&ver, sizeof(ver))) { - logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno)); - return -1; - } - - int on = 1; - if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *)&on, sizeof(on))) { - logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno)); - } - - struct packet_ring *ring = &tunnel->ring; - ring->numblocks = TP_NUM_BLOCKS; - - int total_frames = TP_FRAMES * ring->numblocks; - - struct tpacket_req req = { - .tp_frame_size = TP_FRAME_SIZE, // Frame size. - .tp_block_size = TP_BLOCK_SIZE, // Frames per block. - .tp_block_nr = ring->numblocks, // Number of blocks. - .tp_frame_nr = total_frames, // Total frames. - }; - - if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { - logmsg(ANDROID_LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno)); - return -1; - } - - size_t buflen = TP_BLOCK_SIZE * ring->numblocks; - ring->base = mmap(NULL, buflen, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, - packetsock, 0); - if (ring->base == MAP_FAILED) { - logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno)); - return -1; - } - - ring->block = 0; - ring->slot = 0; - ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE; - ring->next = (struct tpacket2_hdr *)ring->base; - - logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", total_frames, - buflen, ring->base); - - return packetsock; -} - -/* function: ring_advance - * advances to the next position in the packet ring - * ring - packet ring buffer - */ -static struct tpacket2_hdr *ring_advance(struct packet_ring *ring) { - uint8_t *next = (uint8_t *)ring->next; - - ring->slot++; - next += TP_FRAME_SIZE; - - if (ring->slot == ring->numslots) { - ring->slot = 0; - ring->block++; - - if (ring->block < ring->numblocks) { - next += TP_FRAME_GAP; - } else { - ring->block = 0; - next = (uint8_t *)ring->base; - } - } - - ring->next = (struct tpacket2_hdr *)next; - return ring->next; -} - -/* function: ring_read - * reads a packet from the ring buffer and translates it - * read_fd - file descriptor to read original packet from - * write_fd - file descriptor to write translated packet to - * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 - */ -void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) { - struct tpacket2_hdr *tp = ring->next; - if (tp->tp_status & TP_STATUS_USER) { - uint8_t *packet = ((uint8_t *)tp) + tp->tp_net; - translate_packet(write_fd, to_ipv6, packet, tp->tp_len); - tp->tp_status = TP_STATUS_KERNEL; - tp = ring_advance(ring); - } -} diff --git a/ring.h b/ring.h deleted file mode 100644 index b9b8c11d2e..0000000000 --- a/ring.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ring.c - packet ring buffer functions - */ -#ifndef __RING_H__ -#define __RING_H__ - -#include -#include - -#include "clatd.h" - -struct tun_data; - -// Frame size. Must be a multiple of TPACKET_ALIGNMENT (=16) -// Why the 16? http://lxr.free-electrons.com/source/net/packet/af_packet.c?v=3.4#L1764 -#define TP_FRAME_SIZE (TPACKET_ALIGN(MAXMTU) + TPACKET_ALIGN(TPACKET2_HDRLEN) + 16) - -// Block size. Must be a multiple of the page size, and a power of two for efficient memory use. -#define TP_BLOCK_SIZE 65536 - -// In order to save memory, our frames are not an exact divider of the block size. Therefore, the -// mmaped region will have gaps corresponding to the empty space at the end of each block. -#define TP_FRAMES (TP_BLOCK_SIZE / TP_FRAME_SIZE) -#define TP_FRAME_GAP (TP_BLOCK_SIZE % TP_FRAME_SIZE) - -// TODO: Make this configurable. This requires some refactoring because the packet socket is -// opened before we drop privileges, but the configuration file is read after. A value of 16 -// results in 656 frames (1048576 bytes). -#define TP_NUM_BLOCKS 16 - -struct packet_ring { - uint8_t *base; - struct tpacket2_hdr *next; - int slot, numslots; - int block, numblocks; -}; - -int ring_create(struct tun_data *tunnel); -void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6); - -#endif From 7c87aaa50a8c0068d91b8980ec3cab57916d5258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Fri, 22 Oct 2021 16:07:00 -0700 Subject: [PATCH 211/261] clatd: drop AID_INET and AID_VPN supplementary groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I can't see a reason for these still existing. AID_VPN was needed in order to open tap/tun devices, but this logic has long been moved out of clatd and into netd. AID_INET is something left over from the Paranoid Android kernel patch days. Paranoid Android was definitely removed from the beginning of the Android Common Kernel 4.14-r branch, but we later removed it from 4.14-q as well (at least as far as AID_INET is concerned, it looks like AID_NET_RAW/AID_NET_ADMIN support stayed around). Anyway, the last branches in which AID_INET has special meaning are ACK 4.9-Q and 4.14-P. We effectively already require 4.9+ Q+ in Android S due to Android Q clat bpf requirements. Besides, the upgrade path for any 4.14-P kernels is trivial enough: 4.14-Q or 4.14-R. Nowadays '/sys/fs/bpf/prog_netd_cgroupsock_inet_create' *must* load on *all* 4.14 devices or bpfloader panics. This should enforce the internet permission via bpf as opposed to requiring Paranoid Android kernel patch. As such we should have all the privs necessary for correct functionality without this - at least on any 4.14+ devices. We dropped support for <4.9 || Change-Id: I8fe2e2c0232b6cf0d4b7b2585f840a574080afa9 --- clatd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clatd.c b/clatd.c index 86850b0bb7..bd2bf62976 100644 --- a/clatd.c +++ b/clatd.c @@ -159,8 +159,8 @@ void set_capability(uint64_t target_cap) { * drops root privs but keeps the needed capabilities */ void drop_root_but_keep_caps() { - gid_t groups[] = { AID_INET, AID_VPN }; - if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) < 0) { + // see man setgroups: this drops all supplementary groups + if (setgroups(0, NULL) < 0) { logmsg(ANDROID_LOG_FATAL, "setgroups failed: %s", strerror(errno)); exit(1); } From b64249ec5367e5ab174b1688c1125af14d8a8cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Fri, 22 Oct 2021 18:31:50 -0700 Subject: [PATCH 212/261] simplify clatd permission dropping logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no danger here, because we drop privileges before we start processing packets. This should also make clatd fully functional on 4.9-q kernels that still have the Paranoid Android patchset (by creating all sockets prior to dropping netd's uid and capabilities). Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I14cab836907be8bbedc0812cec277808e9f3f91f --- clatd.c | 11 ++++------- clatd.h | 2 +- main.c | 10 ++-------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/clatd.c b/clatd.c index bd2bf62976..7ec345450a 100644 --- a/clatd.c +++ b/clatd.c @@ -155,18 +155,16 @@ void set_capability(uint64_t target_cap) { } } -/* function: drop_root_but_keep_caps - * drops root privs but keeps the needed capabilities +/* function: drop_root_and_caps + * drops root privs and all capabilities */ -void drop_root_but_keep_caps() { +void drop_root_and_caps() { // see man setgroups: this drops all supplementary groups if (setgroups(0, NULL) < 0) { logmsg(ANDROID_LOG_FATAL, "setgroups failed: %s", strerror(errno)); exit(1); } - prctl(PR_SET_KEEPCAPS, 1); - if (setresgid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { logmsg(ANDROID_LOG_FATAL, "setresgid failed: %s", strerror(errno)); exit(1); @@ -176,8 +174,7 @@ void drop_root_but_keep_caps() { exit(1); } - // keep CAP_NET_RAW capability to open raw socket. - set_capability((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW)); + set_capability(0); } /* function: open_sockets diff --git a/clatd.h b/clatd.h index 34fa885334..75ffea3212 100644 --- a/clatd.h +++ b/clatd.h @@ -40,7 +40,7 @@ extern volatile sig_atomic_t running; void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); -void drop_root_but_keep_caps(); +void drop_root_and_caps(); void open_sockets(struct tun_data *tunnel, uint32_t mark); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, diff --git a/main.c b/main.c index 52e22acbc9..d452985ba3 100644 --- a/main.c +++ b/main.c @@ -123,19 +123,13 @@ int main(int argc, char **argv) { plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); - // run under a regular user but keep needed capabilities - drop_root_but_keep_caps(); - // open our raw sockets before dropping privs open_sockets(&tunnel, mark); - // keeps only admin capability - set_capability(1 << CAP_NET_ADMIN); - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); - // Drop all remaining capabilities. - set_capability(0); + // run under a regular user with no capabilities + drop_root_and_caps(); // Loop until someone sends us a signal or brings down the tun interface. if (signal(SIGTERM, stop_loop) == SIG_ERR) { From a4e9681c88bd5b0be7062b4cad17ab8da1ff37a7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 9 Dec 2021 17:36:58 +0900 Subject: [PATCH 213/261] Depend on libipchecksum, not libnetutils. clatd depends on libnetutils but it only actually uses the IP checksumming functions. Depend on libipchecksum instead. The unit test still depends on libnetutils because it uses libnetd_test_tun_interface, which calls into libnetutils. Test: m Test: atest clatd_test Test: builds, boots, connects to IPv6-only wifi Change-Id: I4f12e09f78a01d6df8b11a0c9e92c25c9d7a1248 --- Android.bp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index c6961de0aa..6df30786ea 100644 --- a/Android.bp +++ b/Android.bp @@ -65,11 +65,13 @@ cc_binary { ":clatd_common", "main.c" ], - static_libs: ["libnl"], + static_libs: [ + "libipchecksum", + "libnl", + ], shared_libs: [ "libcutils", "liblog", - "libnetutils", ], // Only enable clang-tidy for the daemon, not the tests, because enabling it for the @@ -104,6 +106,7 @@ cc_test { ], static_libs: [ "libbase", + "libipchecksum", "libnetd_test_tun_interface", "libnl", ], From 6006d17eab888bcd2c91d975e005c862477b4787 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Wed, 24 Nov 2021 15:06:17 +0800 Subject: [PATCH 214/261] [RFCLAT#1] Move the raw socket creation from clatd to netd This is a preparation for reducing the clatd required capability. Function change: Opening raw socket in open_sockets() is moved to ClatdController Argument change: -w [write socket file descriptor number] (added) Test: manual test 1. Connect to ipv6-only wifi. 2. Try a IPv4 traffic. $ ping 8.8.8.8 Change-Id: I9b4ea3de76dffd98cbab37c4091ad1a7e688dbc2 --- clatd.c | 14 +------------- clatd.h | 2 +- main.c | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/clatd.c b/clatd.c index 7ec345450a..4a34bf3e5f 100644 --- a/clatd.c +++ b/clatd.c @@ -182,19 +182,7 @@ void drop_root_and_caps() { * tunnel - tun device data * mark - the socket mark to use for the sending raw socket */ -void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); - if (rawsock < 0) { - logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); - exit(1); - } - - if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); - } - - tunnel->write_fd6 = rawsock; - +void open_sockets(struct tun_data *tunnel) { // Will eventually be bound to htons(ETH_P_IPV6) protocol, // but only after appropriate bpf filter is attached. tunnel->read_fd6 = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); diff --git a/clatd.h b/clatd.h index 75ffea3212..6907829c05 100644 --- a/clatd.h +++ b/clatd.h @@ -41,7 +41,7 @@ extern volatile sig_atomic_t running; void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_and_caps(); -void open_sockets(struct tun_data *tunnel, uint32_t mark); +void open_sockets(struct tun_data *tunnel); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); diff --git a/main.c b/main.c index d452985ba3..e4f7356cd1 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,7 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); + printf("-w [write socket descriptor number]\n"); } /* function: main @@ -60,11 +61,11 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *write_sock_str = NULL; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:w:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -84,6 +85,9 @@ int main(int argc, char **argv) { case 't': tunfd_str = optarg; break; + case 'w': + write_sock_str = optarg; + break; case 'h': print_help(); exit(0); @@ -112,6 +116,15 @@ int main(int argc, char **argv) { exit(1); } + if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { + logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", write_sock_str); + exit(1); + } + if (!tunnel.write_fd6) { + logmsg(ANDROID_LOG_FATAL, "no write_fd6 specified on commandline."); + exit(1); + } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); @@ -124,7 +137,7 @@ int main(int argc, char **argv) { v6_addr ? v6_addr : "(none)"); // open our raw sockets before dropping privs - open_sockets(&tunnel, mark); + open_sockets(&tunnel); configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); From 685fc68ed5ba3a385db10fe83c193c4a6ea564ad Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Mon, 13 Dec 2021 07:04:19 +0000 Subject: [PATCH 215/261] Revert "[RFCLAT#1] Move the raw socket creation from clatd to netd" Revert submission 1902610-clat_move_raw_socket Reason for revert: security patch (aosp/1903466) and clean up patch (aosp/1911234) have not merged yet Reverted Changes: I9b4ea3de7:[RFCLAT#1] Move the raw socket creation from clatd... Ibc46981a8:[RFNETD#1] Move the raw socket creation from clatd... Change-Id: Ic4fad13f522e06815168db4abbd047cf99956122 --- clatd.c | 14 +++++++++++++- clatd.h | 2 +- main.c | 19 +++---------------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/clatd.c b/clatd.c index 4a34bf3e5f..7ec345450a 100644 --- a/clatd.c +++ b/clatd.c @@ -182,7 +182,19 @@ void drop_root_and_caps() { * tunnel - tun device data * mark - the socket mark to use for the sending raw socket */ -void open_sockets(struct tun_data *tunnel) { +void open_sockets(struct tun_data *tunnel, uint32_t mark) { + int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); + if (rawsock < 0) { + logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); + exit(1); + } + + if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { + logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); + } + + tunnel->write_fd6 = rawsock; + // Will eventually be bound to htons(ETH_P_IPV6) protocol, // but only after appropriate bpf filter is attached. tunnel->read_fd6 = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); diff --git a/clatd.h b/clatd.h index 6907829c05..75ffea3212 100644 --- a/clatd.h +++ b/clatd.h @@ -41,7 +41,7 @@ extern volatile sig_atomic_t running; void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_and_caps(); -void open_sockets(struct tun_data *tunnel); +void open_sockets(struct tun_data *tunnel, uint32_t mark); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); diff --git a/main.c b/main.c index e4f7356cd1..d452985ba3 100644 --- a/main.c +++ b/main.c @@ -51,7 +51,6 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); - printf("-w [write socket descriptor number]\n"); } /* function: main @@ -61,11 +60,11 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *write_sock_str = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:w:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -85,9 +84,6 @@ int main(int argc, char **argv) { case 't': tunfd_str = optarg; break; - case 'w': - write_sock_str = optarg; - break; case 'h': print_help(); exit(0); @@ -116,15 +112,6 @@ int main(int argc, char **argv) { exit(1); } - if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { - logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", write_sock_str); - exit(1); - } - if (!tunnel.write_fd6) { - logmsg(ANDROID_LOG_FATAL, "no write_fd6 specified on commandline."); - exit(1); - } - len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); @@ -137,7 +124,7 @@ int main(int argc, char **argv) { v6_addr ? v6_addr : "(none)"); // open our raw sockets before dropping privs - open_sockets(&tunnel); + open_sockets(&tunnel, mark); configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); From 0cd5aa540389cfd8a9be13e680c1ca34c4e85bc8 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 9 Dec 2021 15:05:52 +0900 Subject: [PATCH 216/261] Clatd changes for move of libipchecksum. libipchecksum is becoming its own library in frameworks/libs/net and its include files are no longer under netutils/. Adjust clatd code accordingly. Also reorder some include files as suggested by clang-format. Test: m Test: atest clatd_test Test: connects to IPv6-only wifi, testipv4.com shows 10/10 Change-Id: I5e4c73065ce828123aef7e8851227f9bdf444ff9 --- Android.bp | 4 ++-- clatd_test.cpp | 2 +- dump.c | 11 +++++------ ipv4.c | 3 +-- ipv6.c | 6 ++---- translate.c | 6 +++--- 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Android.bp b/Android.bp index 6df30786ea..9d2d85e878 100644 --- a/Android.bp +++ b/Android.bp @@ -66,7 +66,7 @@ cc_binary { "main.c" ], static_libs: [ - "libipchecksum", + "libip_checksum", "libnl", ], shared_libs: [ @@ -106,7 +106,7 @@ cc_test { ], static_libs: [ "libbase", - "libipchecksum", + "libip_checksum", "libnetd_test_tun_interface", "libnl", ], diff --git a/clatd_test.cpp b/clatd_test.cpp index 9b7a3e7554..d6d7e66aa8 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -30,10 +30,10 @@ #include "tun_interface.h" extern "C" { +#include "checksum.h" #include "clatd.h" #include "config.h" #include "getaddr.h" -#include "netutils/checksum.h" #include "translate.h" } diff --git a/dump.c b/dump.c index 7332d6c652..289f161742 100644 --- a/dump.c +++ b/dump.c @@ -15,9 +15,7 @@ * * dump.c - print various headers for debugging */ -#include -#include -#include +#include "dump.h" #include #include @@ -29,12 +27,13 @@ #include #include #include +#include +#include +#include -#include "netutils/checksum.h" - +#include "checksum.h" #include "clatd.h" #include "debug.h" -#include "dump.h" #include "logging.h" #if CLAT_DEBUG diff --git a/ipv4.c b/ipv4.c index c052d03e89..2be02e34c2 100644 --- a/ipv4.c +++ b/ipv4.c @@ -17,8 +17,7 @@ */ #include -#include "netutils/checksum.h" - +#include "checksum.h" #include "debug.h" #include "dump.h" #include "logging.h" diff --git a/ipv6.c b/ipv6.c index ca3e4c68a5..05cd3ab116 100644 --- a/ipv6.c +++ b/ipv6.c @@ -15,12 +15,10 @@ * * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them */ +#include #include -#include - -#include "netutils/checksum.h" - +#include "checksum.h" #include "config.h" #include "debug.h" #include "dump.h" diff --git a/translate.c b/translate.c index 728acc3045..22830d890c 100644 --- a/translate.c +++ b/translate.c @@ -15,17 +15,17 @@ * * translate.c - CLAT functions / partial implementation of rfc6145 */ +#include "translate.h" + #include -#include "netutils/checksum.h" - +#include "checksum.h" #include "clatd.h" #include "common.h" #include "config.h" #include "debug.h" #include "icmp.h" #include "logging.h" -#include "translate.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos From 0714a18a36a20cc94ac6c9d8916bb9a3782e43ac Mon Sep 17 00:00:00 2001 From: Nucca Chen Date: Mon, 13 Dec 2021 09:24:38 +0000 Subject: [PATCH 217/261] Revert "Revert "[RFCLAT#1] Move the raw socket creation from cla..." Revert submission 1919326-revert-1902610-clat_move_raw_socket-XKUYDBPMIO Reason for revert: security patch (aosp/1903466) and clean up patch (aosp/1911234) have merged Reverted Changes: I849cf4150:Revert "[RFNETD#1] Move the raw socket creation fr... Ic4fad13f5:Revert "[RFCLAT#1] Move the raw socket creation fr... Bug: 212345928 Test: atest clatd_test Change-Id: Ib3f35c293b160f57917bf6f52a533faecf2abf80 --- clatd.c | 14 +------------- clatd.h | 2 +- main.c | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/clatd.c b/clatd.c index 7ec345450a..4a34bf3e5f 100644 --- a/clatd.c +++ b/clatd.c @@ -182,19 +182,7 @@ void drop_root_and_caps() { * tunnel - tun device data * mark - the socket mark to use for the sending raw socket */ -void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); - if (rawsock < 0) { - logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); - exit(1); - } - - if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); - } - - tunnel->write_fd6 = rawsock; - +void open_sockets(struct tun_data *tunnel) { // Will eventually be bound to htons(ETH_P_IPV6) protocol, // but only after appropriate bpf filter is attached. tunnel->read_fd6 = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); diff --git a/clatd.h b/clatd.h index 75ffea3212..6907829c05 100644 --- a/clatd.h +++ b/clatd.h @@ -41,7 +41,7 @@ extern volatile sig_atomic_t running; void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_and_caps(); -void open_sockets(struct tun_data *tunnel, uint32_t mark); +void open_sockets(struct tun_data *tunnel); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); diff --git a/main.c b/main.c index d452985ba3..e4f7356cd1 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,7 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); + printf("-w [write socket descriptor number]\n"); } /* function: main @@ -60,11 +61,11 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *write_sock_str = NULL; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:w:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -84,6 +85,9 @@ int main(int argc, char **argv) { case 't': tunfd_str = optarg; break; + case 'w': + write_sock_str = optarg; + break; case 'h': print_help(); exit(0); @@ -112,6 +116,15 @@ int main(int argc, char **argv) { exit(1); } + if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { + logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", write_sock_str); + exit(1); + } + if (!tunnel.write_fd6) { + logmsg(ANDROID_LOG_FATAL, "no write_fd6 specified on commandline."); + exit(1); + } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); @@ -124,7 +137,7 @@ int main(int argc, char **argv) { v6_addr ? v6_addr : "(none)"); // open our raw sockets before dropping privs - open_sockets(&tunnel, mark); + open_sockets(&tunnel); configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); From 06367f38702a763777652aff8f2ac50c57772dbb Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Wed, 24 Nov 2021 17:22:52 +0800 Subject: [PATCH 218/261] [RFCLAT#2] Move the packet socket creation from clatd to netd This is a preparation for reducing the clatd required capability. Function change: - Opening packet socket in open_sockets() is moved to ClatdController - remove open_sockets() Argument change: -r [read socket descriptor number] Bug: 212345928 Test: manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: I912a4663dad101a4bcba30934340862572aa9318 --- clatd.c | 15 --------------- clatd.h | 1 - main.c | 21 ++++++++++++++++----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/clatd.c b/clatd.c index 4a34bf3e5f..6a0a0d8a4e 100644 --- a/clatd.c +++ b/clatd.c @@ -177,21 +177,6 @@ void drop_root_and_caps() { set_capability(0); } -/* function: open_sockets - * opens a packet socket to receive IPv6 packets and a raw socket to send them - * tunnel - tun device data - * mark - the socket mark to use for the sending raw socket - */ -void open_sockets(struct tun_data *tunnel) { - // Will eventually be bound to htons(ETH_P_IPV6) protocol, - // but only after appropriate bpf filter is attached. - tunnel->read_fd6 = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (tunnel->read_fd6 < 0) { - logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); - exit(1); - } -} - int ipv6_address_changed(const char *interface) { union anyip *interface_ip; diff --git a/clatd.h b/clatd.h index 6907829c05..21f3c9569a 100644 --- a/clatd.h +++ b/clatd.h @@ -41,7 +41,6 @@ extern volatile sig_atomic_t running; void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_and_caps(); -void open_sockets(struct tun_data *tunnel); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); diff --git a/main.c b/main.c index e4f7356cd1..96fbd0d6c3 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,7 @@ void print_help() { printf("-6 [IPv6 address]\n"); printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); + printf("-r [read socket descriptor number]\n"); printf("-w [write socket descriptor number]\n"); } @@ -61,11 +62,12 @@ int main(int argc, char **argv) { struct tun_data tunnel; int opt; char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *write_sock_str = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *read_sock_str = NULL, + *write_sock_str = NULL; uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:w:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:m:t:r:w:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -85,6 +87,9 @@ int main(int argc, char **argv) { case 't': tunfd_str = optarg; break; + case 'r': + read_sock_str = optarg; + break; case 'w': write_sock_str = optarg; break; @@ -116,6 +121,15 @@ int main(int argc, char **argv) { exit(1); } + if (read_sock_str != NULL && !parse_int(read_sock_str, &tunnel.read_fd6)) { + logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", read_sock_str); + exit(1); + } + if (!tunnel.read_fd6) { + logmsg(ANDROID_LOG_FATAL, "no read_fd6 specified on commandline."); + exit(1); + } + if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", write_sock_str); exit(1); @@ -136,9 +150,6 @@ int main(int argc, char **argv) { plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); - // open our raw sockets before dropping privs - open_sockets(&tunnel); - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); // run under a regular user with no capabilities From 5dafb0e92cef0e60fa83a8056f59fc93f5bfd572 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Wed, 24 Nov 2021 20:19:43 +0800 Subject: [PATCH 219/261] [RFCLAT#3] Move the tun interface setup from clatd to netd This is a preparation for reducing the clatd required capability. Function change: - configure_tun_ip(), detect_mtu() are moved to netd/ClatdController - move Clatd_Config.ipv4_local_subnet setup from configure_tun_ip() to main() because configure_tun_ip() is removed. translate.c needs the clat IPv4 address for 4->6, 6>->4 address translation. Bug: 212345928 Test: manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: Ibf80b17865f414f329ab7c50836595f3b99360e2 --- clatd.c | 90 ++------------------------------------------------------- clatd.h | 6 ++-- main.c | 7 ++++- 3 files changed, 10 insertions(+), 93 deletions(-) diff --git a/clatd.c b/clatd.c index 6a0a0d8a4e..865a568ac8 100644 --- a/clatd.c +++ b/clatd.c @@ -104,37 +104,6 @@ int configure_packet_socket(int sock) { return 1; } -/* function: configure_tun_ip - * configures the ipv4 and ipv6 addresses on the tunnel interface - * tunnel - tun device data - * mtu - mtu of tun device - */ -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { - if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { - logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); - exit(1); - } - - char addrstr[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr)); - logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4); - - // Configure the interface before bringing it up. As soon as we bring the interface up, the - // framework will be notified and will assume the interface's configuration has been finalized. - int status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32, - &Global_Clatd_Config.ipv4_local_subnet); - if (status < 0) { - logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status)); - exit(1); - } - - status = if_up(tunnel->device4, mtu); - if (status < 0) { - logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status)); - exit(1); - } -} - /* function: set_capability * set the permitted, effective and inheritable capabilities of the current * thread @@ -230,76 +199,21 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter return 1; } -int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark) { - // Create an IPv6 UDP socket. - int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (s < 0) { - logmsg(ANDROID_LOG_FATAL, "socket(AF_INET6, SOCK_DGRAM, 0) failed"); - exit(1); - } - - // Socket's mark affects routing decisions (network selection) - if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) { - logmsg(ANDROID_LOG_FATAL, "setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno)); - exit(1); - } - - // Try to connect udp socket to plat_subnet(96 bits):plat_suffix(32 bits) - struct sockaddr_in6 dst = { - .sin6_family = AF_INET6, - .sin6_addr = *plat_subnet, - }; - dst.sin6_addr.s6_addr32[3] = plat_suffix; - if (connect(s, (struct sockaddr *)&dst, sizeof(dst))) { - logmsg(ANDROID_LOG_FATAL, "connect() failed: %s", strerror(errno)); - exit(1); - } - - // Fetch the socket's IPv6 mtu - this is effectively fetching mtu from routing table - int mtu; - socklen_t sz_mtu = sizeof(mtu); - if (getsockopt(s, SOL_IPV6, IPV6_MTU, &mtu, &sz_mtu)) { - logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) failed: %s", strerror(errno)); - exit(1); - } - if (sz_mtu != sizeof(mtu)) { - logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) returned unexpected size: %d", - sz_mtu); - exit(1); - } - close(s); - - return mtu; -} - /* 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 - * v4_addr - the v4 address to use on the tunnel interface * v6_addr - the v6 address to use on the native interface * tunnel - tun device data - * mark - the socket mark to use for the sending raw socket */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { +void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v6_addr, + struct tun_data *tunnel) { Global_Clatd_Config.native_ipv6_interface = uplink_interface; if (!plat_prefix || 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); exit(1); } - int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark); - // clamp to minimum ipv6 mtu - this probably cannot ever trigger - if (mtu < 1280) mtu = 1280; - // clamp to buffer size - if (mtu > MAXMTU) mtu = MAXMTU; - // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes - mtu -= MTU_DELTA; - logmsg(ANDROID_LOG_WARN, "ipv4 mtu is %d", mtu); - - configure_tun_ip(tunnel, v4_addr, mtu); - if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { exit(1); } diff --git a/clatd.h b/clatd.h index 21f3c9569a..a42768caa8 100644 --- a/clatd.h +++ b/clatd.h @@ -38,15 +38,13 @@ struct tun_data; extern volatile sig_atomic_t running; -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); void drop_root_and_caps(); int ipv6_address_changed(const char *interface); int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *src_addr); -int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark); -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6, struct tun_data *tunnel, uint32_t mark); +void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v6, + struct tun_data *tunnel); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/main.c b/main.c index 96fbd0d6c3..fe88dfdfb9 100644 --- a/main.c +++ b/main.c @@ -145,12 +145,17 @@ int main(int argc, char **argv) { exit(1); } + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); + exit(1); + } + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", CLATD_VERSION, uplink_interface, mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); + configure_interface(uplink_interface, plat_prefix, v6_addr, &tunnel); // run under a regular user with no capabilities drop_root_and_caps(); From 5c112131d907a3864e75f713ebe97d5bc5925e53 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Thu, 25 Nov 2021 09:40:17 +0800 Subject: [PATCH 220/261] [RFCLAT#4] Move clat ipv6 address setup from clatd to netd This is a preparation for reducing the clatd required capability. Function change: - configure_clat_ipv6_address(), configure_interface(), configure_packet_socket() are moved to netd/ClatdController - move the follows to main() because previous functions are removed. setup Global_Clatd_Config.native_ipv6_interface setup Global_Clatd_Config.plat_subnet setup Global_Clatd_Config.ipv6_local_subnet Bug: 212345928 Test: manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: I8457b7752a799756549a72296aae572ad73ff653 --- clatd.c | 97 --------------------------------------------------------- clatd.h | 4 --- main.c | 13 ++++++-- 3 files changed, 11 insertions(+), 103 deletions(-) diff --git a/clatd.c b/clatd.c index 865a568ac8..bc6b652469 100644 --- a/clatd.c +++ b/clatd.c @@ -57,53 +57,6 @@ struct clat_config Global_Clatd_Config; volatile sig_atomic_t running = 1; -/* function: configure_packet_socket - * Binds the packet socket and attaches the receive filter to it. - * sock - the socket to configure - */ -int configure_packet_socket(int sock) { - uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; - - // clang-format off - struct sock_filter filter_code[] = { - // Load the first four bytes of the IPv6 destination address (starts 24 bytes in). - // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads - // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it - // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other - // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet). - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 24), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[0]), 0, 7), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 28), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[1]), 0, 5), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 32), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[2]), 0, 3), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1), - BPF_STMT(BPF_RET | BPF_K, PACKETLEN), - BPF_STMT(BPF_RET | BPF_K, 0), - }; - // clang-format on - struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code }; - - if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) { - logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno)); - return 0; - } - - struct sockaddr_ll sll = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), - .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. - }; - if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { - logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); - return 0; - } - - return 1; -} - /* function: set_capability * set the permitted, effective and inheritable capabilities of the current * thread @@ -169,56 +122,6 @@ int ipv6_address_changed(const char *interface) { } } -/* function: configure_clat_ipv6_address - * picks the clat IPv6 address and configures packet translation to use it. - * tunnel - tun device data - * interface - uplink interface name - * returns: 1 on success, 0 on failure - */ -int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, - const char *v6_addr) { - if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { - logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); - return 0; - } - - char addrstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); - logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface); - - // Start translating packets to the new prefix. - add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface); - - // Update our packet socket filter to reflect the new 464xlat IP address. - if (!configure_packet_socket(tunnel->read_fd6)) { - // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because configure_packet_socket has already done so. - return 0; - } - - return 1; -} - -/* function: configure_interface - * reads the configuration and applies it to the interface - * uplink_interface - network interface to use to reach the ipv6 internet - * plat_prefix - PLAT prefix to use - * v6_addr - the v6 address to use on the native interface - * tunnel - tun device data - */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v6_addr, - struct tun_data *tunnel) { - Global_Clatd_Config.native_ipv6_interface = uplink_interface; - if (!plat_prefix || 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); - exit(1); - } - - if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { - exit(1); - } -} - /* function: read_packet * reads a packet from the tunnel fd and translates it * read_fd - file descriptor to read original packet from diff --git a/clatd.h b/clatd.h index a42768caa8..be3c9c18eb 100644 --- a/clatd.h +++ b/clatd.h @@ -41,10 +41,6 @@ extern volatile sig_atomic_t running; void set_capability(uint64_t target_cap); void drop_root_and_caps(); int ipv6_address_changed(const char *interface); -int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, - const char *src_addr); -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v6, - struct tun_data *tunnel); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/main.c b/main.c index fe88dfdfb9..ef5e3ab92b 100644 --- a/main.c +++ b/main.c @@ -145,18 +145,27 @@ int main(int argc, char **argv) { exit(1); } + Global_Clatd_Config.native_ipv6_interface = uplink_interface; + if (!plat_prefix || 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); + exit(1); + } + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); exit(1); } + if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + exit(1); + } + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", CLATD_VERSION, uplink_interface, mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); - configure_interface(uplink_interface, plat_prefix, v6_addr, &tunnel); - // run under a regular user with no capabilities drop_root_and_caps(); From 1443a9e357b22fd8192b8a865cfb16ca36a697ea Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Mon, 6 Dec 2021 15:19:55 +0800 Subject: [PATCH 221/261] [RFCLAT#5] remove the unnecessary tun fd, socket handler and socket opt cleanup - Closing tun fd is not necessary because any open fds are closed when a process exits - Closing socket fds are not necessary because any open fds are closed when a process exits - Applying IPV6_LEAVE_ANYCAST is not necessary because the setting is stored with the socket and removed when it is closed See addrconf_ifdown, ipv6_ac_destroy_dev Bug: 212345928 Test: atest clatd_test and manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: I8002c1adef208f5dcd6665fecee0df6236159f88 --- main.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/main.c b/main.c index ef5e3ab92b..069592f47f 100644 --- a/main.c +++ b/main.c @@ -178,11 +178,6 @@ int main(int argc, char **argv) { event_loop(&tunnel); logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); - del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); - - close(tunnel.write_fd6); - close(tunnel.read_fd6); - close(tunnel.fd4); if (running) { logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface); From ed36b4b664063609c922458222aee7b351e17685 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Fri, 26 Nov 2021 19:12:49 +0800 Subject: [PATCH 222/261] [RFCLAT#6] clatd_test: remove dead test for detect_mtu Delete the test because it has been moved to netd. - DetectMtu Bug: 212345928 Test: atest clatd_test Change-Id: I9b9d178ae6c2f93699697a46477414ea3c66d9a9 --- clatd_test.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index d6d7e66aa8..8c8eb799d3 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -625,11 +625,6 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -TEST_F(ClatdTest, DetectMtu) { - // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB - ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); -} - TEST_F(ClatdTest, ConfigureTunIpManual) { // Create an interface for configure_tun_ip to configure and bring up. TunInterface v4Iface; From 45b2f7c3c5092137ef564bdd0e5b77e6b7d59978 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Fri, 26 Nov 2021 19:16:22 +0800 Subject: [PATCH 223/261] [RFCLAT#7] clatd_test: remove dead test for configure_tun_ip Delete the test because it has been moved to netd. - ConfigureTunIpManual Bug: 212345928 Test: atest clatd_test Change-Id: I6b997aa0eb383aad04fd85618b64796f11c79cbf --- clatd_test.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 8c8eb799d3..7b2fb37c73 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -625,24 +625,6 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -TEST_F(ClatdTest, ConfigureTunIpManual) { - // Create an interface for configure_tun_ip to configure and bring up. - TunInterface v4Iface; - ASSERT_EQ(0, v4Iface.init()); - struct tun_data tunnel = makeTunData(); - strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - - configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */, 1472); - EXPECT_EQ(inet_addr("192.0.2.1"), Global_Clatd_Config.ipv4_local_subnet.s_addr); - - union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); - ASSERT_NE(nullptr, ip); - EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); - free(ip); - - v4Iface.destroy(); -} - TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks the data. uint8_t v4_header[] = { IPV4_UDP_HEADER }; From 049cbd87b34421cf379b7b67cb7621802967a901 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Fri, 26 Nov 2021 19:24:29 +0800 Subject: [PATCH 224/261] [RFCLAT#8] clatd_test: remove or refactor tests for configure_clat_ipv6_address 1. Delete the test because it has been moved to netd. - ConfigureIpv6Address 2. Refactor the existing test. Assign IPv6 address directly because the address is applied by cli argument now because configure_clat_ipv6_address() is removed. - Ipv6AddressChanged Bug: 212345928 Test: atest clatd_test Change-Id: I673327a36727e22f4ab23c191ee389fc1914016c --- clatd_test.cpp | 59 +------------------------------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 7b2fb37c73..7fa870e763 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -560,21 +560,6 @@ int get_transport_checksum(const uint8_t *packet) { } } -static tun_data makeTunData() { - // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. - return { - .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), - .fd4 = socket(AF_UNIX, SOCK_DGRAM, 0), - }; -} - -void freeTunData(tun_data *tunnel) { - close(tunnel->write_fd6); - close(tunnel->read_fd6); - close(tunnel->fd4); -} - struct clat_config Global_Clatd_Config; class ClatdTest : public ::testing::Test { @@ -860,50 +845,8 @@ TEST_F(ClatdTest, GetInterfaceIp) { expect_ipv6_addr_equal(&expected, &actual); } -void expectSocketBound(int ifindex, int sock) { - // Check that the packet socket is bound to the interface. We can't check the socket filter - // because there is no way to fetch it from the kernel. - sockaddr_ll sll; - socklen_t len = sizeof(sll); - ASSERT_EQ(0, getsockname(sock, reinterpret_cast(&sll), &len)); - EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); - EXPECT_EQ(ifindex, sll.sll_ifindex); -} - -TEST_F(ClatdTest, ConfigureIpv6Address) { - struct tun_data tunnel = makeTunData(); - - ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet)); - - const char *addrStr = "2001:db8::f00"; - in6_addr addr; - ASSERT_EQ(1, inet_pton(AF_INET6, addrStr, &addr)); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), addrStr)); - - EXPECT_EQ(htonl(0x20010db8), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[0]); - EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[1]); - EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[2]); - EXPECT_EQ(htonl(0x00000f00), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); - - // Check that the packet socket is bound to the interface. We can't check the socket filter - // because there is no way to fetch it from the kernel. - sockaddr_ll sll; - socklen_t len = sizeof(sll); - ASSERT_EQ(0, getsockname(tunnel.read_fd6, reinterpret_cast(&sll), &len)); - EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); - EXPECT_EQ(sll.sll_ifindex, sTun.ifindex()); - - expectSocketBound(sTun.ifindex(), tunnel.read_fd6); - - freeTunData(&tunnel); -} - TEST_F(ClatdTest, Ipv6AddressChanged) { // Configure the clat IPv6 address. - struct tun_data tunnel = { - .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), - }; const char *ifname = sTun.name().c_str(); in6_addr myaddr = sTun.srcAddr(); @@ -911,7 +854,7 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { char addrstr[INET6_ADDRSTRLEN]; ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, addrstr)); + Global_Clatd_Config.ipv6_local_subnet = myaddr; EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); From 208b2a1a0c9a705fa509ab3de74822d091a10542 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Thu, 2 Dec 2021 18:13:33 +0800 Subject: [PATCH 225/261] [RFCLAT#9] refactor getinterface_ip() to stop using libnl The interface address query doesn't use libnl anymore. Needed for reducing the library dependency. Once the last libnl user del_anycast_address() is removed, clatd can remove libnl. Less dependency helps moving clatd to mainline. parse_ifaddrmsg() is used to replaced with getaddr_cb() which updates struct target variable until walking through all messages. parse_ifaddrmsg() keeps the same behavior as getaddr_cb(). Bug: 212345928 Test: atest clatd_test and manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: I56591ec851d3202e18cce9fce3995d658d9f07e2 --- clatd_test.cpp | 15 +++++- getaddr.c | 138 ++++++++++++++++++++++++++++++------------------- getaddr.h | 2 + 3 files changed, 101 insertions(+), 54 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 7fa870e763..8eef738461 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -837,7 +837,20 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { "UDP/IPv4 -> UDP/IPv6 checksum neutral"); } -TEST_F(ClatdTest, GetInterfaceIp) { +TEST_F(ClatdTest, GetInterfaceIpV4) { + TunInterface v4Iface; + ASSERT_EQ(0, v4Iface.init()); + EXPECT_EQ(0, v4Iface.addAddress("192.0.2.1", 32)); + + union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); + ASSERT_NE(nullptr, ip); + EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); + free(ip); + + v4Iface.destroy(); +} + +TEST_F(ClatdTest, GetInterfaceIpV6) { union anyip *ip = getinterface_ip(sTun.name().c_str(), AF_INET6); ASSERT_NE(nullptr, ip); in6_addr expected = sTun.srcAddr(); diff --git a/getaddr.c b/getaddr.c index 6f4bc866f8..f25efc1176 100644 --- a/getaddr.c +++ b/getaddr.c @@ -15,21 +15,27 @@ * * getaddr.c - get a locally configured address */ -#include -#include -#include -#include +#include "getaddr.h" +#include #include #include +#include +#include #include #include +#include +#include +#include -#include "getaddr.h" #include "logging.h" -#include "netlink_msg.h" -// shared state between getinterface_ip and getaddr_cb +// Kernel suggests that keep the packet under 8KiB (NLMSG_GOODSIZE) in include/linux/netlink.h. +#define NLMSG_SIZE 8192 + +// shared state between getinterface_ip and parse_ifaddrmsg +// TODO: refactor the communication between getinterface_ip and parse_ifaddrmsg because there +// is no netlink callback anymore. struct target { int family; unsigned int ifindex; @@ -37,57 +43,97 @@ struct target { int foundip; }; -/* function: getaddr_cb - * callback for getinterface_ip - * msg - netlink message - * data - (struct target) info for which address we're looking for +/* function: parse_ifaddrmsg + * parse ifaddrmsg for getinterface_ip + * nh - netlink message header + * targ_p - (struct target) info for which address we're looking for + * and the parsed result if any. */ -static int getaddr_cb(struct nl_msg *msg, void *data) { +static void parse_ifaddrmsg(struct nlmsghdr *nh, struct target *targ_p) { 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)); + ifa_p = (struct ifaddrmsg *)NLMSG_DATA(nh); rta_p = (struct rtattr *)IFA_RTA(ifa_p); - if (ifa_p->ifa_index != targ_p->ifindex) return NL_OK; + if (ifa_p->ifa_index != targ_p->ifindex) return; - if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return NL_OK; + if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return; - rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); + rta_len = IFA_PAYLOAD(nh); 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; + return; } 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; + return; } break; } } - - return NL_OK; } -/* function: error_handler - * error callback for getinterface_ip - * nla - source of the error message - * err - netlink message - * arg - (struct target) info for which address we're looking for - */ -static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, - __attribute__((unused)) struct nlmsgerr *err, - __attribute__((unused)) void *arg) { - return NL_OK; +void sendrecv_ifaddrmsg(struct target *targ_p) { + int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE); + if (s < 0) { + logmsg(ANDROID_LOG_ERROR, "open NETLINK_ROUTE socket failed %s", strerror(errno)); + return; + } + + // Fill in netlink structures. + struct { + struct nlmsghdr n; + struct ifaddrmsg r; + } req = { + // Netlink message header. + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, + .n.nlmsg_type = RTM_GETADDR, + + // Interface address message header. + .r.ifa_family = targ_p->family, + }; + + // Send interface address message. + if ((send(s, &req, req.n.nlmsg_len, 0)) < 0) { + logmsg(ANDROID_LOG_ERROR, "send netlink socket failed %s", strerror(errno)); + close(s); + return; + } + + // Read interface address message and parse the result if any. + ssize_t bytes_read; + char buf[NLMSG_SIZE]; + while ((bytes_read = recv(s, buf, sizeof(buf), 0)) > 0) { + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + for (; NLMSG_OK(nh, bytes_read); nh = NLMSG_NEXT(nh, bytes_read)) { + if (nh->nlmsg_type == NLMSG_DONE) { + close(s); + return; + } + if (nh->nlmsg_type == NLMSG_ERROR) { + logmsg(ANDROID_LOG_ERROR, "netlink message error"); + close(s); + return; + } + if (nh->nlmsg_type == RTM_NEWADDR) { + // Walk through the all messages and update struct target variable as the deleted + // callback behavior of getaddr_cb() which always returns NL_OK. + // TODO: review if this can early return once address has been found. + parse_ifaddrmsg(nh, targ_p); + } + } + } + close(s); } /* function: getinterface_ip @@ -97,42 +143,28 @@ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, * 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; + struct target targ = { + .family = family, + .foundip = 0, + .ifindex = if_nametoindex(interface), + }; - 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); + // sends message and receives the response. + sendrecv_ifaddrmsg(&targ); if (targ.foundip) { retval = malloc(sizeof(union anyip)); if (!retval) { logmsg(ANDROID_LOG_FATAL, "getinterface_ip/out of memory"); - goto cleanup; + return NULL; } memcpy(retval, &targ.ip, sizeof(union anyip)); } -cleanup: - if (callbacks) nl_cb_put(callbacks); - return retval; } diff --git a/getaddr.h b/getaddr.h index 5718e6236d..482ac13cac 100644 --- a/getaddr.h +++ b/getaddr.h @@ -18,6 +18,8 @@ #ifndef __GETADDR_H__ #define __GETADDR_H__ +#include + union anyip { struct in6_addr ip6; struct in_addr ip4; From 396b5e7b92ea985280d8286d5c372f4d4089637a Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Mon, 6 Dec 2021 16:58:35 +0800 Subject: [PATCH 226/261] [RFCLAT#10] remove libnl related files libnl is not required anymore. remove all related files. Bug: 212345928 Test: atest clatd_test and manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: Ic4725ef01667576f2a958f38d458374c2e749569 --- Android.bp | 5 -- clatd.c | 1 - getaddr.c | 3 +- main.c | 1 - netlink_callbacks.c | 67 ---------------- netlink_callbacks.h | 24 ------ netlink_msg.c | 182 ------------------------------------------- netlink_msg.h | 30 -------- setif.c | 184 -------------------------------------------- setif.h | 28 ------- 10 files changed, 1 insertion(+), 524 deletions(-) delete mode 100644 netlink_callbacks.c delete mode 100644 netlink_callbacks.h delete mode 100644 netlink_msg.c delete mode 100644 netlink_msg.h delete mode 100644 setif.c delete mode 100644 setif.h diff --git a/Android.bp b/Android.bp index 9d2d85e878..0eb5298e13 100644 --- a/Android.bp +++ b/Android.bp @@ -50,9 +50,6 @@ filegroup { "ipv4.c", "ipv6.c", "logging.c", - "netlink_callbacks.c", - "netlink_msg.c", - "setif.c", "translate.c", ], } @@ -67,7 +64,6 @@ cc_binary { ], static_libs: [ "libip_checksum", - "libnl", ], shared_libs: [ "libcutils", @@ -108,7 +104,6 @@ cc_test { "libbase", "libip_checksum", "libnetd_test_tun_interface", - "libnl", ], shared_libs: [ "libcutils", diff --git a/clatd.c b/clatd.c index bc6b652469..bb6aedccbc 100644 --- a/clatd.c +++ b/clatd.c @@ -47,7 +47,6 @@ #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "setif.h" #include "translate.h" struct clat_config Global_Clatd_Config; diff --git a/getaddr.c b/getaddr.c index f25efc1176..ba8052d16f 100644 --- a/getaddr.c +++ b/getaddr.c @@ -22,8 +22,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/main.c b/main.c index 069592f47f..74fb0c2cb2 100644 --- a/main.c +++ b/main.c @@ -31,7 +31,6 @@ #include "common.h" #include "config.h" #include "logging.h" -#include "setif.h" #define DEVICEPREFIX "v4-" diff --git a/netlink_callbacks.c b/netlink_callbacks.c deleted file mode 100644 index 804976dda3..0000000000 --- a/netlink_callbacks.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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(__attribute__((unused)) struct nl_msg *msg, void *data) { - int *retval = data; - *retval = 0; - return NL_OK; -} - -/* function: error_handler - * generic netlink callback for error messages - * nla - error source - * err - netlink error message - * arg - pointer to an int, stores the error code - */ -static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, struct nlmsgerr *err, - void *arg) { - int *retval = arg; - if (err->error < 0) { - *retval = err->error; - } else { - *retval = 0; // NLMSG_ERROR used as reply type on no error - } - return NL_OK; -} - -/* function: alloc_ack_callbacks - * allocates a set of netlink callbacks. returns NULL on failure. callbacks will modify retval - * with <0 meaning failure retval - shared state between caller and callback functions - */ -struct nl_cb *alloc_ack_callbacks(int *retval) { - struct nl_cb *callbacks; - - callbacks = nl_cb_alloc(NL_CB_DEFAULT); - if (!callbacks) { - return NULL; - } - nl_cb_set(callbacks, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, retval); - nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, retval); - return callbacks; -} diff --git a/netlink_callbacks.h b/netlink_callbacks.h deleted file mode 100644 index 298ad3e393..0000000000 --- a/netlink_callbacks.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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 deleted file mode 100644 index be76ecda83..0000000000 --- a/netlink_msg.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "netlink_callbacks.h" -#include "netlink_msg.h" - -/* function: family_size - * returns the size of the address structure for the given family, or 0 on error - * family - AF_INET or AF_INET6 - */ -size_t inet_family_size(int family) { - if (family == AF_INET) { - return sizeof(struct in_addr); - } else if (family == AF_INET6) { - return sizeof(struct in6_addr); - } else { - return 0; - } -} - -/* function: nlmsg_alloc_generic - * allocates a netlink message with the given struct inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * payload_struct - pointer to a struct to add to netlink message - * payload_len - bytelength of structure - */ -struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, - size_t payload_len) { - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) { - return NULL; - } - - if ((sizeof(struct nl_msg) + payload_len) > msg->nm_size) { - nlmsg_free(msg); - return NULL; - } - - msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len); - msg->nm_nlh->nlmsg_flags = flags; - msg->nm_nlh->nlmsg_type = type; - - memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len); - - return msg; -} - -/* function: nlmsg_alloc_ifaddr - * allocates a netlink message with a struct ifaddrmsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * ifa - ifaddrmsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa) { - return nlmsg_alloc_generic(type, flags, ifa, sizeof(*ifa)); -} - -/* function: nlmsg_alloc_ifinfo - * allocates a netlink message with a struct ifinfomsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * ifi - ifinfomsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi) { - return nlmsg_alloc_generic(type, flags, ifi, sizeof(*ifi)); -} - -/* function: nlmsg_alloc_rtmsg - * allocates a netlink message with a struct rtmsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * rt - rtmsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt) { - return nlmsg_alloc_generic(type, flags, rt, sizeof(*rt)); -} - -/* function: netlink_set_kernel_only - * sets a socket to receive messages only from the kernel - * sock - socket to connect - */ -int netlink_set_kernel_only(struct nl_sock *nl_sk) { - struct sockaddr_nl addr = { AF_NETLINK, 0, 0, 0 }; - - if (!nl_sk) { - return -EFAULT; - } - - int sockfd = nl_socket_get_fd(nl_sk); - return connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); -} - -/* function: send_netlink_msg - * sends a netlink message, reads a response, and hands the response(s) to the callbacks - * msg - netlink message to send - * callbacks - callbacks to use on responses - */ -void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) { - struct nl_sock *nl_sk; - - nl_sk = nl_socket_alloc(); - if (!nl_sk) goto cleanup; - - if (nl_connect(nl_sk, NETLINK_ROUTE) != 0) goto cleanup; - - if (nl_send_auto_complete(nl_sk, msg) < 0) goto cleanup; - - if (netlink_set_kernel_only(nl_sk) < 0) goto cleanup; - - nl_recvmsgs(nl_sk, callbacks); - -cleanup: - if (nl_sk) nl_socket_free(nl_sk); -} - -/* function: send_ifaddrmsg - * sends a netlink/ifaddrmsg message and hands the responses to the callbacks - * type - netlink message type - * flags - netlink message flags - * ifa - ifaddrmsg to send - * callbacks - callbacks to use with the responses - */ -void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) { - struct nl_msg *msg = NULL; - - msg = nlmsg_alloc_ifaddr(type, flags, ifa); - if (!msg) return; - - send_netlink_msg(msg, callbacks); - - nlmsg_free(msg); -} - -/* function: netlink_sendrecv - * send a nl_msg and return an int status - only supports OK/ERROR responses - * msg - msg to send - */ -int netlink_sendrecv(struct nl_msg *msg) { - struct nl_cb *callbacks = NULL; - int retval = -EIO; - - callbacks = alloc_ack_callbacks(&retval); - if (!callbacks) { - return -ENOMEM; - } - - send_netlink_msg(msg, callbacks); - - nl_cb_put(callbacks); - - return retval; -} diff --git a/netlink_msg.h b/netlink_msg.h deleted file mode 100644 index 13e1f285e5..0000000000 --- a/netlink_msg.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * netlink_msg.h - send an ifaddrmsg/ifinfomsg via netlink - */ -#ifndef __NETLINK_IFMSG_H__ -#define __NETLINK_IFMSG_H__ - -size_t inet_family_size(int family); -struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa); -struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi); -struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt); -void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks); -void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks); -int netlink_sendrecv(struct nl_msg *msg); -int netlink_set_kernel_only(struct nl_sock *nl_sk); - -#endif diff --git a/setif.c b/setif.c deleted file mode 100644 index 184a46b24a..0000000000 --- a/setif.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setif.c - network interface configuration - */ -#include -#include -#include - -#include -#include -#include - -#include "logging.h" -#include "netlink_msg.h" - -#define DEBUG_OPTNAME(a) \ - case (a): { \ - optname = #a; \ - break; \ - } - -/* function: add_address - * adds an IP address to/from an interface, returns 0 on success and <0 on failure - * ifname - name of interface to change - * family - address family (AF_INET, AF_INET6) - * address - pointer to a struct in_addr or in6_addr - * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) - * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) - */ -int add_address(const char *ifname, int family, const void *address, int prefixlen, - const void *broadcast) { - int retval; - size_t addr_size; - struct ifaddrmsg ifa; - struct nl_msg *msg = NULL; - - addr_size = inet_family_size(family); - if (addr_size == 0) { - retval = -EAFNOSUPPORT; - goto cleanup; - } - - memset(&ifa, 0, sizeof(ifa)); - if (!(ifa.ifa_index = if_nametoindex(ifname))) { - retval = -ENODEV; - goto cleanup; - } - ifa.ifa_family = family; - ifa.ifa_prefixlen = prefixlen; - ifa.ifa_scope = RT_SCOPE_UNIVERSE; - - msg = - nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); - if (!msg) { - retval = -ENOMEM; - goto cleanup; - } - - if (nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if (family == AF_INET6) { - // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS - if (nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { - retval = -ENOMEM; - goto cleanup; - } - } else if (family == AF_INET) { - // AF_INET gets IFA_LOCAL + IFA_BROADCAST - if (nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { - retval = -ENOMEM; - goto cleanup; - } - } else { - retval = -EAFNOSUPPORT; - goto cleanup; - } - - retval = netlink_sendrecv(msg); - -cleanup: - if (msg) nlmsg_free(msg); - - return retval; -} - -/* function: if_up - * sets interface link state to up and sets mtu, returns 0 on success and <0 on failure - * ifname - interface name to change - * mtu - new mtu - */ -int if_up(const char *ifname, int mtu) { - int retval = -1; - struct ifinfomsg ifi; - struct nl_msg *msg = NULL; - - memset(&ifi, 0, sizeof(ifi)); - if (!(ifi.ifi_index = if_nametoindex(ifname))) { - retval = -ENODEV; - goto cleanup; - } - ifi.ifi_change = IFF_UP; - ifi.ifi_flags = IFF_UP; - - msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); - if (!msg) { - retval = -ENOMEM; - goto cleanup; - } - - if (nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { - retval = -ENOMEM; - goto cleanup; - } - - retval = netlink_sendrecv(msg); - -cleanup: - if (msg) nlmsg_free(msg); - - return retval; -} - -static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) { - struct ipv6_mreq mreq = { *addr, ifindex }; - char *optname; - int ret; - - switch (what) { - DEBUG_OPTNAME(IPV6_JOIN_ANYCAST) - DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST) - default: - optname = "???"; - break; - } - - ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq)); - if (ret) { - logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno)); - } - - return ret; -} - -/* function: add_anycast_address - * adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure - * sock - the socket to add the address to - * addr - the IP address to add - * ifname - name of interface to add the address to - */ -int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { - int ifindex; - - ifindex = if_nametoindex(ifname); - if (!ifindex) { - logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname); - return -ENODEV; - } - - return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex); -} - -/* function: del_anycast_address - * removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure - * sock - the socket to remove from, must have had the address added via add_anycast_address - * addr - the IP address to remove - */ -int del_anycast_address(int sock, struct in6_addr *addr) { - return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0); -} diff --git a/setif.h b/setif.h deleted file mode 100644 index 2d0adb11c2..0000000000 --- a/setif.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * setif.h - network interface configuration - */ -#ifndef __SETIF_H__ -#define __SETIF_H__ - -int add_address(const char *ifname, int family, const void *address, int cidr, - const void *broadcast); -int if_up(const char *ifname, int mtu); - -int add_anycast_address(int sock, const struct in6_addr *addr, const char *interface); -int del_anycast_address(int sock, const struct in6_addr *addr); - -#endif From 935a5ee67cab1567ecb24d53d90474d182d8ea5b Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Thu, 16 Dec 2021 12:56:36 +0800 Subject: [PATCH 227/261] [RFCLAT#11] remove libnetd_client_headers and netid_client.h Removed for reducing the libaray dependency. MARK_UNSET is going to be removed because the mark doesn't need to be an argument of cli now. libnetd_client_headers is not required as well. Bug: 212345928 Test: atest clatd_test Change-Id: Ia81287dcfa2a48a89187b21d4bc721570c0d1e0f --- Android.bp | 5 ----- clatd.c | 1 - main.c | 6 ++++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Android.bp b/Android.bp index 0eb5298e13..8d51b4924c 100644 --- a/Android.bp +++ b/Android.bp @@ -32,11 +32,6 @@ cc_defaults { // Bug: http://b/33566695 "-Wno-address-of-packed-member", ], - - // For MARK_UNSET. - header_libs: [ - "libnetd_client_headers" - ], } // Code used both by the daemon and by unit tests. diff --git a/clatd.c b/clatd.c index bb6aedccbc..f760fef05c 100644 --- a/clatd.c +++ b/clatd.c @@ -39,7 +39,6 @@ #include #include -#include // For MARK_UNSET. #include // For AID_CLAT. #include "clatd.h" diff --git a/main.c b/main.c index 74fb0c2cb2..46ab20d3cc 100644 --- a/main.c +++ b/main.c @@ -25,8 +25,6 @@ #include #include -#include // For MARK_UNSET. - #include "clatd.h" #include "common.h" #include "config.h" @@ -34,6 +32,10 @@ #define DEVICEPREFIX "v4-" +// Sync MARK_UNSET from netid_client.h +// TODO: remove MARK_UNSET definition once argument "-m" is removed. +#define MARK_UNSET 0u + /* function: stop_loop * signal handler: stop the event loop */ From 07125615386f27159dcca90f177ec867e602cdc5 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Thu, 16 Dec 2021 12:38:34 +0800 Subject: [PATCH 228/261] [RFCLAT#12] Add apex_available and min_sdk_version to clatd Used to package clatd binary into tethering apex. Bug: 212345928 Test: atest clatd_test Change-Id: I94f7272e088e8b16fa906585a8dfe430c6ba4631 --- Android.bp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Android.bp b/Android.bp index 8d51b4924c..aca84817e3 100644 --- a/Android.bp +++ b/Android.bp @@ -85,6 +85,12 @@ cc_binary { sanitize: { memtag_heap: true, }, + + apex_available: [ + "com.android.tethering", + "//apex_available:platform", + ], + min_sdk_version: "30", } // Unit tests. From 9e718f44be83fefbb42324f48edeb5a144922049 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 4 Jan 2022 22:48:54 +0800 Subject: [PATCH 229/261] [RFCLAT#13] remove clatd -m argument clatd doesn't need -m argument anymore because raw socket mark is applied in ClatdController now. fix the logging typo as well. Bug: 212345928 Test: atest clatd_test Change-Id: I49963c621468b9847a64091c10a1d9e7126ceba3 --- main.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/main.c b/main.c index 46ab20d3cc..3d1ff95d41 100644 --- a/main.c +++ b/main.c @@ -32,10 +32,6 @@ #define DEVICEPREFIX "v4-" -// Sync MARK_UNSET from netid_client.h -// TODO: remove MARK_UNSET definition once argument "-m" is removed. -#define MARK_UNSET 0u - /* function: stop_loop * signal handler: stop the event loop */ @@ -50,7 +46,6 @@ void print_help() { printf("-p [plat prefix]\n"); printf("-4 [IPv4 address]\n"); printf("-6 [IPv6 address]\n"); - printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); printf("-r [read socket descriptor number]\n"); printf("-w [write socket descriptor number]\n"); @@ -62,13 +57,12 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; + char *uplink_interface = NULL, *plat_prefix = NULL; char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *read_sock_str = NULL, *write_sock_str = NULL; - uint32_t mark = MARK_UNSET; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:r:w:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -82,9 +76,6 @@ int main(int argc, char **argv) { case '6': v6_addr = optarg; break; - case 'm': - mark_str = optarg; - break; case 't': tunfd_str = optarg; break; @@ -108,11 +99,6 @@ int main(int argc, char **argv) { exit(1); } - if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { - logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); - exit(1); - } - if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) { logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str); exit(1); @@ -123,7 +109,7 @@ int main(int argc, char **argv) { } if (read_sock_str != NULL && !parse_int(read_sock_str, &tunnel.read_fd6)) { - logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", read_sock_str); + logmsg(ANDROID_LOG_FATAL, "invalid read socket %s", read_sock_str); exit(1); } if (!tunnel.read_fd6) { @@ -132,7 +118,7 @@ int main(int argc, char **argv) { } if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { - logmsg(ANDROID_LOG_FATAL, "invalid sock_write %s", write_sock_str); + logmsg(ANDROID_LOG_FATAL, "invalid write socket %s", write_sock_str); exit(1); } if (!tunnel.write_fd6) { @@ -162,9 +148,8 @@ int main(int argc, char **argv) { exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", - CLATD_VERSION, uplink_interface, mark_str ? mark_str : "(none)", - plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s plat=%s v4=%s v6=%s", CLATD_VERSION, + uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); // run under a regular user with no capabilities From 98e86faad93b7a8cdbecebdbcbb0fb93fb68446c Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Wed, 5 Jan 2022 19:14:53 +0800 Subject: [PATCH 230/261] [RFCLAT#14] replace "libcutils" with "libcutils_headers" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit replacing "libcutils" with "libcutils_headers" can save the apex size ~356KiB (libbase.so + libcutils.so). clatd only needs the definition AID_CLAT in the libcutils exported headers. It would be more appropriate for using libcutils_headers. before: $ deapexer extract out/target/product/raven/system/apex/com.google.android.tethering.apex apex_with_clat_uses_libcutils $ ls -all out/target/product/raven/system/apex/com.google.android.tethering.apex -rw-r--r-- 1 nuccachen primarygroup 4083712 Jan 3 21:51 out/target/product/raven/system/apex/com.google.android.tethering.apex after: $ deapexer extract out/target/product/raven/system/apex/com.google.android.tethering.apex apex_with_clat_uses_libcutils_headers $ ls -all out/target/product/raven/system/apex/com.google.android.tethering.apex -rw-r--r-- 1 nuccachen primarygroup 3719168 Jan 5 18:40 out/target/product/raven/system/apex/com.google.android.tethering.apex Both libbase.so and libcutils.so are removed from the apex which is using clatd with libcutils_headers: $ diff -r apex_with_clat_uses_libcutils apex_with_clat_uses_libcutils_headers Only in apex_with_clat_uses_libcutils/lib64: libbase.so (263.4 kB) Only in apex_with_clat_uses_libcutils/lib64: libcutils.so (91.0 kB) Bug: 212345928 Test: atest clatd_test Change-Id: Ia286c1d50be758685e92a6e86faa57b675e9cc54 --- Android.bp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index aca84817e3..4e3c1626d7 100644 --- a/Android.bp +++ b/Android.bp @@ -53,6 +53,10 @@ filegroup { cc_binary { name: "clatd", defaults: ["clatd_defaults"], + // TODO: remove once drop_root_and_caps() is removed. + header_libs: [ + "libcutils_headers", // for AID_CLAT + ], srcs: [ ":clatd_common", "main.c" @@ -61,7 +65,6 @@ cc_binary { "libip_checksum", ], shared_libs: [ - "libcutils", "liblog", ], From 03486e0f358ed120b8ab13eb22ef29e108445616 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Fri, 7 Jan 2022 18:13:20 +0800 Subject: [PATCH 231/261] [RFCLAT#15] Static link libc++ Static link libc++ can save the apex size 700KiB+ (libc++.so). before: $ deapexer extract out/target/product/raven/system/apex/com.google.android.tethering.apex apex_default $ ls -all out/target/product/raven/system/apex/com.google.android.tethering.apex -rw-r--r-- 1 nuccachen primarygroup 3719168 Jan 5 18:40 out/target/product/raven/system/apex/com.google.android.tethering.apex after: $ deapexer extract out/target/product/raven/system/apex/com.google.android.tethering.apex apex_stl_static $ ls -all out/target/product/raven/system/apex/com.google.android.tethering.apex -rw-r--r-- 1 nuccachen primarygroup 2998272 Jan 6 22:50 out/target/product/raven/system/apex/com.google.android.tethering.apex libc++.so is removed from the apex which uses clatd with static link libc++: $ diff -r apex_default/ apex_stl_static/ Only in apex_default/lib64: libc++.so $ ls -all apex_default/lib64/libc++.so -rw-r--r-- 1 nuccachen primarygroup 716664 Jan 1 1970 libc++.so Bug: 212345928 Bug: 213123047 Test: atest clatd_test Change-Id: I7f71eac09e659355781ecd275fcc70bef262f3d0 --- Android.bp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Android.bp b/Android.bp index 4e3c1626d7..3ef01151da 100644 --- a/Android.bp +++ b/Android.bp @@ -68,6 +68,10 @@ cc_binary { "liblog", ], + // Static libc++ for smaller apex size while shipping clatd in the mainline module. + // See b/213123047 + stl: "libc++_static", + // Only enable clang-tidy for the daemon, not the tests, because enabling it for the // tests substantially increases build/compile cycle times and doesn't really provide a // security benefit. From ef9f62b0761836b2514ff1763faffd7a72e3dbd8 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 11 Jan 2022 15:51:28 +0800 Subject: [PATCH 232/261] Set relative_install_path to "for-system" Used for allocating clatd under $(apex_path)\bin\for-system\clatd Bug: 212345928 Test: manual test 1. Connect to ipv6-only wifi. 2. Try IPv4 traffic. $ ping 8.8.8.8 Change-Id: Ibfb83db738a67d266856966042486edc57473649 --- Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.bp b/Android.bp index 3ef01151da..70b525b251 100644 --- a/Android.bp +++ b/Android.bp @@ -67,6 +67,7 @@ cc_binary { shared_libs: [ "liblog", ], + relative_install_path: "for-system", // Static libc++ for smaller apex size while shipping clatd in the mainline module. // See b/213123047 From f752bb4bb71da2f4a5c84e7c190f99d6568daabf Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Fri, 14 Jan 2022 18:11:51 +0800 Subject: [PATCH 233/261] [RFCLAT#16] remove drop_root_and_caps gid and uid of clatd binary has been set by apex canned_fs_config. don't need drop_root_and_caps() anymore. Bug: 212345928 Test: manual test 1. Connect to ipv6-only wifi. 2. Make IPv4 traffic. $ ping 8.8.8.8 Check uid and uid is AID_CLAT(1029) in process status: $ adb shell ps | grep clat clat 9743 740 10826108 2264 0 0 S clatd-wlan0 $ adb shell cat /proc/9743/status | egrep -i '^(Uid|Gid|Groups):|^Cap' Uid: 0 1029 1029 1029 Gid: 0 1029 1029 1029 Groups: 3005 CapInh: 00000000000074ef CapPrm: 00000000000074ef CapEff: 0000000000000000 CapBnd: 00000000000074ef CapAmb: 0000000000000000 Change-Id: I8f7a9c50368d5c789621ea39b39d9242adfb0fcc --- Android.bp | 4 ---- clatd.c | 44 -------------------------------------------- clatd.h | 2 -- main.c | 3 --- 4 files changed, 53 deletions(-) diff --git a/Android.bp b/Android.bp index 70b525b251..38d45a0da4 100644 --- a/Android.bp +++ b/Android.bp @@ -53,10 +53,6 @@ filegroup { cc_binary { name: "clatd", defaults: ["clatd_defaults"], - // TODO: remove once drop_root_and_caps() is removed. - header_libs: [ - "libcutils_headers", // for AID_CLAT - ], srcs: [ ":clatd_common", "main.c" diff --git a/clatd.c b/clatd.c index f760fef05c..f72f4316a6 100644 --- a/clatd.c +++ b/clatd.c @@ -39,8 +39,6 @@ #include #include -#include // For AID_CLAT. - #include "clatd.h" #include "config.h" #include "dump.h" @@ -55,48 +53,6 @@ struct clat_config Global_Clatd_Config; volatile sig_atomic_t running = 1; -/* function: set_capability - * set the permitted, effective and inheritable capabilities of the current - * thread - */ -void set_capability(uint64_t target_cap) { - struct __user_cap_header_struct header = { - .version = _LINUX_CAPABILITY_VERSION_3, - .pid = 0 // 0 = change myself - }; - struct __user_cap_data_struct cap[_LINUX_CAPABILITY_U32S_3] = {}; - - cap[0].permitted = cap[0].effective = cap[0].inheritable = target_cap; - cap[1].permitted = cap[1].effective = cap[1].inheritable = target_cap >> 32; - - if (capset(&header, cap) < 0) { - logmsg(ANDROID_LOG_FATAL, "capset failed: %s", strerror(errno)); - exit(1); - } -} - -/* function: drop_root_and_caps - * drops root privs and all capabilities - */ -void drop_root_and_caps() { - // see man setgroups: this drops all supplementary groups - if (setgroups(0, NULL) < 0) { - logmsg(ANDROID_LOG_FATAL, "setgroups failed: %s", strerror(errno)); - exit(1); - } - - if (setresgid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { - logmsg(ANDROID_LOG_FATAL, "setresgid failed: %s", strerror(errno)); - exit(1); - } - if (setresuid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { - logmsg(ANDROID_LOG_FATAL, "setresuid failed: %s", strerror(errno)); - exit(1); - } - - set_capability(0); -} - int ipv6_address_changed(const char *interface) { union anyip *interface_ip; diff --git a/clatd.h b/clatd.h index be3c9c18eb..87cdbcf3ae 100644 --- a/clatd.h +++ b/clatd.h @@ -38,8 +38,6 @@ struct tun_data; extern volatile sig_atomic_t running; -void set_capability(uint64_t target_cap); -void drop_root_and_caps(); int ipv6_address_changed(const char *interface); void event_loop(struct tun_data *tunnel); diff --git a/main.c b/main.c index 3d1ff95d41..d9371dad36 100644 --- a/main.c +++ b/main.c @@ -152,9 +152,6 @@ int main(int argc, char **argv) { uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); - // run under a regular user with no capabilities - drop_root_and_caps(); - // 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)); From 07461b953267c37d427e2766f03b378ff9b59604 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Sun, 16 Jan 2022 17:27:35 +0800 Subject: [PATCH 234/261] Move vendor-464xlat.rc from external to netd Since clatd is modularized and shipped in apex, migrate the clat vendor property to netd. Bug: 182333299 Bug: 212345928 Test: manual Change-Id: I73b06547d8443abc749a4ccfe5c7b16b1ed32586 --- Android.bp | 4 ---- vendor-464xlat.rc | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 vendor-464xlat.rc diff --git a/Android.bp b/Android.bp index 70b525b251..47d22a12c3 100644 --- a/Android.bp +++ b/Android.bp @@ -86,10 +86,6 @@ cc_binary { tidy_flags: [ "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", ], - // Actually not required for clatd itself. See comments in the rc file. - init_rc: [ - "vendor-464xlat.rc", - ], sanitize: { memtag_heap: true, }, diff --git a/vendor-464xlat.rc b/vendor-464xlat.rc deleted file mode 100644 index 609531d3f9..0000000000 --- a/vendor-464xlat.rc +++ /dev/null @@ -1,15 +0,0 @@ -# Certain vendors disable 464xlat by setting a vendor property. -# The connectivity code in the Tethering APEX needs to disable -# 464xlat when the property is set, but it is only allowed to -# access non-vendor system properties. So copy the property to -# a property available to system APIs in android.sysprop. -# -# Arguably this script should live close to the code that uses -# it, but scrips in APEXes are not allowed to use "on property". -# So put it here close to clatd, which is at least related to -# 464xlat. -on property:persist.vendor.net.doxlat=true - setprop net.464xlat.cellular.enabled true - -on property:persist.vendor.net.doxlat=false - setprop net.464xlat.cellular.enabled false From 2f7e47837ace8b7c6e08a28c3f327f8f6ce98cf3 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Sun, 23 Jan 2022 11:48:25 +0800 Subject: [PATCH 235/261] Remove libnetdbpf_test from TEST_MAPPING libnetdbpf_test has nothing to do with clat Bug: 202086915 Test: m Change-Id: I6785827864b98ebabc56e57e418a7ecba39a405f --- TEST_MAPPING | 1 - 1 file changed, 1 deletion(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index 873ff2feb0..d36908adfc 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,7 +1,6 @@ { "presubmit": [ { "name": "clatd_test" }, - { "name": "libnetdbpf_test" }, { "name": "netd_integration_test" }, { "name": "netd_unit_test" }, { "name": "netdutils_test" }, From 0e175b7929fc511e5b157df632d143ac3e89446b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 8 Feb 2022 15:26:29 -0800 Subject: [PATCH 236/261] android-clat: switch OWNERS to core networking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (effectively a no-op, just more directly accomplishes the same thing) Test: N/A, TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: Ie0b9f0ed5e9dcc8c05e749171bddea81320a9481 --- OWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index eb9ff18231..62c5737a2e 100644 --- a/OWNERS +++ b/OWNERS @@ -1 +1,2 @@ -include platform/system/netd:/OWNERS +set noparent +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking From 3abac000c0c74e2ab2132089e7b4d6bd3f6fb2cc Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Mon, 4 Apr 2022 04:23:34 -0700 Subject: [PATCH 237/261] Suppress tidy checks that block migration to gnu11 The clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling check gave more warnings in gnu11 mode and they are not suitable for Android. This check is disabled in AOSP global default, and local projects should not enable it or should change source code to avoid those warnings. Use tidy_checks_as_errors instead of tidy_flags to specify the checks to be treated as errors. Bug: 227361697 Test: make with global default set to gnu11 Change-Id: I09f7aa562ab51d3f536cd48184d430212d67b51b --- Android.bp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index 7a8ba03459..982e656a82 100644 --- a/Android.bp +++ b/Android.bp @@ -77,10 +77,16 @@ cc_binary { "-*", "cert-*", "clang-analyzer-security*", + // b/2043314, warnings on memcpy_s, memset_s, snprintf_s calls + // are blocking the migration from gnu99 to gnu11. + // Until those warnings are fixed, disable these checks. + "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling", "android-*", ], - tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", + tidy_checks_as_errors: [ + "clang-analyzer-security*", + "cert-*", + "android-*", ], sanitize: { memtag_heap: true, From f6ec94ea69dd8b76d977ea2a68c2893a733e2806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 12 Jul 2022 16:17:33 -0700 Subject: [PATCH 238/261] send out IPv6 DAD packets when starting up clatd for the clat v6 address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we're telling the kernel this is an anycast ip [via setsockopt(sock, SOL_IPV6, IPV6_JOIN_ANYCAST)], it apparently doesn't perform DAD for us when adding the ip. From the kernel's perspective this behaviour makes absolute sense: an anycast'ed IP would normally be configured on multiple machines - possibly on the same L2 network segment - and thus DAD may well fail. For the CLAT use case we want to add the IP, so that the kernel handles IPv6 MLD and replies with NA to incoming NS, yet we don't want the kernel to 'own' the IP itself, as that would cause it to send out spurious TCP resets and icmp port unreachable messages. (Not to mention applications could be confused by the IP being configured on an interface: while it is *not* for direct application use) Basically we want something like 'proxy arp (nd)' for the clat ip... We should probably do it manually... but that's hard... As such this isn't a full DAD [duplicate address detection] implementation. We don't actually perform DAD, just like we didn't before this patch... We simply send the packets out to pretend we did. The chance of collision is super low, since the bottom 64-bits are picked randomly with 16 of them chosen to guarantee checksum neutrality. ie. we should have around 48 bits of entropy. That's a few hundred - few thousand in 2.8e14 chance of collision. (depending on how many ips are used on the L2 segment) I'm hoping this will be enough to resolve compatibility problems with some Cisco networking gear based ipv6-only wifi networks, where apparently the CLAT v6 address isn't ever neighbour solicited for by the router. I'm guessing it's some security feature: you didn't do DAD, so it's not your IP... Sample generated packet: b6:7a:01:a5:26:27 (oui Unknown) > 33:33:ff:54:ae:21 (oui Unknown), ethertype IPv6 (0x86dd), length 86: (hlim 255, next-header ICMPv6 (58) payload length: 32) :: > ff02::1:ff54:ae21: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has 2401:fa00:480:13d:2a65:4e6a:7554:ae21 unknown option (14), length 8 (1): 0x0000: 891b eaef 1263 0x0000: 6000 0000 0020 3aff 0000 0000 0000 0000 `.....:......... 0x0010: 0000 0000 0000 0000 ff02 0000 0000 0000 ................ 0x0020: 0000 0001 ff54 ae21 8700 77b6 0000 0000 .....T.!..w..... 0x0030: 2401 fa00 0480 013d 2a65 4e6a 7554 ae21 $......=*eNjuT.! 0x0040: 0e01 891b 434c 4154 ....CLAT Bug: 238387793 Test: TreeHugger, manually on bramble with ipv6-only wifi network Signed-off-by: Maciej Żenczykowski Change-Id: I16141bc427467e935da55f4eac6943659d8a65ad --- clatd.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/clatd.c b/clatd.c index f72f4316a6..4ce94f7ee5 100644 --- a/clatd.c +++ b/clatd.c @@ -40,6 +40,7 @@ #include #include "clatd.h" +#include "checksum.h" #include "config.h" #include "dump.h" #include "getaddr.h" @@ -123,11 +124,101 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen); } +// IPv6 DAD packet format: +// Ethernet header (if needed) will be added by the kernel: +// u8[6] src_mac; u8[6] dst_mac '33:33:ff:XX:XX:XX'; be16 ethertype '0x86DD' +// IPv6 header: +// be32 0x60000000 - ipv6, tclass 0, flowlabel 0 +// be16 payload_length '32'; u8 nxt_hdr ICMPv6 '58'; u8 hop limit '255' +// u128 src_ip6 '::' +// u128 dst_ip6 'ff02::1:ffXX:XXXX' +// ICMPv6 header: +// u8 type '135'; u8 code '0'; u16 icmp6 checksum; u32 reserved '0' +// ICMPv6 neighbour solicitation payload: +// u128 tgt_ip6 +// ICMPv6 ND options: +// u8 opt nr '14'; u8 length '1'; u8[6] nonce '6 random bytes' +void send_dad(int fd, const struct in6_addr* tgt, int count) { + struct { + struct ip6_hdr ip6h; + struct nd_neighbor_solicit ns; + uint8_t ns_opt_nr; + uint8_t ns_opt_len; + uint8_t ns_opt_nonce[6]; + } dad_pkt = { + .ip6h = { + .ip6_flow = htonl(6 << 28), // v6, 0 tclass, 0 flowlabel + .ip6_plen = htons(sizeof(dad_pkt) - sizeof(struct ip6_hdr)), // payload length, ie. 32 + .ip6_nxt = IPPROTO_ICMPV6, // 58 + .ip6_hlim = 255, + .ip6_src = {}, // :: + .ip6_dst.s6_addr = { + 0xFF, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 1, + 0xFF, tgt->s6_addr[13], tgt->s6_addr[14], tgt->s6_addr[15], + }, // ff02::1:ffXX:XXXX - multicast group address derived from bottom 24-bits of tgt + }, + .ns = { + .nd_ns_type = ND_NEIGHBOR_SOLICIT, // 135 + .nd_ns_code = 0, + .nd_ns_cksum = 0, // will be calculated later + .nd_ns_reserved = 0, + .nd_ns_target = *tgt, + }, + .ns_opt_nr = 14, // icmp6 option 'nonce' from RFC3971 + .ns_opt_len = 1, // in units of 8 bytes, including option nr and len + .ns_opt_nonce = { // 'opt_len' * 8 - [size of u8: opt nr] - [size of u8: opt len] = 6 bytes + 0, 0, 'C', 'L', 'A', 'T', + }, // first 2 bytes filled in below with random bytes, CLAT left to ease id in tcpdump + }; + arc4random_buf(&dad_pkt.ns_opt_nonce, 2); + + // 40 byte IPv6 header + 8 byte ICMPv6 header + 16 byte ipv6 target address + 8 byte nonce option + _Static_assert(sizeof(dad_pkt) == 40 + 8 + 16 + 8, "sizeof dad packet != 72"); + + // IPv6 header checksum is standard negated 16-bit one's complement sum over the icmpv6 pseudo + // header (which includes payload length, nextheader, and src/dst ip) and the icmpv6 payload. + // + // Src/dst ip immediately prefix the icmpv6 header itself, so can be handled along + // with the payload. We thus only need to manually account for payload len & next header. + // + // The magic '8' is simply the offset of the ip6_src field in the ipv6 header, + // ie. we're skipping over the ipv6 version, tclass, flowlabel, payload length, next header + // and hop limit fields, because they're not quite where we want them to be. + // + // ip6_plen is already in network order, while ip6_nxt is a single byte and thus needs htons(). + uint32_t csum = dad_pkt.ip6h.ip6_plen + htons(dad_pkt.ip6h.ip6_nxt); + csum = ip_checksum_add(csum, &dad_pkt.ip6h.ip6_src, sizeof(dad_pkt) - 8); + dad_pkt.ns.nd_ns_cksum = ip_checksum_finish(csum); + + const struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_addr = dad_pkt.ip6h.ip6_dst, + .sin6_scope_id = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), + }; + + while (count--) { + sendto(fd, &dad_pkt, sizeof(dad_pkt), 0 /*flags*/, (const struct sockaddr *)&dst, sizeof(dst)); + } +} + /* function: event_loop * reads packets from the tun network interface and passes them down the stack * tunnel - tun device data */ void event_loop(struct tun_data *tunnel) { + // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed, + // this would then result in an ipv6-only network with working native ipv6, working + // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps). + // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND). + // So we'll spoof dad here, and yeah, we really should check for a response and in + // case of failure pick a different IP. Seeing as 48-bits of the IP are utterly random + // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote + // concern... + // TODO: actually perform true DAD + send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, 2); + time_t last_interface_poll; struct pollfd wait_fd[] = { { tunnel->read_fd6, POLLIN, 0 }, From a1cb0f30dec63d02154bf131d74816f467214cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 19 Jul 2022 09:22:58 -0700 Subject: [PATCH 239/261] randomized clat dad nonce, send only one copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is to fully match observed behaviour of the Linux Kernel's DAD, and to make the resulting packets indistinguishable Bug: 238387793 Test: TreeHugger, manually on bramble with ipv6-only wifi network Signed-off-by: Maciej Żenczykowski Change-Id: I2f041c066bb85dd083fb2d6e137b0a1fed41af54 --- clatd.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/clatd.c b/clatd.c index 4ce94f7ee5..9c3424e64c 100644 --- a/clatd.c +++ b/clatd.c @@ -138,7 +138,7 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { // u128 tgt_ip6 // ICMPv6 ND options: // u8 opt nr '14'; u8 length '1'; u8[6] nonce '6 random bytes' -void send_dad(int fd, const struct in6_addr* tgt, int count) { +void send_dad(int fd, const struct in6_addr* tgt) { struct { struct ip6_hdr ip6h; struct nd_neighbor_solicit ns; @@ -168,11 +168,9 @@ void send_dad(int fd, const struct in6_addr* tgt, int count) { }, .ns_opt_nr = 14, // icmp6 option 'nonce' from RFC3971 .ns_opt_len = 1, // in units of 8 bytes, including option nr and len - .ns_opt_nonce = { // 'opt_len' * 8 - [size of u8: opt nr] - [size of u8: opt len] = 6 bytes - 0, 0, 'C', 'L', 'A', 'T', - }, // first 2 bytes filled in below with random bytes, CLAT left to ease id in tcpdump + .ns_opt_nonce = {}, // opt_len *8 - sizeof u8(opt_nr) - sizeof u8(opt_len) = 6 ranodmized bytes }; - arc4random_buf(&dad_pkt.ns_opt_nonce, 2); + arc4random_buf(&dad_pkt.ns_opt_nonce, sizeof(dad_pkt.ns_opt_nonce)); // 40 byte IPv6 header + 8 byte ICMPv6 header + 16 byte ipv6 target address + 8 byte nonce option _Static_assert(sizeof(dad_pkt) == 40 + 8 + 16 + 8, "sizeof dad packet != 72"); @@ -198,9 +196,7 @@ void send_dad(int fd, const struct in6_addr* tgt, int count) { .sin6_scope_id = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), }; - while (count--) { - sendto(fd, &dad_pkt, sizeof(dad_pkt), 0 /*flags*/, (const struct sockaddr *)&dst, sizeof(dst)); - } + sendto(fd, &dad_pkt, sizeof(dad_pkt), 0 /*flags*/, (const struct sockaddr *)&dst, sizeof(dst)); } /* function: event_loop @@ -217,7 +213,7 @@ void event_loop(struct tun_data *tunnel) { // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote // concern... // TODO: actually perform true DAD - send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, 2); + send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet); time_t last_interface_poll; struct pollfd wait_fd[] = { From a7bd4fcfd0db6dc3f37f545ca4e2626e8d6d62b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 25 Oct 2022 19:36:11 -0700 Subject: [PATCH 240/261] drop not needed sys/capability.h header includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I6b2051ef9aa0bee370fd5ae1bbff118a407bcaf2 --- clatd.c | 1 - main.c | 1 - 2 files changed, 2 deletions(-) diff --git a/clatd.c b/clatd.c index 9c3424e64c..307211f137 100644 --- a/clatd.c +++ b/clatd.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "clatd.h" diff --git a/main.c b/main.c index d9371dad36..2ab865edb2 100644 --- a/main.c +++ b/main.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "clatd.h" From 97de334faea3bba200c3f904222fa1e5e0ca269e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 18 Jan 2023 17:36:22 +0000 Subject: [PATCH 241/261] clat: slightly increase MAXMTU and add comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Signed-off-by: Maciej Żenczykowski   Change-Id: I7ef6d3a7c76a0c42f847d029a61f35832f913e6b --- clatd.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/clatd.h b/clatd.h index 87cdbcf3ae..e810a3b254 100644 --- a/clatd.h +++ b/clatd.h @@ -24,8 +24,25 @@ struct tun_data; -#define MAXMTU 65536 -#define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) +// IPv4 header has a u16 total length field, for maximum L3 mtu of 0xFFFF. +// +// Translating IPv4 to IPv6 requires removing the IPv4 header (20) and adding +// an IPv6 header (40), possibly with an extra ipv6 fragment extension header (8). +// +// As such the maximum IPv4 L3 mtu size is 0xFFFF (by u16 tot_len field) +// and the maximum IPv6 L3 mtu size is 0xFFFF + 28 (which is larger) +// +// A received non-jumbogram IPv6 frame could potentially be u16 payload_len = 0xFFFF +// + sizeof ipv6 header = 40, bytes in size. But such a packet cannot be meaningfully +// converted to IPv4 (it's too large). As such the restriction is the same: 0xFFFF + 28 +// +// (since there's no jumbogram support in IPv4, IPv6 jumbograms cannot be meaningfully +// converted to IPv4 anyway, and are thus entirely unsupported) +// +// We bump it by one more, since it makes truncation more obvious. +// ie. if we ever read >= MAXMTU bytes we should discard. +#define MAXMTU (0xFFFF + 28 + 1) +#define PACKETLEN (sizeof(struct tun_pi) + MAXMTU) #define CLATD_VERSION "1.5" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) From e6e0c009b9e2c3a815961f881b940440f5dd1687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 18 Jan 2023 23:57:35 +0000 Subject: [PATCH 242/261] clat: split read_packet in twain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does introduce some duplication, but this is to facilitate later changes to how AF_PACKET read works. Switching from read() to recvmsg(), processing cmsg's, etc... Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I8368d58d2cec88bafa688c38d63347fa898f3ad7 --- clatd.c | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/clatd.c b/clatd.c index 307211f137..be1a42c83c 100644 --- a/clatd.c +++ b/clatd.c @@ -76,35 +76,50 @@ int ipv6_address_changed(const char *interface) { } } -/* function: read_packet - * reads a packet from the tunnel fd and translates it - * read_fd - file descriptor to read original packet from - * write_fd - file descriptor to write translated packet to - * to_ipv6 - whether the packet is to be translated to ipv6 or ipv4 - */ -void read_packet(int read_fd, int write_fd, int to_ipv6) { - uint8_t buf[PACKETLEN]; - ssize_t readlen = read(read_fd, buf, PACKETLEN); +// reads L3 IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun +void process_packet_6_to_4(struct tun_data *tunnel) { + uint8_t buf[MAXMTU]; + ssize_t readlen = read(tunnel->read_fd6, buf, MAXMTU); if (readlen < 0) { if (errno != EAGAIN) { - logmsg(ANDROID_LOG_WARN, "read_packet/read error: %s", strerror(errno)); + logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno)); } return; } else if (readlen == 0) { - logmsg(ANDROID_LOG_WARN, "read_packet/tun interface removed"); + logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__); running = 0; return; + } else if (readlen >= MAXMTU) { + logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__); + return; } - if (!to_ipv6) { - translate_packet(write_fd, 0 /* to_ipv6 */, buf, readlen); + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf, readlen); +} + +// reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket +void process_packet_4_to_6(struct tun_data *tunnel) { + uint8_t buf[PACKETLEN]; + ssize_t readlen = read(tunnel->fd4, buf, PACKETLEN); + + if (readlen < 0) { + if (errno != EAGAIN) { + logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno)); + } + return; + } else if (readlen == 0) { + logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__); + running = 0; + return; + } else if (readlen >= PACKETLEN) { + logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__); return; } struct tun_pi *tun_header = (struct tun_pi *)buf; if (readlen < (ssize_t)sizeof(*tun_header)) { - logmsg(ANDROID_LOG_WARN, "read_packet/short read: got %ld bytes", readlen); + logmsg(ANDROID_LOG_WARN, "%s: short read: got %ld bytes", __func__, readlen); return; } @@ -120,7 +135,7 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { uint8_t *packet = (uint8_t *)(tun_header + 1); readlen -= sizeof(*tun_header); - translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen); + translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, packet, readlen); } // IPv6 DAD packet format: @@ -229,13 +244,13 @@ void event_loop(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno)); } } else { - // Call read_packet if the socket has data to be read, but also if an + // Call process_packet if the socket has data to be read, but also if an // error is waiting. If we don't call read() after getting POLLERR, a // subsequent poll() will return immediately with POLLERR again, // causing this code to spin in a loop. Calling read() will clear the // socket error flag instead. - if (wait_fd[0].revents) read_packet(tunnel->read_fd6, tunnel->fd4, 0 /* to_ipv6 */); - if (wait_fd[1].revents) read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); + if (wait_fd[0].revents) process_packet_6_to_4(tunnel); + if (wait_fd[1].revents) process_packet_4_to_6(tunnel); } time_t now = time(NULL); From bea3f7f9819bfc2007dbc2e60e5d2588b333d615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 30 Jan 2023 22:29:59 +0000 Subject: [PATCH 243/261] clatd: introduce MAXDUMPLEN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PACKETLEN is a bad name, let's try to move away from it Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I56e890d479bc64ed6c31a29772a370f0e83a2ff9 --- clatd.h | 4 ++++ dump.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clatd.h b/clatd.h index e810a3b254..4be071cbcb 100644 --- a/clatd.h +++ b/clatd.h @@ -43,6 +43,10 @@ struct tun_data; // ie. if we ever read >= MAXMTU bytes we should discard. #define MAXMTU (0xFFFF + 28 + 1) #define PACKETLEN (sizeof(struct tun_pi) + MAXMTU) + +// logcat_hexdump() maximum binary data length +#define MAXDUMPLEN PACKETLEN + #define CLATD_VERSION "1.5" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/dump.c b/dump.c index 289f161742..dff3d5ec8d 100644 --- a/dump.c +++ b/dump.c @@ -226,11 +226,11 @@ void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const uint8_ /* generic hex dump */ void logcat_hexdump(const char *info, const uint8_t *data, size_t len) { - char output[PACKETLEN * 3 + 2]; + char output[MAXDUMPLEN * 3 + 2]; size_t i; output[0] = '\0'; - for (i = 0; i < len && i < PACKETLEN; i++) { + for (i = 0; i < len && i < MAXDUMPLEN; i++) { snprintf(output + i * 3, 4, " %02x", data[i]); } output[len * 3 + 3] = '\0'; From 88cb78e3937687e42b4d92643b17281c9677d7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 30 Jan 2023 22:30:55 +0000 Subject: [PATCH 244/261] clatd: bump version number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit let's also print the running kernel's version, since that is actually very relevant Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: Icb9314df53f395d5efa38d4f68f8a7f0b0e71372 --- clatd.h | 2 +- main.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/clatd.h b/clatd.h index 4be071cbcb..cfcb253f1d 100644 --- a/clatd.h +++ b/clatd.h @@ -47,7 +47,7 @@ struct tun_data; // logcat_hexdump() maximum binary data length #define MAXDUMPLEN PACKETLEN -#define CLATD_VERSION "1.5" +#define CLATD_VERSION "1.6" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/main.c b/main.c index 2ab865edb2..11da65bec9 100644 --- a/main.c +++ b/main.c @@ -19,9 +19,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include "clatd.h" @@ -151,6 +154,34 @@ int main(int argc, char **argv) { uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)"); + { + // Compile time detection of 32 vs 64-bit build. (note: C does not have 'constexpr') + // Avoid use of preprocessor macros to get compile time syntax checking even on 64-bit. + const int user_bits = sizeof(void*) * 8; + const bool user32 = (user_bits == 32); + + // Note that on 64-bit all this personality related code simply compile optimizes out. + // 32-bit: fetch current personality (see 'man personality': 0xFFFFFFFF means retrieve only) + // On Linux fetching personality cannot fail. + const int prev_personality = user32 ? personality(0xFFFFFFFFuL) : PER_LINUX; + // 32-bit: attempt to get rid of kernel spoofing of 'uts.machine' architecture, + // In theory this cannot fail, as PER_LINUX should always be supported. + if (user32) (void)personality((prev_personality & ~PER_MASK) | PER_LINUX); + // 64-bit: this will compile time evaluate to false. + const bool was_linux32 = (prev_personality & PER_MASK) == PER_LINUX32; + + struct utsname uts = {}; + if (uname(&uts)) exit(1); // only possible error is EFAULT, but 'uts' is on stack + + // sysname is likely 'Linux', release is 'kver', machine is kernel's *true* architecture + logmsg(ANDROID_LOG_INFO, "%d-bit userspace on %s kernel %s for %s%s.", user_bits, + uts.sysname, uts.release, uts.machine, was_linux32 ? " (was spoofed)" : ""); + + // 32-bit: try to return to the 'default' personality + // In theory this cannot fail, because it was already previously in use. + if (user32) (void)personality(prev_personality); + } + // 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)); From fe7a167737e14d7002d1528ac2b91ac70131173d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 17 Jan 2023 21:28:22 +0000 Subject: [PATCH 245/261] clat: enable PACKET_AUXDATA cmsg processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: flashed on a bluejay, ping 1.1.1.1 via v4-wlan0 works Bug: 259872525 Signed-off-by: Maciej Żenczykowski Change-Id: I1288ba354cd02ba68b88c1169481140a53d1fa12 --- clatd.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index be1a42c83c..7eb7904821 100644 --- a/clatd.c +++ b/clatd.c @@ -78,8 +78,19 @@ int ipv6_address_changed(const char *interface) { // reads L3 IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun void process_packet_6_to_4(struct tun_data *tunnel) { + char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; uint8_t buf[MAXMTU]; - ssize_t readlen = read(tunnel->read_fd6, buf, MAXMTU); + struct iovec iov = { + .iov_base = buf, + .iov_len = MAXMTU, + }; + struct msghdr msgh = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf), + }; + ssize_t readlen = recvmsg(tunnel->read_fd6, &msgh, /*flags*/ 0); if (readlen < 0) { if (errno != EAGAIN) { @@ -95,6 +106,21 @@ void process_packet_6_to_4(struct tun_data *tunnel) { return; } + __u32 tp_status = 0; + + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { + struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); + tp_status = aux->tp_status; + break; + } + } + + // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum + if (tp_status & TP_STATUS_CSUMNOTREADY) { + logmsg(ANDROID_LOG_WARN, "read_packet checksum not ready"); + } + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf, readlen); } From 1f395ef0818211be563c42b8e958b062ed18ef6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 16 Feb 2023 05:11:54 +0000 Subject: [PATCH 246/261] clatd: log checksum issues only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Bug: 269387841 Signed-off-by: Maciej Żenczykowski Change-Id: Icb6ea6d801f5234ea51b4cad25f7b8b17a541977 --- clatd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clatd.c b/clatd.c index 7eb7904821..98f51953a4 100644 --- a/clatd.c +++ b/clatd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -118,7 +119,11 @@ void process_packet_6_to_4(struct tun_data *tunnel) { // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum if (tp_status & TP_STATUS_CSUMNOTREADY) { - logmsg(ANDROID_LOG_WARN, "read_packet checksum not ready"); + static bool logged = false; + if (!logged) { + logmsg(ANDROID_LOG_WARN, "read_packet checksum not ready"); + logged = true; + } } translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf, readlen); From f3eeff99df06d1370a71706dfd0f0a90202c47cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 13 Mar 2023 21:50:01 +0000 Subject: [PATCH 247/261] clatd: rework buffer sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function internal implementation details of the true required number of bytes for these buffers doesn't really belong in the header file. Test: TreeHugger Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I1aef7adc4c8af9b53d2c0b8da0eb2c1c6e6dd12a --- clatd.c | 12 ++++++------ clatd.h | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/clatd.c b/clatd.c index 98f51953a4..a089157a34 100644 --- a/clatd.c +++ b/clatd.c @@ -80,10 +80,10 @@ int ipv6_address_changed(const char *interface) { // reads L3 IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun void process_packet_6_to_4(struct tun_data *tunnel) { char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; - uint8_t buf[MAXMTU]; + uint8_t buf[MAXMTU + 1]; // +1 to make packet truncation obvious struct iovec iov = { .iov_base = buf, - .iov_len = MAXMTU, + .iov_len = sizeof(buf), }; struct msghdr msgh = { .msg_iov = &iov, @@ -102,7 +102,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__); running = 0; return; - } else if (readlen >= MAXMTU) { + } else if (readlen >= sizeof(buf)) { logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__); return; } @@ -131,8 +131,8 @@ void process_packet_6_to_4(struct tun_data *tunnel) { // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket void process_packet_4_to_6(struct tun_data *tunnel) { - uint8_t buf[PACKETLEN]; - ssize_t readlen = read(tunnel->fd4, buf, PACKETLEN); + uint8_t buf[sizeof(struct tun_pi) + MAXMTU + 1]; // +1 to make packet truncation obvious + ssize_t readlen = read(tunnel->fd4, buf, sizeof(buf)); if (readlen < 0) { if (errno != EAGAIN) { @@ -143,7 +143,7 @@ void process_packet_4_to_6(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__); running = 0; return; - } else if (readlen >= PACKETLEN) { + } else if (readlen >= sizeof(buf)) { logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__); return; } diff --git a/clatd.h b/clatd.h index cfcb253f1d..59f78abcf1 100644 --- a/clatd.h +++ b/clatd.h @@ -38,14 +38,15 @@ struct tun_data; // // (since there's no jumbogram support in IPv4, IPv6 jumbograms cannot be meaningfully // converted to IPv4 anyway, and are thus entirely unsupported) -// -// We bump it by one more, since it makes truncation more obvious. -// ie. if we ever read >= MAXMTU bytes we should discard. -#define MAXMTU (0xFFFF + 28 + 1) -#define PACKETLEN (sizeof(struct tun_pi) + MAXMTU) +#define MAXMTU (0xFFFF + 28) -// logcat_hexdump() maximum binary data length -#define MAXDUMPLEN PACKETLEN +// logcat_hexdump() maximum binary data length, this is the maximum packet size +// plus some extra space for various headers: +// struct tun_pi (4 bytes) +// struct virtio_net_hdr (10 bytes) +// ethernet (14 bytes), potentially including vlan tag (4) or tags (8 or 12) +// plus some extra just-in-case headroom, because it doesn't hurt. +#define MAXDUMPLEN (64 + MAXMTU) #define CLATD_VERSION "1.6" From 4e764176509ec498b0afa841a177aec4a35de7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Mon, 13 Mar 2023 21:55:54 +0000 Subject: [PATCH 248/261] clatd: use tpacket_auxdata->tp_net L3 header offset information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger, ping 1.1.1.1 on v6-only wifi Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I9d4bc095021addd07b80ed8b3928bc336382a210 --- clatd.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/clatd.c b/clatd.c index a089157a34..c5ab952c50 100644 --- a/clatd.c +++ b/clatd.c @@ -79,12 +79,15 @@ int ipv6_address_changed(const char *interface) { // reads L3 IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun void process_packet_6_to_4(struct tun_data *tunnel) { - char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; - uint8_t buf[MAXMTU + 1]; // +1 to make packet truncation obvious + // ethernet header is 14 bytes, plus 4 for a normal VLAN tag or 8 for Q-in-Q + // we don't really support vlans (or especially Q-in-Q)... + // but a few bytes of extra buffer space doesn't hurt... + uint8_t buf[22 + MAXMTU + 1]; // +1 to make packet truncation obvious struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf), }; + char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; struct msghdr msgh = { .msg_iov = &iov, .msg_iovlen = 1, @@ -108,15 +111,23 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } __u32 tp_status = 0; + __u16 tp_net = 0; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); tp_status = aux->tp_status; + tp_net = aux->tp_net; break; } } + if (readlen < tp_net) { + logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %u L2 header", + __func__, readlen, tp_net); + return; + } + // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum if (tp_status & TP_STATUS_CSUMNOTREADY) { static bool logged = false; @@ -126,7 +137,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } } - translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf, readlen); + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf + tp_net, readlen - tp_net); } // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket From cfa100f7e21f3a3ed0a2f230d6c08dcf2f9c7833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 14 Mar 2023 03:04:50 +0000 Subject: [PATCH 249/261] clatd: embed struct tun_pi directly in buf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I2bacb761f854ede558b701f2c3d8dc0c65a2da97 --- clatd.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/clatd.c b/clatd.c index c5ab952c50..96611d1613 100644 --- a/clatd.c +++ b/clatd.c @@ -142,8 +142,12 @@ void process_packet_6_to_4(struct tun_data *tunnel) { // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket void process_packet_4_to_6(struct tun_data *tunnel) { - uint8_t buf[sizeof(struct tun_pi) + MAXMTU + 1]; // +1 to make packet truncation obvious - ssize_t readlen = read(tunnel->fd4, buf, sizeof(buf)); + struct { + struct tun_pi pi; + uint8_t payload[MAXMTU]; + char pad; // +1 byte to make packet truncation obvious + } buf; + ssize_t readlen = read(tunnel->fd4, &buf, sizeof(buf)); if (readlen < 0) { if (errno != EAGAIN) { @@ -159,25 +163,23 @@ void process_packet_4_to_6(struct tun_data *tunnel) { return; } - struct tun_pi *tun_header = (struct tun_pi *)buf; - if (readlen < (ssize_t)sizeof(*tun_header)) { + if (readlen < (ssize_t)sizeof(buf.pi)) { logmsg(ANDROID_LOG_WARN, "%s: short read: got %ld bytes", __func__, readlen); return; } - uint16_t proto = ntohs(tun_header->proto); + uint16_t proto = ntohs(buf.pi.proto); if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); return; } - if (tun_header->flags != 0) { - logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); + if (buf.pi.flags != 0) { + logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, buf.pi.flags); } - uint8_t *packet = (uint8_t *)(tun_header + 1); - readlen -= sizeof(*tun_header); - translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, packet, readlen); + readlen -= sizeof(buf.pi); + translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, buf.payload, readlen); } // IPv6 DAD packet format: From 206b238eb9ac4d634fa2bd515819a0b480bab067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 14 Mar 2023 03:50:28 +0000 Subject: [PATCH 250/261] clatd: use struct instead of direct u8[] for buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (this will allow adding more headers) Note the ethernet header may or may not be present, so it has to stay as part of the payload. Test: TreeHugger Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I4e64fd1d15ac26ebb7a41ee9884b506625018fda --- clatd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index 96611d1613..284a676057 100644 --- a/clatd.c +++ b/clatd.c @@ -77,14 +77,17 @@ int ipv6_address_changed(const char *interface) { } } -// reads L3 IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun +// reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun void process_packet_6_to_4(struct tun_data *tunnel) { // ethernet header is 14 bytes, plus 4 for a normal VLAN tag or 8 for Q-in-Q // we don't really support vlans (or especially Q-in-Q)... // but a few bytes of extra buffer space doesn't hurt... - uint8_t buf[22 + MAXMTU + 1]; // +1 to make packet truncation obvious + struct { + uint8_t payload[22 + MAXMTU]; + char pad; // +1 to make packet truncation obvious + } buf; struct iovec iov = { - .iov_base = buf, + .iov_base = &buf, .iov_len = sizeof(buf), }; char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; @@ -137,7 +140,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } } - translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf + tp_net, readlen - tp_net); + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, readlen - tp_net); } // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket From 8eb4888e0ed98f81c3157dec8de2faf5339e2d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 14 Mar 2023 03:55:22 +0000 Subject: [PATCH 251/261] clatd: skip over virtio_net_hdr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger, ping 1.1.1.1 on ipv6-only wifi works Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I88ab5381bd144633ae3d3117c3b340cc2cb0b5fe --- clatd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clatd.c b/clatd.c index 284a676057..e10bda11bd 100644 --- a/clatd.c +++ b/clatd.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { // we don't really support vlans (or especially Q-in-Q)... // but a few bytes of extra buffer space doesn't hurt... struct { + struct virtio_net_hdr vnet; uint8_t payload[22 + MAXMTU]; char pad; // +1 to make packet truncation obvious } buf; @@ -125,7 +127,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } } - if (readlen < tp_net) { + if (readlen < sizeof(struct virtio_net_hdr) + tp_net) { logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %u L2 header", __func__, readlen, tp_net); return; @@ -140,7 +142,7 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } } - translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, readlen - tp_net); + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, readlen - sizeof(struct virtio_net_hdr) - tp_net); } // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket From aae2fb956c405449ba76827fa558ef440bd890f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 15 Mar 2023 00:51:54 +0000 Subject: [PATCH 252/261] clatd: perform checksum calculation when required MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: Ia0188a34d26eeffadbdbfd5a9c8fe3427f4eba98 --- clatd.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/clatd.c b/clatd.c index e10bda11bd..e7f9899776 100644 --- a/clatd.c +++ b/clatd.c @@ -128,21 +128,39 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } if (readlen < sizeof(struct virtio_net_hdr) + tp_net) { - logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %u L2 header", - __func__, readlen, tp_net); + logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %zu+%u L2 header", + __func__, readlen, sizeof(struct virtio_net_hdr), tp_net); return; } + const int pkt_len = readlen - sizeof(struct virtio_net_hdr); + // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum if (tp_status & TP_STATUS_CSUMNOTREADY) { static bool logged = false; if (!logged) { - logmsg(ANDROID_LOG_WARN, "read_packet checksum not ready"); + logmsg(ANDROID_LOG_WARN, "%s: L4 checksum calculation required", __func__); logged = true; } + + // These are non-negative by virtue of csum_start/offset being u16 + const int cs_start = buf.vnet.csum_start; + const int cs_offset = cs_start + buf.vnet.csum_offset; + if (cs_start > pkt_len) { + logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum start %d > %d", + __func__, cs_start, pkt_len); + } else if (cs_offset + 1 >= pkt_len) { + logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum offset %d + 1 >= %d", + __func__, cs_offset, pkt_len); + } else { + uint16_t csum = ip_checksum(buf.payload + cs_start, pkt_len - cs_start); + if (!csum) csum = 0xFFFF; // required fixup for UDP, TCP must live with it + buf.payload[cs_offset] = csum & 0xFF; + buf.payload[cs_offset + 1] = csum >> 8; + } } - translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, readlen - sizeof(struct virtio_net_hdr) - tp_net); + translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, pkt_len - tp_net); } // reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket From e4aa7b087923181ad243e71408a0df81ca9386ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 15 Mar 2023 17:08:57 +0000 Subject: [PATCH 253/261] clatd: remove spurious MTU_DELTA define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: Ic7e24b6b14a74d0a8ef994c2831d5fed42b04edb --- clatd.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/clatd.c b/clatd.c index e10bda11bd..21ec0d775e 100644 --- a/clatd.c +++ b/clatd.c @@ -50,9 +50,6 @@ struct clat_config Global_Clatd_Config; -/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */ -#define MTU_DELTA 28 - volatile sig_atomic_t running = 1; int ipv6_address_changed(const char *interface) { From a4df1010f69d1cff2a38e4f7aafb45dfc438cd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 16 Mar 2023 02:42:37 +0000 Subject: [PATCH 254/261] clatd: use offsetof MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I51fc4c669657042c120d1b8a4d35add6b51b4139 --- clatd.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/clatd.c b/clatd.c index e7f9899776..d1bfa5e69f 100644 --- a/clatd.c +++ b/clatd.c @@ -127,13 +127,14 @@ void process_packet_6_to_4(struct tun_data *tunnel) { } } - if (readlen < sizeof(struct virtio_net_hdr) + tp_net) { - logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %zu+%u L2 header", - __func__, readlen, sizeof(struct virtio_net_hdr), tp_net); + const int payload_offset = offsetof(typeof(buf), payload); + if (readlen < payload_offset + tp_net) { + logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %d+%u L2 header", + __func__, readlen, payload_offset, tp_net); return; } - const int pkt_len = readlen - sizeof(struct virtio_net_hdr); + const int pkt_len = readlen - payload_offset; // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum if (tp_status & TP_STATUS_CSUMNOTREADY) { @@ -186,11 +187,15 @@ void process_packet_4_to_6(struct tun_data *tunnel) { return; } - if (readlen < (ssize_t)sizeof(buf.pi)) { + const int payload_offset = offsetof(typeof(buf), payload); + + if (readlen < payload_offset) { logmsg(ANDROID_LOG_WARN, "%s: short read: got %ld bytes", __func__, readlen); return; } + const int pkt_len = readlen - payload_offset; + uint16_t proto = ntohs(buf.pi.proto); if (proto != ETH_P_IP) { logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto); @@ -201,8 +206,7 @@ void process_packet_4_to_6(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, buf.pi.flags); } - readlen -= sizeof(buf.pi); - translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, buf.payload, readlen); + translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, buf.payload, pkt_len); } // IPv6 DAD packet format: From ca9466c76809f6bec044669dc2ffb35552e6b404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 15 Mar 2023 17:40:45 +0000 Subject: [PATCH 255/261] clatd: remove ipv6 address monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code/logic is spurious, because the majority of packets are translated by eBPF, so whether the daemon runs or not, usually just doesn't matter - bpf will handle them anyway. As such what we actually care about is that the daemon is terminated at the same time as the bpf map configuration is removed. This is done by jni native code stopClatdProcess() which is called from ClatCoordinator java code immediately after maybeStopBpf(). Additionally on ipv6 address change we don't even terminate the daemon - we simply stop the event loop, but still block in main() until we receive SIGTERM/SIGKILL. (This was done a while ago to fix a bunch of issues with daemon dieing and being killed racing with each other) Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: I86d203a244838166ed1fbec1bc48a680e433f9fa --- Android.bp | 1 - clatd.c | 38 +---------- clatd.h | 7 -- clatd_test.cpp | 53 ---------------- getaddr.c | 169 ------------------------------------------------- getaddr.h | 30 --------- 6 files changed, 1 insertion(+), 297 deletions(-) delete mode 100644 getaddr.c delete mode 100644 getaddr.h diff --git a/Android.bp b/Android.bp index 982e656a82..983ee00d52 100644 --- a/Android.bp +++ b/Android.bp @@ -40,7 +40,6 @@ filegroup { srcs: [ "clatd.c", "dump.c", - "getaddr.c", "icmp.c", "ipv4.c", "ipv6.c", diff --git a/clatd.c b/clatd.c index d1bfa5e69f..7cee4e8eec 100644 --- a/clatd.c +++ b/clatd.c @@ -44,7 +44,6 @@ #include "checksum.h" #include "config.h" #include "dump.h" -#include "getaddr.h" #include "logging.h" #include "translate.h" @@ -55,29 +54,6 @@ struct clat_config Global_Clatd_Config; volatile sig_atomic_t running = 1; -int ipv6_address_changed(const char *interface) { - union anyip *interface_ip; - - interface_ip = getinterface_ip(interface, AF_INET6); - if (!interface_ip) { - logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); - return 1; - } - - if (!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) { - char oldstr[INET6_ADDRSTRLEN]; - char newstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, oldstr, sizeof(oldstr)); - inet_ntop(AF_INET6, &interface_ip->ip6, newstr, sizeof(newstr)); - logmsg(ANDROID_LOG_INFO, "IPv6 prefix on %s changed: %s -> %s", interface, oldstr, newstr); - free(interface_ip); - return 1; - } else { - free(interface_ip); - return 0; - } -} - // reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun void process_packet_6_to_4(struct tun_data *tunnel) { // ethernet header is 14 bytes, plus 4 for a normal VLAN tag or 8 for Q-in-Q @@ -300,17 +276,13 @@ void event_loop(struct tun_data *tunnel) { // TODO: actually perform true DAD send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet); - time_t last_interface_poll; struct pollfd wait_fd[] = { { tunnel->read_fd6, POLLIN, 0 }, { tunnel->fd4, POLLIN, 0 }, }; - // start the poll timer - last_interface_poll = time(NULL); - while (running) { - if (poll(wait_fd, ARRAY_SIZE(wait_fd), NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) { + if (poll(wait_fd, ARRAY_SIZE(wait_fd), -1) == -1) { if (errno != EINTR) { logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno)); } @@ -323,13 +295,5 @@ void event_loop(struct tun_data *tunnel) { if (wait_fd[0].revents) process_packet_6_to_4(tunnel); if (wait_fd[1].revents) process_packet_4_to_6(tunnel); } - - time_t now = time(NULL); - if (now >= (last_interface_poll + INTERFACE_POLL_FREQUENCY)) { - last_interface_poll = now; - if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) { - break; - } - } } } diff --git a/clatd.h b/clatd.h index 59f78abcf1..85e2c493bb 100644 --- a/clatd.h +++ b/clatd.h @@ -52,15 +52,8 @@ struct tun_data; #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -// how frequently (in seconds) to poll for an address change while traffic is passing -#define INTERFACE_POLL_FREQUENCY 30 - -// how frequently (in seconds) to poll for an address change while there is no traffic -#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90 - extern volatile sig_atomic_t running; -int ipv6_address_changed(const char *interface); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/clatd_test.cpp b/clatd_test.cpp index 8eef738461..94ae0b5783 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -33,7 +33,6 @@ extern "C" { #include "checksum.h" #include "clatd.h" #include "config.h" -#include "getaddr.h" #include "translate.h" } @@ -836,55 +835,3 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20, "UDP/IPv4 -> UDP/IPv6 checksum neutral"); } - -TEST_F(ClatdTest, GetInterfaceIpV4) { - TunInterface v4Iface; - ASSERT_EQ(0, v4Iface.init()); - EXPECT_EQ(0, v4Iface.addAddress("192.0.2.1", 32)); - - union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); - ASSERT_NE(nullptr, ip); - EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); - free(ip); - - v4Iface.destroy(); -} - -TEST_F(ClatdTest, GetInterfaceIpV6) { - union anyip *ip = getinterface_ip(sTun.name().c_str(), AF_INET6); - ASSERT_NE(nullptr, ip); - in6_addr expected = sTun.srcAddr(); - in6_addr actual = ip->ip6; - expect_ipv6_addr_equal(&expected, &actual); -} - -TEST_F(ClatdTest, Ipv6AddressChanged) { - // Configure the clat IPv6 address. - const char *ifname = sTun.name().c_str(); - - in6_addr myaddr = sTun.srcAddr(); - gen_random_iid(&myaddr, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet); - char addrstr[INET6_ADDRSTRLEN]; - ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); - - Global_Clatd_Config.ipv6_local_subnet = myaddr; - EXPECT_EQ(0, ipv6_address_changed(ifname)); - EXPECT_EQ(0, ipv6_address_changed(ifname)); - - // Change the IP address on the tun interface to a new prefix. - char srcaddr[INET6_ADDRSTRLEN]; - char dstaddr[INET6_ADDRSTRLEN]; - ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.srcAddr(), srcaddr, sizeof(srcaddr))); - ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.dstAddr(), dstaddr, sizeof(dstaddr))); - EXPECT_EQ(0, ifc_del_address(ifname, srcaddr, 64)); - EXPECT_EQ(0, ifc_del_address(ifname, dstaddr, 64)); - - // Check that we can tell that the address has changed. - EXPECT_EQ(0, ifc_add_address(ifname, "2001:db8::1:2", 64)); - EXPECT_EQ(1, ipv6_address_changed(ifname)); - EXPECT_EQ(1, ipv6_address_changed(ifname)); - - // Restore the tun interface configuration. - sTun.destroy(); - ASSERT_EQ(0, sTun.init()); -} diff --git a/getaddr.c b/getaddr.c deleted file mode 100644 index ba8052d16f..0000000000 --- a/getaddr.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2012 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * getaddr.c - get a locally configured address - */ -#include "getaddr.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logging.h" - -// Kernel suggests that keep the packet under 8KiB (NLMSG_GOODSIZE) in include/linux/netlink.h. -#define NLMSG_SIZE 8192 - -// shared state between getinterface_ip and parse_ifaddrmsg -// TODO: refactor the communication between getinterface_ip and parse_ifaddrmsg because there -// is no netlink callback anymore. -struct target { - int family; - unsigned int ifindex; - union anyip ip; - int foundip; -}; - -/* function: parse_ifaddrmsg - * parse ifaddrmsg for getinterface_ip - * nh - netlink message header - * targ_p - (struct target) info for which address we're looking for - * and the parsed result if any. - */ -static void parse_ifaddrmsg(struct nlmsghdr *nh, struct target *targ_p) { - struct ifaddrmsg *ifa_p; - struct rtattr *rta_p; - int rta_len; - - ifa_p = (struct ifaddrmsg *)NLMSG_DATA(nh); - rta_p = (struct rtattr *)IFA_RTA(ifa_p); - - if (ifa_p->ifa_index != targ_p->ifindex) return; - - if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return; - - rta_len = IFA_PAYLOAD(nh); - 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; - } - 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; - } - break; - } - } -} - -void sendrecv_ifaddrmsg(struct target *targ_p) { - int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE); - if (s < 0) { - logmsg(ANDROID_LOG_ERROR, "open NETLINK_ROUTE socket failed %s", strerror(errno)); - return; - } - - // Fill in netlink structures. - struct { - struct nlmsghdr n; - struct ifaddrmsg r; - } req = { - // Netlink message header. - .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), - .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, - .n.nlmsg_type = RTM_GETADDR, - - // Interface address message header. - .r.ifa_family = targ_p->family, - }; - - // Send interface address message. - if ((send(s, &req, req.n.nlmsg_len, 0)) < 0) { - logmsg(ANDROID_LOG_ERROR, "send netlink socket failed %s", strerror(errno)); - close(s); - return; - } - - // Read interface address message and parse the result if any. - ssize_t bytes_read; - char buf[NLMSG_SIZE]; - while ((bytes_read = recv(s, buf, sizeof(buf), 0)) > 0) { - struct nlmsghdr *nh = (struct nlmsghdr *)buf; - for (; NLMSG_OK(nh, bytes_read); nh = NLMSG_NEXT(nh, bytes_read)) { - if (nh->nlmsg_type == NLMSG_DONE) { - close(s); - return; - } - if (nh->nlmsg_type == NLMSG_ERROR) { - logmsg(ANDROID_LOG_ERROR, "netlink message error"); - close(s); - return; - } - if (nh->nlmsg_type == RTM_NEWADDR) { - // Walk through the all messages and update struct target variable as the deleted - // callback behavior of getaddr_cb() which always returns NL_OK. - // TODO: review if this can early return once address has been found. - parse_ifaddrmsg(nh, targ_p); - } - } - } - close(s); -} - -/* 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) { - union anyip *retval = NULL; - struct target targ = { - .family = family, - .foundip = 0, - .ifindex = if_nametoindex(interface), - }; - - if (targ.ifindex == 0) { - return NULL; // interface not found - } - - // sends message and receives the response. - sendrecv_ifaddrmsg(&targ); - - if (targ.foundip) { - retval = malloc(sizeof(union anyip)); - if (!retval) { - logmsg(ANDROID_LOG_FATAL, "getinterface_ip/out of memory"); - return NULL; - } - memcpy(retval, &targ.ip, sizeof(union anyip)); - } - - return retval; -} diff --git a/getaddr.h b/getaddr.h deleted file mode 100644 index 482ac13cac..0000000000 --- a/getaddr.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2011 Daniel Drown - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * getaddr.h - get a locally configured address - */ -#ifndef __GETADDR_H__ -#define __GETADDR_H__ - -#include - -union anyip { - struct in6_addr ip6; - struct in_addr ip4; -}; - -union anyip *getinterface_ip(const char *interface, int family); - -#endif From d775fd7e175f5bc7946ec2dc41235348049c50e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Wed, 15 Mar 2023 17:10:18 +0000 Subject: [PATCH 256/261] clatd: bump version number to 1.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we believe we have CHECKSUM_PARTIAL support implemented, it seems worthwhile to bump the version number. Test: TreeHugger Bug: 265591307 Signed-off-by: Maciej Żenczykowski Change-Id: I4d69ab6889b6d16c9947b38c327b1c806890d8cf --- clatd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clatd.h b/clatd.h index 85e2c493bb..e170c58dc8 100644 --- a/clatd.h +++ b/clatd.h @@ -48,7 +48,7 @@ struct tun_data; // plus some extra just-in-case headroom, because it doesn't hurt. #define MAXDUMPLEN (64 + MAXMTU) -#define CLATD_VERSION "1.6" +#define CLATD_VERSION "1.7" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) From 9c05f75eaa7d8e04c8f67c2cf6840ec9f79940d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 13 Apr 2023 09:15:01 +0000 Subject: [PATCH 257/261] clatd: only wait 15s for SIGTERM during shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Bug: 277682237 Signed-off-by: Maciej Żenczykowski Change-Id: If69c00f6f1c3cc1db9f8942852eeb1dbd0b4ab2e --- main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 11da65bec9..f888041c0f 100644 --- a/main.c +++ b/main.c @@ -194,8 +194,12 @@ int main(int argc, char **argv) { if (running) { logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface); - while (running) sleep(60); - logmsg(ANDROID_LOG_INFO, "Clatd on %s received SIGTERM", uplink_interface); + // let's give higher level java code 15 seconds to kill us, + // but eventually terminate anyway, in case system server forgets about us... + // sleep() should be interrupted by SIGTERM, the handler should clear running + sleep(15); + logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface, + running ? "timed out waiting for" : "received"); } else { logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface); } From 8fe16b0c52ff8edc9346a3951ff90787185b7fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Thu, 1 Jun 2023 08:58:43 +0000 Subject: [PATCH 258/261] add more logging for missing tpacket_auxdata cmsg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: If4ee940d4694e3f3197e14c8da9f9cd80080eee7 --- clatd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clatd.c b/clatd.c index 9a7a45acb6..bac8b1da4f 100644 --- a/clatd.c +++ b/clatd.c @@ -88,18 +88,29 @@ void process_packet_6_to_4(struct tun_data *tunnel) { return; } + bool ok = false; __u32 tp_status = 0; __u16 tp_net = 0; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); + ok = true; tp_status = aux->tp_status; tp_net = aux->tp_net; break; } } + if (!ok) { + // theoretically this should not happen... + static bool logged = false; + if (!logged) { + logmsg(ANDROID_LOG_ERROR, "%s: failed to fetch tpacket_auxdata cmsg", __func__); + logged = true; + } + } + const int payload_offset = offsetof(typeof(buf), payload); if (readlen < payload_offset + tp_net) { logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %d+%u L2 header", From b30a72dd2d492ad66f4bdf0c0f15884a1ba194aa Mon Sep 17 00:00:00 2001 From: Evgenii Stepanov Date: Wed, 7 Jun 2023 15:01:55 -0700 Subject: [PATCH 259/261] [NFC] Move MTE mode settings to a product variable. The default list of memtag targets found in build/make/target/product/memtag-common.mk Bug: 280343521 Test: no functional change Change-Id: I2b0eeecb680c33792063b9fb5e932d46d98d0d4e --- Android.bp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Android.bp b/Android.bp index 983ee00d52..595c6b924d 100644 --- a/Android.bp +++ b/Android.bp @@ -87,9 +87,6 @@ cc_binary { "cert-*", "android-*", ], - sanitize: { - memtag_heap: true, - }, apex_available: [ "com.android.tethering", From 3591f72de539533f76be081ecd3a68d71c7f5e3a Mon Sep 17 00:00:00 2001 From: zijunzhao Date: Mon, 10 Jul 2023 22:33:09 +0000 Subject: [PATCH 260/261] Fix duplicate symbols error in external/android-clat Symbol Global_Clatd_Config is duplicated when switch -fcommon to -fno-common Bug: b/151457797 Test: mm Change-Id: Ic6beb25cbc370d5bb085a3c569e66bae87446a49 --- clatd_test.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clatd_test.cpp b/clatd_test.cpp index 94ae0b5783..0ed5f28423 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -559,8 +559,6 @@ int get_transport_checksum(const uint8_t *packet) { } } -struct clat_config Global_Clatd_Config; - class ClatdTest : public ::testing::Test { protected: static TunInterface sTun; From 20078300757aea3988fa8467649c19ae6d353af3 Mon Sep 17 00:00:00 2001 From: Sam Saccone Date: Tue, 18 Jul 2023 17:50:16 +0000 Subject: [PATCH 261/261] Move OWNER reference master=>main. BUG=b/291759353 Change-Id: I6257998d96a6551d22577e1cdc52be76859e1c23 --- OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index 62c5737a2e..c24680e9b0 100644 --- a/OWNERS +++ b/OWNERS @@ -1,2 +1,2 @@ set noparent -file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking +file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking