Skip to content

Instantly share code, notes, and snippets.

@delroth
Created August 14, 2017 01:15
Show Gist options
  • Save delroth/26746c6315adab42044c791450bafa8a to your computer and use it in GitHub Desktop.
Save delroth/26746c6315adab42044c791450bafa8a to your computer and use it in GitHub Desktop.
// 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