skf_net_off.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Open a tun device.
  3. *
  4. * [modifications: use IFF_NAPI_FRAGS, add sk filter]
  5. *
  6. * Expects the device to have been configured previously, e.g.:
  7. * sudo ip tuntap add name tap1 mode tap
  8. * sudo ip link set tap1 up
  9. * sudo ip link set dev tap1 addr 02:00:00:00:00:01
  10. * sudo ip -6 addr add fdab::1 peer fdab::2 dev tap1 nodad
  11. *
  12. * And to avoid premature pskb_may_pull:
  13. *
  14. * sudo ethtool -K tap1 gro off
  15. * sudo bash -c 'echo 0 > /proc/sys/net/ipv4/ip_early_demux'
  16. */
  17. #define _GNU_SOURCE
  18. #include <arpa/inet.h>
  19. #include <errno.h>
  20. #include <error.h>
  21. #include <fcntl.h>
  22. #include <getopt.h>
  23. #include <linux/filter.h>
  24. #include <linux/if.h>
  25. #include <linux/if_packet.h>
  26. #include <linux/if_tun.h>
  27. #include <linux/ipv6.h>
  28. #include <netinet/if_ether.h>
  29. #include <netinet/in.h>
  30. #include <netinet/ip.h>
  31. #include <netinet/ip6.h>
  32. #include <netinet/udp.h>
  33. #include <poll.h>
  34. #include <signal.h>
  35. #include <stdbool.h>
  36. #include <stddef.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <sys/ioctl.h>
  41. #include <sys/socket.h>
  42. #include <sys/poll.h>
  43. #include <sys/types.h>
  44. #include <sys/uio.h>
  45. #include <unistd.h>
  46. static bool cfg_do_filter;
  47. static bool cfg_do_frags;
  48. static int cfg_dst_port = 8000;
  49. static char *cfg_ifname;
  50. static int tun_open(const char *tun_name)
  51. {
  52. struct ifreq ifr = {0};
  53. int fd, ret;
  54. fd = open("/dev/net/tun", O_RDWR);
  55. if (fd == -1)
  56. error(1, errno, "open /dev/net/tun");
  57. ifr.ifr_flags = IFF_TAP;
  58. if (cfg_do_frags)
  59. ifr.ifr_flags |= IFF_NAPI | IFF_NAPI_FRAGS;
  60. strncpy(ifr.ifr_name, tun_name, IFNAMSIZ - 1);
  61. ret = ioctl(fd, TUNSETIFF, &ifr);
  62. if (ret)
  63. error(1, ret, "ioctl TUNSETIFF");
  64. return fd;
  65. }
  66. static void sk_set_filter(int fd)
  67. {
  68. const int offset_proto = offsetof(struct ip6_hdr, ip6_nxt);
  69. const int offset_dport = sizeof(struct ip6_hdr) + offsetof(struct udphdr, dest);
  70. /* Filter UDP packets with destination port cfg_dst_port */
  71. struct sock_filter filter_code[] = {
  72. BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE),
  73. BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PACKET_HOST, 0, 4),
  74. BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_NET_OFF + offset_proto),
  75. BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 2),
  76. BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + offset_dport),
  77. BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_dst_port, 1, 0),
  78. BPF_STMT(BPF_RET + BPF_K, 0),
  79. BPF_STMT(BPF_RET + BPF_K, 0xFFFF),
  80. };
  81. struct sock_fprog filter = {
  82. sizeof(filter_code) / sizeof(filter_code[0]),
  83. filter_code,
  84. };
  85. if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)))
  86. error(1, errno, "setsockopt attach filter");
  87. }
  88. static int raw_open(void)
  89. {
  90. int fd;
  91. fd = socket(PF_INET6, SOCK_RAW, IPPROTO_UDP);
  92. if (fd == -1)
  93. error(1, errno, "socket raw (udp)");
  94. if (cfg_do_filter)
  95. sk_set_filter(fd);
  96. return fd;
  97. }
  98. static void tun_write(int fd)
  99. {
  100. const char eth_src[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x02 };
  101. const char eth_dst[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 };
  102. struct tun_pi pi = {0};
  103. struct ipv6hdr ip6h = {0};
  104. struct udphdr uh = {0};
  105. struct ethhdr eth = {0};
  106. uint32_t payload;
  107. struct iovec iov[5];
  108. int ret;
  109. pi.proto = htons(ETH_P_IPV6);
  110. memcpy(eth.h_source, eth_src, sizeof(eth_src));
  111. memcpy(eth.h_dest, eth_dst, sizeof(eth_dst));
  112. eth.h_proto = htons(ETH_P_IPV6);
  113. ip6h.version = 6;
  114. ip6h.payload_len = htons(sizeof(uh) + sizeof(uint32_t));
  115. ip6h.nexthdr = IPPROTO_UDP;
  116. ip6h.hop_limit = 8;
  117. if (inet_pton(AF_INET6, "fdab::2", &ip6h.saddr) != 1)
  118. error(1, errno, "inet_pton src");
  119. if (inet_pton(AF_INET6, "fdab::1", &ip6h.daddr) != 1)
  120. error(1, errno, "inet_pton src");
  121. uh.source = htons(8000);
  122. uh.dest = htons(cfg_dst_port);
  123. uh.len = ip6h.payload_len;
  124. uh.check = 0;
  125. payload = htonl(0xABABABAB); /* Covered in IPv6 length */
  126. iov[0].iov_base = &pi;
  127. iov[0].iov_len = sizeof(pi);
  128. iov[1].iov_base = &eth;
  129. iov[1].iov_len = sizeof(eth);
  130. iov[2].iov_base = &ip6h;
  131. iov[2].iov_len = sizeof(ip6h);
  132. iov[3].iov_base = &uh;
  133. iov[3].iov_len = sizeof(uh);
  134. iov[4].iov_base = &payload;
  135. iov[4].iov_len = sizeof(payload);
  136. ret = writev(fd, iov, sizeof(iov) / sizeof(iov[0]));
  137. if (ret <= 0)
  138. error(1, errno, "writev");
  139. }
  140. static void raw_read(int fd)
  141. {
  142. struct timeval tv = { .tv_usec = 100 * 1000 };
  143. struct msghdr msg = {0};
  144. struct iovec iov[2];
  145. struct udphdr uh;
  146. uint32_t payload[2];
  147. int ret;
  148. if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
  149. error(1, errno, "setsockopt rcvtimeo udp");
  150. iov[0].iov_base = &uh;
  151. iov[0].iov_len = sizeof(uh);
  152. iov[1].iov_base = payload;
  153. iov[1].iov_len = sizeof(payload);
  154. msg.msg_iov = iov;
  155. msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
  156. ret = recvmsg(fd, &msg, 0);
  157. if (ret <= 0)
  158. error(1, errno, "read raw");
  159. if (ret != sizeof(uh) + sizeof(payload[0]))
  160. error(1, errno, "read raw: len=%d\n", ret);
  161. fprintf(stderr, "raw recv: 0x%x\n", payload[0]);
  162. }
  163. static void parse_opts(int argc, char **argv)
  164. {
  165. int c;
  166. while ((c = getopt(argc, argv, "fFi:")) != -1) {
  167. switch (c) {
  168. case 'f':
  169. cfg_do_filter = true;
  170. printf("bpf filter enabled\n");
  171. break;
  172. case 'F':
  173. cfg_do_frags = true;
  174. printf("napi frags mode enabled\n");
  175. break;
  176. case 'i':
  177. cfg_ifname = optarg;
  178. break;
  179. default:
  180. error(1, 0, "unknown option %c", optopt);
  181. break;
  182. }
  183. }
  184. if (!cfg_ifname)
  185. error(1, 0, "must specify tap interface name (-i)");
  186. }
  187. int main(int argc, char **argv)
  188. {
  189. int fdt, fdr;
  190. parse_opts(argc, argv);
  191. fdr = raw_open();
  192. fdt = tun_open(cfg_ifname);
  193. tun_write(fdt);
  194. raw_read(fdr);
  195. if (close(fdt))
  196. error(1, errno, "close tun");
  197. if (close(fdr))
  198. error(1, errno, "close udp");
  199. fprintf(stderr, "OK\n");
  200. return 0;
  201. }