1#include "faketcp.h"
2
3#include <fcntl.h>
4#include <stdio.h>
5#include <string.h>
6#include <unistd.h>
7#include <linux/if_tun.h>
8#include <netinet/in.h>
9#include <netinet/ip_icmp.h>
10#include <sys/ioctl.h>
11
12int sethostaddr(const char* dev)
13{
14  struct ifreq ifr;
15  bzero(&ifr, sizeof(ifr));
16  strcpy(ifr.ifr_name, dev);
17  struct sockaddr_in addr;
18  bzero(&addr, sizeof addr);
19  addr.sin_family = AF_INET;
20  inet_pton(AF_INET, "192.168.0.1", &addr.sin_addr);
21  //addr.sin_addr.s_addr = htonl(0xc0a80001);
22  bcopy(&addr, &ifr.ifr_addr, sizeof addr);
23  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
24  if (sockfd < 0)
25    return sockfd;
26  int err = 0;
27  // ifconfig tun0 192.168.0.1
28  if ((err = ioctl(sockfd, SIOCSIFADDR, (void *) &ifr)) < 0)
29  {
30    perror("ioctl SIOCSIFADDR");
31    goto done;
32  }
33  // ifup tun0
34  if ((err = ioctl(sockfd, SIOCGIFFLAGS, (void *) &ifr)) < 0)
35  {
36    perror("ioctl SIOCGIFFLAGS");
37    goto done;
38  }
39  ifr.ifr_flags |= IFF_UP;
40  if ((err = ioctl(sockfd, SIOCSIFFLAGS, (void *) &ifr)) < 0)
41  {
42    perror("ioctl SIOCSIFFLAGS");
43    goto done;
44  }
45  // ifconfig tun0 192.168.0.1/24
46  inet_pton(AF_INET, "255.255.255.0", &addr.sin_addr);
47  bcopy(&addr, &ifr.ifr_netmask, sizeof addr);
48  if ((err = ioctl(sockfd, SIOCSIFNETMASK, (void *) &ifr)) < 0)
49  {
50    perror("ioctl SIOCSIFNETMASK");
51    goto done;
52  }
53done:
54  close(sockfd);
55  return err;
56}
57
58int tun_alloc(char dev[IFNAMSIZ], bool offload)
59{
60  struct ifreq ifr;
61  int fd, err;
62
63  if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
64  {
65    perror("open");
66    return -1;
67  }
68
69  bzero(&ifr, sizeof(ifr));
70  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
71
72  if (*dev)
73  {
74    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
75  }
76
77  if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0)
78  {
79    perror("ioctl TUNSETIFF");
80    close(fd);
81    return err;
82  }
83
84  if (offload) {
85    const uint32_t offload_flags =
86        TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN;
87    if (ioctl(fd, TUNSETOFFLOAD, offload_flags) != 0) {
88      perror("TUNSETOFFLOAD");
89      return -1;
90    }
91  }
92
93  strcpy(dev, ifr.ifr_name);
94  if ((err = sethostaddr(dev)) < 0)
95    return err;
96
97  return fd;
98}
99
100uint16_t in_checksum(const void* buf, int len)
101{
102  assert(len % 2 == 0);
103  const uint16_t* data = static_cast<const uint16_t*>(buf);
104  int sum = 0;
105  for (int i = 0; i < len; i+=2)
106  {
107    sum += *data++;
108  }
109  while (sum >> 16)
110    sum = (sum & 0xFFFF) + (sum >> 16);
111  assert(sum <= 0xFFFF);
112  return ~sum;
113}
114
115void icmp_input(int fd, const void* input, const void* payload, int len)
116{
117  const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
118  const struct icmphdr* icmphdr = static_cast<const struct icmphdr*>(payload);
119  // const int icmphdr_size = sizeof(*icmphdr);
120  const int iphdr_len = iphdr->ihl*4;
121
122  if (icmphdr->type == ICMP_ECHO)
123  {
124    char source[INET_ADDRSTRLEN];
125    char dest[INET_ADDRSTRLEN];
126    inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
127    inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
128    printf("%s > %s: ", source, dest);
129    printf("ICMP echo request, id %d, seq %d, length %d\n",
130           ntohs(icmphdr->un.echo.id),
131           ntohs(icmphdr->un.echo.sequence),
132           len - iphdr_len);
133
134    union
135    {
136      unsigned char output[ETH_FRAME_LEN];
137      struct
138      {
139        struct iphdr iphdr;
140        struct icmphdr icmphdr;
141      } out;
142    };
143
144    memcpy(output, input, len);
145    out.icmphdr.type = ICMP_ECHOREPLY;
146    out.icmphdr.checksum += ICMP_ECHO; // FIXME: not portable
147    std::swap(out.iphdr.saddr, out.iphdr.daddr);
148    write(fd, output, len);
149  }
150}
151
152