Merge "Support NLM_F_REPLACE flag when sending a RTM_NEWADDR message."
diff --git a/staticlibs/native/ip_checksum/checksum.c b/staticlibs/native/ip_checksum/checksum.c
index 04217a7..5641fad 100644
--- a/staticlibs/native/ip_checksum/checksum.c
+++ b/staticlibs/native/ip_checksum/checksum.c
@@ -32,20 +32,16 @@
* 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;
+ while (len >= 2) {
+ current += *data_16;
data_16++;
- left -= 2;
+ len -= 2;
}
- if (left) {
- checksum += *(uint8_t*)data_16;
- }
+ if (len) current += *(uint8_t*)data_16; // assumes little endian!
- return checksum;
+ return current;
}
/* function: ip_checksum_fold
@@ -54,9 +50,8 @@
* 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);
- }
+ temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+ temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
return temp_sum;
}
@@ -75,12 +70,7 @@
* len - length of data
*/
uint16_t ip_checksum(const void* data, int len) {
- // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
- // is correctly calculated as 0.
- uint32_t temp_sum;
-
- temp_sum = ip_checksum_add(0, data, len);
- return ip_checksum_finish(temp_sum);
+ return ip_checksum_finish(ip_checksum_add(0xFFFF, data, len));
}
/* function: ipv6_pseudo_header_checksum
@@ -92,7 +82,6 @@
uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
uint32_t checksum_len = htonl(len);
uint32_t checksum_next = htonl(protocol);
-
uint32_t current = 0;
current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
@@ -109,11 +98,8 @@
* 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);
-
+ uint16_t temp_protocol = htons(ip->protocol);
+ uint16_t temp_length = htons(len);
uint32_t current = 0;
current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
@@ -135,7 +121,7 @@
// 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_sum = ip_checksum_fold(new_hdr_sum + checksum);
uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
if (folded_sum > folded_old) {
return ~(folded_sum - folded_old);
diff --git a/staticlibs/native/ip_checksum/checksum.h b/staticlibs/native/ip_checksum/checksum.h
index 868217c..87393c9 100644
--- a/staticlibs/native/ip_checksum/checksum.h
+++ b/staticlibs/native/ip_checksum/checksum.h
@@ -12,11 +12,8 @@
* 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__
+#pragma once
#include <netinet/ip.h>
#include <netinet/ip6.h>
@@ -30,5 +27,3 @@
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/staticlibs/testutils/devicetests/com/android/testutils/PacketReflector.java b/staticlibs/testutils/devicetests/com/android/testutils/PacketReflector.java
new file mode 100644
index 0000000..96bca62
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/PacketReflector.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.testutils;
+
+import static android.system.OsConstants.ICMP6_ECHO_REPLY;
+import static android.system.OsConstants.ICMP6_ECHO_REQUEST;
+
+import android.annotation.NonNull;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class PacketReflector extends Thread {
+
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int IPV6_HEADER_LENGTH = 40;
+
+ private static final int IPV4_ADDR_OFFSET = 12;
+ private static final int IPV6_ADDR_OFFSET = 8;
+ private static final int IPV4_ADDR_LENGTH = 4;
+ private static final int IPV6_ADDR_LENGTH = 16;
+
+ private static final int IPV4_PROTO_OFFSET = 9;
+ private static final int IPV6_PROTO_OFFSET = 6;
+
+ private static final byte IPPROTO_ICMP = 1;
+ private static final byte IPPROTO_TCP = 6;
+ private static final byte IPPROTO_UDP = 17;
+ private static final byte IPPROTO_ICMPV6 = 58;
+
+ private static final int ICMP_HEADER_LENGTH = 8;
+ private static final int TCP_HEADER_LENGTH = 20;
+ private static final int UDP_HEADER_LENGTH = 8;
+
+ private static final byte ICMP_ECHO = 8;
+ private static final byte ICMP_ECHOREPLY = 0;
+
+ private static String TAG = "PacketReflector";
+
+ @NonNull private FileDescriptor mFd;
+ @NonNull private byte[] mBuf;
+
+ public PacketReflector(@NonNull FileDescriptor fd, int mtu) {
+ super("PacketReflector");
+ mFd = fd;
+ mBuf = new byte[mtu];
+ }
+
+ private static void swapBytes(@NonNull byte[] buf, int pos1, int pos2, int len) {
+ for (int i = 0; i < len; i++) {
+ byte b = buf[pos1 + i];
+ buf[pos1 + i] = buf[pos2 + i];
+ buf[pos2 + i] = b;
+ }
+ }
+
+ private static void swapAddresses(@NonNull byte[] buf, int version) {
+ int addrPos, addrLen;
+ switch (version) {
+ case 4:
+ addrPos = IPV4_ADDR_OFFSET;
+ addrLen = IPV4_ADDR_LENGTH;
+ break;
+ case 6:
+ addrPos = IPV6_ADDR_OFFSET;
+ addrLen = IPV6_ADDR_LENGTH;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
+ }
+
+ // Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
+ // This is used by the test to "connect to itself" through the VPN.
+ private void processTcpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + TCP_HEADER_LENGTH) {
+ return;
+ }
+
+ // Swap src and dst IP addresses.
+ swapAddresses(buf, version);
+
+ // Send the packet back.
+ writePacket(buf, len);
+ }
+
+ // Echo UDP packets: swap source and destination addresses, and source and destination ports.
+ // This is used by the test to check that the bytes it sends are echoed back.
+ private void processUdpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + UDP_HEADER_LENGTH) {
+ return;
+ }
+
+ // Swap src and dst IP addresses.
+ swapAddresses(buf, version);
+
+ // Swap dst and src ports.
+ int portOffset = hdrLen;
+ swapBytes(buf, portOffset, portOffset + 2, 2);
+
+ // Send the packet back.
+ writePacket(buf, len);
+ }
+
+ private void processIcmpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + ICMP_HEADER_LENGTH) {
+ return;
+ }
+
+ byte type = buf[hdrLen];
+ if (!(version == 4 && type == ICMP_ECHO) &&
+ !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) {
+ return;
+ }
+
+ // Save the ping packet we received.
+ byte[] request = buf.clone();
+
+ // Swap src and dst IP addresses, and send the packet back.
+ // This effectively pings the device to see if it replies.
+ swapAddresses(buf, version);
+ writePacket(buf, len);
+
+ // The device should have replied, and buf should now contain a ping response.
+ int received = readPacket(buf);
+ if (received != len) {
+ Log.i(TAG, "Reflecting ping did not result in ping response: " +
+ "read=" + received + " expected=" + len);
+ return;
+ }
+
+ byte replyType = buf[hdrLen];
+ if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY)
+ || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) {
+ Log.i(TAG, "Received unexpected ICMP reply: original " + type
+ + ", reply " + replyType);
+ return;
+ }
+
+ // Compare the response we got with the original packet.
+ // The only thing that should have changed are addresses, type and checksum.
+ // Overwrite them with the received bytes and see if the packet is otherwise identical.
+ request[hdrLen] = buf[hdrLen]; // Type
+ request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1.
+ request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2.
+
+ // Since Linux kernel 4.2, net.ipv6.auto_flowlabels is set by default, and therefore
+ // the request and reply may have different IPv6 flow label: ignore that as well.
+ if (version == 6) {
+ request[1] = (byte) (request[1] & 0xf0 | buf[1] & 0x0f);
+ request[2] = buf[2];
+ request[3] = buf[3];
+ }
+
+ for (int i = 0; i < len; i++) {
+ if (buf[i] != request[i]) {
+ Log.i(TAG, "Received non-matching packet when expecting ping response.");
+ return;
+ }
+ }
+
+ // Now swap the addresses again and reflect the packet. This sends a ping reply.
+ swapAddresses(buf, version);
+ writePacket(buf, len);
+ }
+
+ private void writePacket(@NonNull byte[] buf, int len) {
+ try {
+ Os.write(mFd, buf, 0, len);
+ } catch (ErrnoException | IOException e) {
+ Log.e(TAG, "Error writing packet: " + e.getMessage());
+ }
+ }
+
+ private int readPacket(@NonNull byte[] buf) {
+ int len;
+ try {
+ len = Os.read(mFd, buf, 0, buf.length);
+ } catch (ErrnoException | IOException e) {
+ Log.e(TAG, "Error reading packet: " + e.getMessage());
+ len = -1;
+ }
+ return len;
+ }
+
+ // Reads one packet from our mFd, and possibly writes the packet back.
+ private void processPacket() {
+ int len = readPacket(mBuf);
+ if (len < 1) {
+ return;
+ }
+
+ int version = mBuf[0] >> 4;
+ int protoPos, hdrLen;
+ if (version == 4) {
+ hdrLen = IPV4_HEADER_LENGTH;
+ protoPos = IPV4_PROTO_OFFSET;
+ } else if (version == 6) {
+ hdrLen = IPV6_HEADER_LENGTH;
+ protoPos = IPV6_PROTO_OFFSET;
+ } else {
+ return;
+ }
+
+ if (len < hdrLen) {
+ return;
+ }
+
+ byte proto = mBuf[protoPos];
+ switch (proto) {
+ case IPPROTO_ICMP:
+ // fall through
+ case IPPROTO_ICMPV6:
+ processIcmpPacket(mBuf, version, len, hdrLen);
+ break;
+ case IPPROTO_TCP:
+ processTcpPacket(mBuf, version, len, hdrLen);
+ break;
+ case IPPROTO_UDP:
+ processUdpPacket(mBuf, version, len, hdrLen);
+ break;
+ }
+ }
+
+ public void run() {
+ Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid());
+ while (!interrupted() && mFd.valid()) {
+ processPacket();
+ }
+ Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid());
+ }
+}