MacEthernetTapAgent.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. * Copyright (c)2019 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2025-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. /*
  14. * This creates a pair of feth devices with the lower numbered device
  15. * being the ZeroTier virtual interface and the other being the device
  16. * used to actually read and write packets. The latter gets no IP config
  17. * and is only used for I/O. The behavior of feth is similar to the
  18. * veth pairs that exist on Linux.
  19. *
  20. * The feth device has only existed since MacOS Sierra, but that's fairly
  21. * long ago in Mac terms.
  22. *
  23. * I/O with feth must be done using two different sockets. The BPF socket
  24. * is used to receive packets, while an AF_NDRV (low-level network driver
  25. * access) socket must be used to inject. AF_NDRV can't read IP frames
  26. * since BSD doesn't forward packets out the NDRV tap if they've already
  27. * been handled, and while BPF can inject its MTU for injected packets
  28. * is limited to 2048. AF_NDRV packet injection is required to inject
  29. * ZeroTier's large MTU frames.
  30. *
  31. * All this stuff is basically undocumented. A lot of tracing through
  32. * the Darwin/XNU kernel source was required to figure out how to make
  33. * this actually work.
  34. *
  35. * We hope to develop a DriverKit-based driver in the near-mid future to
  36. * replace this weird hack, but it works for now through Big Sur in our
  37. * testing.
  38. *
  39. * See also:
  40. *
  41. * https://apple.stackexchange.com/questions/337715/fake-ethernet-interfaces-feth-if-fake-anyone-ever-seen-this
  42. * https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/net/if_fake.c.auto.html
  43. *
  44. */
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <stdarg.h>
  49. #include <unistd.h>
  50. #include <signal.h>
  51. #include <fcntl.h>
  52. #include <errno.h>
  53. #include <sys/signal.h>
  54. #include <sys/types.h>
  55. #include <sys/stat.h>
  56. #include <sys/ioctl.h>
  57. #include <sys/wait.h>
  58. #include <sys/select.h>
  59. #include <sys/cdefs.h>
  60. #include <sys/uio.h>
  61. #include <sys/param.h>
  62. #include <sys/ioctl.h>
  63. #include <sys/socket.h>
  64. #include <sys/sysctl.h>
  65. #include <netinet/in.h>
  66. #include <arpa/inet.h>
  67. #include <net/bpf.h>
  68. #include <net/route.h>
  69. #include <net/if.h>
  70. #include <net/if_arp.h>
  71. #include <net/if_dl.h>
  72. #include <net/if_media.h>
  73. #include <net/ndrv.h>
  74. #include <netinet/in_var.h>
  75. #include <netinet/icmp6.h>
  76. #include <netinet6/in6_var.h>
  77. #include <netinet6/nd6.h>
  78. #include <ifaddrs.h>
  79. #include "../version.h"
  80. #include "MacEthernetTapAgent.h"
  81. #ifndef SIOCAUTOCONF_START
  82. #define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
  83. #endif
  84. #ifndef SIOCAUTOCONF_STOP
  85. #define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
  86. #endif
  87. #define P_IFCONFIG "/sbin/ifconfig"
  88. static unsigned char s_pktReadBuf[131072] __attribute__ ((__aligned__(16)));
  89. static unsigned char s_stdinReadBuf[131072] __attribute__ ((__aligned__(16)));
  90. static char s_deviceName[IFNAMSIZ];
  91. static char s_peerDeviceName[IFNAMSIZ];
  92. static int s_bpffd = -1;
  93. static int s_ndrvfd = -1;
  94. static pid_t s_parentPid;
  95. static void configureIpv6Parameters(const char *ifname,int performNUD,int acceptRouterAdverts)
  96. {
  97. struct in6_ndireq nd;
  98. struct in6_ifreq ifr;
  99. int s = socket(AF_INET6,SOCK_DGRAM,0);
  100. if (s <= 0)
  101. return;
  102. memset(&nd,0,sizeof(nd));
  103. strncpy(nd.ifname,ifname,sizeof(nd.ifname));
  104. if (ioctl(s,SIOCGIFINFO_IN6,&nd)) {
  105. close(s);
  106. return;
  107. }
  108. unsigned long oldFlags = (unsigned long)nd.ndi.flags;
  109. if (performNUD)
  110. nd.ndi.flags |= ND6_IFF_PERFORMNUD;
  111. else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
  112. if (oldFlags != (unsigned long)nd.ndi.flags) {
  113. if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) {
  114. close(s);
  115. return;
  116. }
  117. }
  118. memset(&ifr,0,sizeof(ifr));
  119. strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name));
  120. if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) {
  121. close(s);
  122. return;
  123. }
  124. close(s);
  125. }
  126. static int run(const char *path,...)
  127. {
  128. va_list ap;
  129. char *args[16];
  130. int argNo = 1;
  131. va_start(ap,path);
  132. args[0] = (char *)path;
  133. for(;argNo<15;++argNo) {
  134. args[argNo] = va_arg(ap,char *);
  135. if (!args[argNo]) {
  136. break;
  137. }
  138. }
  139. args[argNo++] = (char *)0;
  140. va_end(ap);
  141. pid_t pid = vfork();
  142. if (pid < 0) {
  143. return -1;
  144. } else if (pid == 0) {
  145. dup2(STDERR_FILENO,STDOUT_FILENO);
  146. execv(args[0],args);
  147. _exit(-1);
  148. }
  149. int rv = 0;
  150. waitpid(pid,&rv,0);
  151. return rv;
  152. }
  153. static void die()
  154. {
  155. if (s_ndrvfd >= 0)
  156. close(s_ndrvfd);
  157. if (s_bpffd >= 0)
  158. close(s_bpffd);
  159. if (s_deviceName[0])
  160. run("/sbin/ifconfig",s_deviceName,"destroy",(char *)0);
  161. if (s_peerDeviceName[0])
  162. run("/sbin/ifconfig",s_peerDeviceName,"destroy",(char *)0);
  163. }
  164. int main(int argc,char **argv)
  165. {
  166. char buf[128];
  167. struct ifreq ifr;
  168. u_int fl;
  169. fd_set rfds,wfds,efds;
  170. struct iovec iov[2];
  171. s_deviceName[0] = 0;
  172. s_peerDeviceName[0] = 0;
  173. s_parentPid = getppid();
  174. atexit(&die);
  175. signal(SIGIO,SIG_IGN);
  176. signal(SIGCHLD,SIG_IGN);
  177. signal(SIGPIPE,SIG_IGN);
  178. signal(SIGUSR1,SIG_IGN);
  179. signal(SIGUSR2,SIG_IGN);
  180. signal(SIGALRM,SIG_IGN);
  181. signal(SIGQUIT,&exit);
  182. signal(SIGTERM,&exit);
  183. signal(SIGKILL,&exit);
  184. signal(SIGINT,&exit);
  185. signal(SIGPIPE,&exit);
  186. if (getuid() != 0) {
  187. if (setuid(0) != 0) {
  188. fprintf(stderr,"E must be run as root or with root setuid bit on executable\n");
  189. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_INVALID_REQUEST;
  190. }
  191. }
  192. if (argc < 5) {
  193. fprintf(stderr,"E invalid or missing argument(s) (usage: MacEthernetTapAgent <0-4999> <mac> <mtu> <metric>)\n");
  194. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_INVALID_REQUEST;
  195. }
  196. const int deviceNo = atoi(argv[1]);
  197. if ((deviceNo < 0)||(deviceNo > 4999)) {
  198. fprintf(stderr,"E invalid or missing argument(s) (usage: MacEthernetTapAgent <0-4999> <mac> <mtu> <metric>)\n");
  199. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_INVALID_REQUEST;
  200. }
  201. const char *mac = argv[2];
  202. const char *mtu = argv[3];
  203. const char *metric = argv[4];
  204. s_ndrvfd = socket(AF_NDRV,SOCK_RAW,0);
  205. if (s_ndrvfd < 0) {
  206. fprintf(stderr,"E unable to open AF_NDRV socket\n");
  207. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  208. }
  209. snprintf(s_deviceName,sizeof(s_deviceName),"feth%d",deviceNo);
  210. snprintf(s_peerDeviceName,sizeof(s_peerDeviceName),"feth%d",deviceNo+5000);
  211. if (run(P_IFCONFIG,s_peerDeviceName,"create",(char *)0) != 0) {
  212. fprintf(stderr,"E unable to create %s\n",s_deviceName);
  213. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  214. }
  215. usleep(10);
  216. if (run(P_IFCONFIG,s_deviceName,"create",(char *)0) != 0) {
  217. fprintf(stderr,"E unable to create %s\n",s_deviceName);
  218. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  219. }
  220. run(P_IFCONFIG,s_deviceName,"lladdr",mac,(char *)0);
  221. usleep(10);
  222. run(P_IFCONFIG,s_peerDeviceName,"peer",s_deviceName,(char *)0);
  223. usleep(10);
  224. run(P_IFCONFIG,s_peerDeviceName,"mtu","16370","up",(char *)0); /* 16370 is the largest MTU MacOS/Darwin seems to allow */
  225. usleep(10);
  226. run(P_IFCONFIG,s_deviceName,"mtu",mtu,"metric",metric,"up",(char *)0);
  227. usleep(10);
  228. configureIpv6Parameters(s_deviceName,1,0);
  229. usleep(10);
  230. struct sockaddr_ndrv nd;
  231. nd.snd_len = sizeof(struct sockaddr_ndrv);
  232. nd.snd_family = AF_NDRV;
  233. memcpy(nd.snd_name,s_peerDeviceName,sizeof(nd.snd_name));
  234. if (bind(s_ndrvfd,(struct sockaddr *)&nd,sizeof(nd)) != 0) {
  235. fprintf(stderr,"E unable to bind AF_NDRV socket\n");
  236. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  237. }
  238. if (connect(s_ndrvfd,(struct sockaddr *)&nd,sizeof(nd)) != 0) {
  239. fprintf(stderr,"E unable to connect AF_NDRV socket\n");
  240. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  241. }
  242. /* Start at /dev/bpf1 since some simple bpf-using net utilities hard-code /dev/bpf0.
  243. * Things like libpcap are smart enough to search. */
  244. for(int bpfno=1;bpfno<5000;++bpfno) {
  245. char tmp[32];
  246. snprintf(tmp,sizeof(tmp),"/dev/bpf%d",bpfno);
  247. s_bpffd = open(tmp,O_RDWR);
  248. if (s_bpffd >= 0) {
  249. break;
  250. }
  251. }
  252. if (s_bpffd < 0) {
  253. fprintf(stderr,"E unable to open bpf device\n");
  254. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  255. }
  256. fl = sizeof(s_pktReadBuf);
  257. if (ioctl(s_bpffd,BIOCSBLEN,&fl) != 0) {
  258. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  259. }
  260. const size_t readPktSize = (size_t)fl;
  261. fl = 1;
  262. if (ioctl(s_bpffd,BIOCIMMEDIATE,&fl) != 0) {
  263. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  264. }
  265. fl = 0;
  266. if (ioctl(s_bpffd,BIOCSSEESENT,&fl) != 0) {
  267. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  268. }
  269. memset(&ifr,0,sizeof(ifr));
  270. memcpy(ifr.ifr_name,s_peerDeviceName,IFNAMSIZ);
  271. if (ioctl(s_bpffd,BIOCSETIF,&ifr) != 0) {
  272. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  273. }
  274. fl = 1;
  275. if (ioctl(s_bpffd,BIOCSHDRCMPLT,&fl) != 0) {
  276. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  277. }
  278. fl = 1;
  279. if (ioctl(s_bpffd,BIOCPROMISC,&fl) != 0) {
  280. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_UNABLE_TO_CREATE;
  281. }
  282. fprintf(stderr,"I %s %s %d.%d.%d.%d\n",s_deviceName,s_peerDeviceName,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD);
  283. FD_ZERO(&rfds);
  284. FD_ZERO(&wfds);
  285. FD_ZERO(&efds);
  286. long stdinReadPtr = 0;
  287. for(;;) {
  288. FD_SET(STDIN_FILENO,&rfds);
  289. FD_SET(s_bpffd,&rfds);
  290. if (select(s_bpffd+1,&rfds,&wfds,&efds,(struct timeval *)0) < 0) {
  291. if ((errno == EAGAIN)||(errno == EINTR)) {
  292. usleep(10);
  293. continue;
  294. }
  295. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_READ_ERROR;
  296. }
  297. if (FD_ISSET(s_bpffd,&rfds)) {
  298. long n = (long)read(s_bpffd,s_pktReadBuf,readPktSize);
  299. if (n > 0) {
  300. for(unsigned char *p=s_pktReadBuf,*eof=p+n;p<eof;) {
  301. struct bpf_hdr *h = (struct bpf_hdr *)p;
  302. if ((h->bh_caplen > 0)&&((p + h->bh_hdrlen + h->bh_caplen) <= eof)) {
  303. uint16_t len = (uint16_t)h->bh_caplen;
  304. iov[0].iov_base = &len;
  305. iov[0].iov_len = 2;
  306. iov[1].iov_base = p + h->bh_hdrlen;
  307. iov[1].iov_len = h->bh_caplen;
  308. writev(STDOUT_FILENO,iov,2);
  309. }
  310. p += BPF_WORDALIGN(h->bh_hdrlen + h->bh_caplen);
  311. }
  312. }
  313. }
  314. if (FD_ISSET(STDIN_FILENO,&rfds)) {
  315. long n = (long)read(STDIN_FILENO,s_stdinReadBuf + stdinReadPtr,sizeof(s_stdinReadBuf) - stdinReadPtr);
  316. if (n > 0) {
  317. stdinReadPtr += n;
  318. while (stdinReadPtr >= 2) {
  319. long len = *((uint16_t *)s_stdinReadBuf);
  320. if (stdinReadPtr >= (len + 2)) {
  321. if (len > 0) {
  322. unsigned char *msg = s_stdinReadBuf + 2;
  323. switch(msg[0]) {
  324. case ZT_MACETHERNETTAPAGENT_STDIN_CMD_PACKET:
  325. if (len > 1) {
  326. if (write(s_ndrvfd,msg+1,len-1) < 0) {
  327. fprintf(stderr,"E inject failed size==%ld errno==%d\n",len-1,errno);
  328. }
  329. }
  330. break;
  331. case ZT_MACETHERNETTAPAGENT_STDIN_CMD_IFCONFIG: {
  332. char *args[16];
  333. args[0] = P_IFCONFIG;
  334. args[1] = s_deviceName;
  335. int argNo = 2;
  336. for(int argPtr=0,k=1,l=(int)len;k<l;++k) {
  337. if (!msg[k]) {
  338. if (argPtr > 0) {
  339. argPtr = 0;
  340. ++argNo;
  341. if (argNo >= 15) {
  342. break;
  343. }
  344. }
  345. } else {
  346. if (argPtr == 0) {
  347. args[argNo] = (char *)(msg + k);
  348. }
  349. argPtr++;
  350. }
  351. }
  352. args[argNo] = (char *)0;
  353. if (argNo > 2) {
  354. pid_t pid = fork();
  355. if (pid < 0) {
  356. return -1;
  357. } else if (pid == 0) {
  358. dup2(STDERR_FILENO,STDOUT_FILENO);
  359. execv(args[0],args);
  360. _exit(-1);
  361. }
  362. int rv = 0;
  363. waitpid(pid,&rv,0);
  364. }
  365. } break;
  366. case ZT_MACETHERNETTAPAGENT_STDIN_CMD_EXIT:
  367. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_SUCCESS;
  368. default:
  369. fprintf(stderr,"E unrecognized message type over pipe from host process: %d (length: %d)\n",(int)msg[0],(int)len);
  370. break;
  371. }
  372. }
  373. if (stdinReadPtr > (len + 2)) {
  374. memmove(s_stdinReadBuf,s_stdinReadBuf + len + 2,stdinReadPtr -= (len + 2));
  375. } else {
  376. stdinReadPtr = 0;
  377. }
  378. } else {
  379. break;
  380. }
  381. }
  382. }
  383. }
  384. }
  385. return ZT_MACETHERNETTAPAGENT_EXIT_CODE_SUCCESS;
  386. }