Created
August 14, 2017 01:15
-
-
Save delroth/26746c6315adab42044c791450bafa8a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Bridges between a TUN-like and a TAP-like interface. Does ARP proxying and | |
// stuff like that. | |
#include <arpa/inet.h> | |
#include <fcntl.h> | |
#include <functional> | |
#include <linux/if_ether.h> | |
#include <linux/if_packet.h> | |
#include <net/ethernet.h> | |
#include <net/if.h> | |
#include <poll.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
void die(const char *format, ...) { | |
va_list va; | |
va_start(va, format); | |
vfprintf(stderr, format, va); | |
fputs("\n", stderr); | |
exit(1); | |
} | |
struct Socket { | |
template <typename... T> Socket(T &&... args) { | |
fd = socket(args...); | |
if (fd < 0) { | |
die("Could not create socket: %m"); | |
} | |
} | |
~Socket() { close(fd); } | |
operator int() { return fd; } | |
int fd; | |
}; | |
int FindInterfaceIndex(const char *iface) { | |
Socket sock{AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)}; | |
ifreq req = {}; | |
strncpy(req.ifr_name, iface, sizeof(req.ifr_name)); | |
if (ioctl(sock, SIOCGIFINDEX, &req) < 0) { | |
die("ioctl(SIOCGIFINDEX) failed: %m"); | |
} | |
return req.ifr_ifindex; | |
} | |
uint16_t ReadBE16(const std::vector<uint8_t> &v, size_t offset) { | |
if (offset + 2 > v.size()) { | |
die("Reading BE16 out of bounds (%z vs. %z)", offset, v.size()); | |
} | |
return (v[offset] << 8) | v[offset + 1]; | |
} | |
uint32_t ReadBE32(const std::vector<uint8_t> &v, size_t offset) { | |
if (offset + 4 > v.size()) { | |
die("Reading BE32 out of bounds (%z vs. %z)", offset, v.size()); | |
} | |
return (v[offset] << 24) | (v[offset + 1] << 16) | (v[offset + 2] << 8) | | |
v[offset + 3]; | |
} | |
void FixupChecksums(uint8_t *p, size_t s) { | |
uint32_t c = 0; | |
auto process16 = [p, &c](int o) { | |
c += static_cast<uint16_t>((p[o] << 8) | p[o + 1]); | |
}; | |
if (p[9] == 0x11) { | |
// UDP checksum is optional. | |
p[26] = 0; | |
p[27] = 0; | |
} else if (p[9] == 0x06) { | |
// TCP | |
process16(12); | |
process16(14); | |
process16(16); | |
process16(18); | |
c += p[9]; | |
c += s - 20; | |
p[36] = 0; | |
p[37] = 0; | |
for (size_t i = 20; i < s - 1; i += 2) { | |
process16(i); | |
} | |
if (s & 1) { | |
c += p[s - 1] << 8; | |
} | |
while (c >= 0x10000) { | |
c = (c & 0xFFFF) + (c >> 16); | |
} | |
uint16_t c2 = ~c; | |
p[36] = c2 >> 8; | |
p[37] = c2 & 0xFF; | |
} | |
} | |
void ParseMacAddress(const char *str, uint8_t *out) { | |
if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &out[0], &out[1], &out[2], | |
&out[3], &out[4], &out[5]) != 6) { | |
die("Wrong MAC address: %s", str); | |
} | |
} | |
int main(int argc, char **argv) { | |
if (argc < 7) { | |
die("usage: %s <tun-if> <tun-ip> <tun-mac> <tap-if> <tap-ip> <tap-mac>", | |
argv[0]); | |
} | |
const char *tun_device_name = argv[1]; | |
uint32_t tun_ip = htonl(inet_addr(argv[2])); | |
uint8_t tun_mac[6]; | |
ParseMacAddress(argv[3], tun_mac); | |
const char *tap_device_name = argv[4]; | |
uint32_t tap_ip = htonl(inet_addr(argv[5])); | |
uint8_t tap_mac[6]; | |
ParseMacAddress(argv[6], tap_mac); | |
printf("Mirroring: %s (ip: %d.%d.%d.%d mac: %02x:%02x:%02x:%02x:%02x:%02x) " | |
"<-> %s (ip: %d.%d.%d.%d mac: %02x:%02x:%02x:%02x:%02x:%02x\n", | |
tun_device_name, tun_ip >> 24, (tun_ip >> 16) & 0xff, | |
(tun_ip >> 8) & 0xff, tun_ip & 0xff, tun_mac[0], tun_mac[1], | |
tun_mac[2], tun_mac[3], tun_mac[4], tun_mac[5], tap_device_name, | |
tap_ip >> 24, (tap_ip >> 16) & 0xff, (tap_ip >> 8) & 0xff, | |
tap_ip & 0xff, tap_mac[0], tap_mac[1], tap_mac[2], tap_mac[3], | |
tap_mac[4], tap_mac[5]); | |
Socket tun_socket{AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)}; | |
Socket tap_socket{AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)}; | |
int tun_iface_index = FindInterfaceIndex(tun_device_name); | |
int tap_iface_index = FindInterfaceIndex(tap_device_name); | |
struct sockaddr_ll addr; | |
addr.sll_family = AF_PACKET; | |
addr.sll_protocol = htons(ETH_P_ALL); | |
addr.sll_pkttype = PACKET_HOST; | |
addr.sll_halen = 0; | |
addr.sll_ifindex = tun_iface_index; | |
if (bind(tun_socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0) { | |
die("Could not bind TUN socket: %m"); | |
} | |
addr.sll_ifindex = tap_iface_index; | |
if (bind(tap_socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0) { | |
die("Could not bind TAP socket: %m"); | |
} | |
for (;;) { | |
struct pollfd events[2]; | |
events[0].fd = tun_socket; | |
events[0].events = POLLIN; | |
events[1].fd = tap_socket; | |
events[1].events = POLLIN; | |
int poll_ret = poll(events, 2, -1); | |
if (poll_ret <= 0) { | |
continue; | |
} | |
uint8_t buffer[65536]; | |
std::vector<uint8_t> packet; | |
int size; | |
if (events[0].revents & POLLERR || events[1].revents & POLLERR) { | |
break; | |
} else if (events[0].revents & POLLIN) { | |
size = read(tun_socket, buffer, sizeof(buffer)); | |
packet.assign(buffer, buffer + size); | |
if (packet.size() >= 20 && ReadBE32(packet, 16) == tap_ip) { | |
std::vector<uint8_t> answer; | |
answer.resize(packet.size() + 14); | |
for (int i = 0; i < 6; ++i) { | |
answer[i] = tap_mac[i]; | |
answer[i + 6] = tun_mac[i]; | |
} | |
answer[12] = 0x08; | |
answer[13] = 0x00; | |
for (size_t i = 0; i < packet.size(); ++i) { | |
answer[i + 14] = packet[i]; | |
} | |
write(tap_socket, answer.data(), answer.size()); | |
} | |
} else if (events[1].revents & POLLIN) { | |
size = read(tap_socket, buffer, sizeof(buffer)); | |
packet.assign(buffer, buffer + size); | |
if (packet.size() == 42 && ReadBE16(packet, 12) == 0x0806 && | |
ReadBE16(packet, 20) == 0x0001 && ReadBE32(packet, 38) == tun_ip) { | |
// ARP request for the TUN IP address. | |
uint8_t answer[] = { | |
// Destination MAC. | |
packet[6], packet[7], packet[8], packet[9], packet[10], packet[11], | |
// Source MAC, faked. | |
tun_mac[0], tun_mac[1], tun_mac[2], tun_mac[3], tun_mac[4], | |
tun_mac[5], | |
// ARP protocol. | |
0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02, | |
// Source MAC, faked. | |
tun_mac[0], tun_mac[1], tun_mac[2], tun_mac[3], tun_mac[4], | |
tun_mac[5], | |
// Source IP. | |
static_cast<uint8_t>(tun_ip >> 24), | |
static_cast<uint8_t>(tun_ip >> 16), | |
static_cast<uint8_t>(tun_ip >> 8), | |
static_cast<uint8_t>(tun_ip >> 0), | |
// Destination MAC. | |
packet[6], packet[7], packet[8], packet[9], packet[10], packet[11], | |
// Destination IP. | |
static_cast<uint8_t>(tap_ip >> 24), | |
static_cast<uint8_t>(tap_ip >> 16), | |
static_cast<uint8_t>(tap_ip >> 8), | |
static_cast<uint8_t>(tap_ip >> 0), | |
}; | |
write(tap_socket, answer, sizeof(answer)); | |
} else if (packet.size() >= 32 && ReadBE16(packet, 12) == 0x0800 && | |
!memcmp(packet.data(), tun_mac, 6)) { | |
FixupChecksums(&packet[14], packet.size() - 14); | |
// Remove LL header and forward. | |
write(tun_socket, packet.data() + 14, packet.size() - 14); | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment