1 /* 2 * Copyright 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * clatd_microbenchmark.c - micro-benchmark for clatd tun send path 17 * 18 * Run with: 19 * 20 * adb push {$ANDROID_PRODUCT_OUT,}/data/nativetest/clatd_microbenchmark/clatd_microbenchmark 21 * adb shell /data/nativetest/clatd_microbenchmark/clatd_microbenchmark 22 * 23 */ 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <string.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <time.h> 30 #include <unistd.h> 31 #include <sys/ioctl.h> 32 #include <sys/socket.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <sys/uio.h> 36 #include <netinet/in.h> 37 #include <netinet/ip.h> 38 #include <netinet/ip6.h> 39 #include <netinet/udp.h> 40 #include <arpa/inet.h> 41 #include <linux/if.h> 42 #include <linux/if_tun.h> 43 44 #include "checksum.h" 45 #include "tun.h" 46 47 #define DEVICENAME "clat4" 48 49 #define PORT 51339 50 #define PAYLOADSIZE (1280 - sizeof(struct iphdr) - sizeof(struct udphdr)) 51 #define NUMPACKETS 1000000 52 #define SEC_TO_NANOSEC (1000 * 1000 * 1000) 53 54 void init_sockaddr_in(struct sockaddr_in *sin, const char *addr) { 55 sin->sin_family = AF_INET; 56 sin->sin_port = 0; 57 sin->sin_addr.s_addr = inet_addr(addr); 58 } 59 60 void die(const char *str) { 61 perror(str); 62 exit(1); 63 } 64 65 int setup_tun() { 66 int fd = tun_open(); 67 if (fd == -1) die("tun_open"); 68 69 char dev[IFNAMSIZ] = DEVICENAME; 70 int ret = tun_alloc(dev, fd); 71 if (ret == -1) die("tun_alloc"); 72 struct ifreq ifr = { 73 .ifr_name = DEVICENAME, 74 }; 75 76 int s = socket(AF_INET, SOCK_DGRAM, 0); 77 init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "192.0.0.4"); 78 if (ioctl(s, SIOCSIFADDR, &ifr) < 0) die("SIOCSIFADDR"); 79 init_sockaddr_in((struct sockaddr_in *) &ifr.ifr_addr, "255.255.255.248"); 80 if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0) die("SIOCSIFNETMASK"); 81 if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) die("SIOCGIFFLAGS"); 82 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); 83 if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) die("SIOCSIFFLAGS"); 84 return fd; 85 } 86 87 int send_packet(int fd, uint8_t payload[], int len, uint32_t payload_checksum) { 88 struct tun_pi tun = { 0, htons(ETH_P_IP) }; 89 struct udphdr udp = { 90 .source = htons(1234), 91 .dest = htons(PORT), 92 .len = htons(len + sizeof(udp)), 93 .check = 0, 94 }; 95 struct iphdr ip = { 96 .version = 4, 97 .ihl = 5, 98 .tot_len = htons(len + sizeof(ip) + sizeof(udp)), 99 .frag_off = htons(IP_DF), 100 .ttl = 55, 101 .protocol = IPPROTO_UDP, 102 .saddr = htonl(0xc0000006), // 192.0.0.6 103 .daddr = htonl(0xc0000004), // 192.0.0.4 104 }; 105 clat_packet out = { 106 { &tun, sizeof(tun) }, // tun header 107 { &ip, sizeof(ip) }, // IP header 108 { NULL, 0 }, // Fragment header 109 { &udp, sizeof(udp) }, // Transport header 110 { NULL, 0 }, // ICMP error IP header 111 { NULL, 0 }, // ICMP error fragment header 112 { NULL, 0 }, // ICMP error transport header 113 { payload, len }, // Payload 114 }; 115 116 ip.check = ip_checksum(&ip, sizeof(ip)); 117 118 uint32_t sum; 119 sum = ipv4_pseudo_header_checksum(&ip, ntohs(udp.len)); 120 sum = ip_checksum_add(sum, &udp, sizeof(udp)); 121 sum += payload_checksum; 122 udp.check = ip_checksum_finish(sum); 123 124 return send_tun(fd, out, sizeof(out) / sizeof(out[0])); 125 } 126 127 double timedelta(const struct timespec tv1, const struct timespec tv2) { 128 struct timespec end = tv2; 129 if (end.tv_nsec < tv1.tv_nsec) { 130 end.tv_sec -= 1; 131 end.tv_nsec += SEC_TO_NANOSEC; 132 } 133 double seconds = (end.tv_sec - tv1.tv_sec); 134 seconds += (((double) (end.tv_nsec - tv1.tv_nsec)) / SEC_TO_NANOSEC); 135 return seconds; 136 } 137 138 void benchmark(const char *name, int fd, int s, int num, int do_read, 139 uint8_t payload[], int len, uint32_t payload_sum) { 140 int i; 141 char buf[4096]; 142 struct timespec tv1, tv2; 143 int write_err = 0, read_err = 0; 144 clock_gettime(CLOCK_MONOTONIC, &tv1); 145 for (i = 0; i < num; i++) { 146 if (send_packet(fd, payload, len, payload_sum) == -1) write_err++; 147 if (do_read && recvfrom(s, buf, sizeof(buf), 0, NULL, NULL) == -1) { 148 read_err++; 149 if (errno == ETIMEDOUT) { 150 printf("Timed out after %d packets!\n", i); 151 break; 152 } 153 } 154 } 155 clock_gettime(CLOCK_MONOTONIC, &tv2); 156 double seconds = timedelta(tv1, tv2); 157 int pps = (int) (i / seconds); 158 double mbps = (i * PAYLOADSIZE / 1000000 * 8 / seconds); 159 printf("%s: %d packets in %.2fs (%d pps, %.2f Mbps), ", name, i, seconds, pps, mbps); 160 printf("read err %d (%.2f%%), write err %d (%.2f%%)\n", 161 read_err, (float) read_err / i * 100, 162 write_err, (float) write_err / i * 100); 163 } 164 165 int open_socket() { 166 int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 167 168 int on = 1; 169 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) die("SO_REUSEADDR"); 170 171 struct timeval tv = { 1, 0 }; 172 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) die("SO_RCVTIMEO"); 173 174 struct sockaddr_in addr = { 175 .sin_family = AF_INET, 176 .sin_port = ntohs(PORT), 177 .sin_addr = { INADDR_ANY } 178 }; 179 if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) die ("bind"); 180 181 return sock; 182 } 183 184 int main() { 185 int fd = setup_tun(); 186 int sock = open_socket(); 187 188 int i; 189 uint8_t payload[PAYLOADSIZE]; 190 for (i = 0; i < (int) sizeof(payload); i++) { 191 payload[i] = (uint8_t) i; 192 } 193 uint32_t payload_sum = ip_checksum_add(0, payload, sizeof(payload)); 194 195 // Check things are working. 196 char buf[4096]; 197 if (send_packet(fd, payload, sizeof(payload), payload_sum) == -1) die("send_packet"); 198 if (recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL) == -1) die("recvfrom"); 199 200 benchmark("Blocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); 201 close(fd); 202 203 fd = setup_tun(); 204 set_nonblocking(fd); 205 benchmark("No read", fd, sock, NUMPACKETS, 0, payload, sizeof(payload), payload_sum); 206 close(fd); 207 208 fd = setup_tun(); 209 set_nonblocking(fd); 210 benchmark("Nonblocking", fd, sock, NUMPACKETS, 1, payload, sizeof(payload), payload_sum); 211 close(fd); 212 213 return 0; 214 } 215