/* * 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 <errno.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/mman.h> #include <linux/if.h> #include <linux/if_packet.h> #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); } }