device.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. device.c -- Interaction with Windows tap driver in a MinGW environment
  3. Copyright (C) 2002-2005 Ivo Timmermans,
  4. 2002-2016 Guus Sliepen <guus@tinc-vpn.org>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License along
  14. with this program; if not, write to the Free Software Foundation, Inc.,
  15. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. */
  17. #include "../system.h"
  18. #include <windows.h>
  19. #include <winioctl.h>
  20. #include "../conf.h"
  21. #include "../device.h"
  22. #include "../logger.h"
  23. #include "../net.h"
  24. #include "../route.h"
  25. #include "../utils.h"
  26. #include "../xalloc.h"
  27. #include "common.h"
  28. int device_fd = -1;
  29. static HANDLE device_handle = INVALID_HANDLE_VALUE;
  30. char *device = NULL;
  31. char *iface = NULL;
  32. static char *device_info = NULL;
  33. static uint64_t device_total_in = 0;
  34. static uint64_t device_total_out = 0;
  35. extern char *myport;
  36. OVERLAPPED r_overlapped;
  37. OVERLAPPED w_overlapped;
  38. static DWORD WINAPI tapreader(void *bla) {
  39. int status;
  40. DWORD len;
  41. vpn_packet_t packet;
  42. int errors = 0;
  43. logger(LOG_DEBUG, "Tap reader running");
  44. /* Read from tap device and send to parent */
  45. r_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  46. for(;;) {
  47. ResetEvent(r_overlapped.hEvent);
  48. status = ReadFile(device_handle, packet.data, MTU, &len, &r_overlapped);
  49. if(!status) {
  50. if(GetLastError() == ERROR_IO_PENDING) {
  51. WaitForSingleObject(r_overlapped.hEvent, INFINITE);
  52. if(!GetOverlappedResult(device_handle, &r_overlapped, &len, FALSE)) {
  53. continue;
  54. }
  55. } else {
  56. logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
  57. device, strerror(errno));
  58. errors++;
  59. if(errors >= 10) {
  60. EnterCriticalSection(&mutex);
  61. running = false;
  62. LeaveCriticalSection(&mutex);
  63. }
  64. usleep(1000000);
  65. continue;
  66. }
  67. }
  68. errors = 0;
  69. packet.len = len;
  70. packet.priority = 0;
  71. EnterCriticalSection(&mutex);
  72. route(myself, &packet);
  73. LeaveCriticalSection(&mutex);
  74. }
  75. return 0;
  76. }
  77. static bool setup_device(void) {
  78. HKEY key, key2;
  79. int i;
  80. char regpath[1024];
  81. char adapterid[1024];
  82. char adaptername[1024];
  83. char tapname[1024];
  84. DWORD len;
  85. unsigned long status;
  86. bool found = false;
  87. int err;
  88. HANDLE thread;
  89. get_config_string(lookup_config(config_tree, "Device"), &device);
  90. get_config_string(lookup_config(config_tree, "Interface"), &iface);
  91. if(device && iface) {
  92. logger(LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
  93. }
  94. /* Open registry and look for network adapters */
  95. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
  96. logger(LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
  97. return false;
  98. }
  99. for(i = 0; ; i++) {
  100. len = sizeof(adapterid);
  101. if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
  102. break;
  103. }
  104. /* Find out more about this adapter */
  105. snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
  106. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
  107. continue;
  108. }
  109. len = sizeof(adaptername);
  110. err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
  111. RegCloseKey(key2);
  112. if(err) {
  113. continue;
  114. }
  115. if(device) {
  116. if(!strcmp(device, adapterid)) {
  117. found = true;
  118. break;
  119. } else {
  120. continue;
  121. }
  122. }
  123. if(iface) {
  124. if(!strcmp(iface, adaptername)) {
  125. found = true;
  126. break;
  127. } else {
  128. continue;
  129. }
  130. }
  131. snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
  132. device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
  133. if(device_handle != INVALID_HANDLE_VALUE) {
  134. found = true;
  135. break;
  136. }
  137. }
  138. RegCloseKey(key);
  139. if(!found) {
  140. logger(LOG_ERR, "No Windows tap device found!");
  141. return false;
  142. }
  143. if(!device) {
  144. device = xstrdup(adapterid);
  145. }
  146. if(!iface) {
  147. iface = xstrdup(adaptername);
  148. }
  149. /* Try to open the corresponding tap device */
  150. if(device_handle == INVALID_HANDLE_VALUE) {
  151. snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
  152. device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
  153. }
  154. if(device_handle == INVALID_HANDLE_VALUE) {
  155. logger(LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
  156. return false;
  157. }
  158. /* Get MAC address from tap device */
  159. if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
  160. logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
  161. return false;
  162. }
  163. if(routing_mode == RMODE_ROUTER) {
  164. overwrite_mac = 1;
  165. }
  166. /* Create overlapped events for tap I/O */
  167. r_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  168. w_overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  169. /* Start the tap reader */
  170. thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
  171. if(!thread) {
  172. logger(LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
  173. return false;
  174. }
  175. /* Set media status for newer TAP-Win32 devices */
  176. status = true;
  177. DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
  178. device_info = "Windows tap device";
  179. logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
  180. return true;
  181. }
  182. static void close_device(void) {
  183. CloseHandle(device_handle);
  184. free(device);
  185. free(iface);
  186. }
  187. static bool read_packet(vpn_packet_t *packet) {
  188. return false;
  189. }
  190. static bool write_packet(vpn_packet_t *packet) {
  191. DWORD lenout;
  192. static vpn_packet_t queue;
  193. ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
  194. packet->len, device_info);
  195. /* Check if there is something in progress */
  196. if(queue.len) {
  197. DWORD size;
  198. BOOL success = GetOverlappedResult(device_handle, &w_overlapped, &size, FALSE);
  199. if(success) {
  200. ResetEvent(&w_overlapped);
  201. queue.len = 0;
  202. } else {
  203. int err = GetLastError();
  204. if(err != ERROR_IO_INCOMPLETE) {
  205. ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error completing previously queued write: %s", winerror(err));
  206. ResetEvent(&w_overlapped);
  207. queue.len = 0;
  208. } else {
  209. ifdebug(TRAFFIC) logger(LOG_DEBUG, "Previous overlapped write still in progress");
  210. // drop this packet
  211. return true;
  212. }
  213. }
  214. }
  215. /* Otherwise, try to write. */
  216. memcpy(queue.data, packet->data, packet->len);
  217. if(!WriteFile(device_handle, queue.data, packet->len, &lenout, &w_overlapped)) {
  218. int err = GetLastError();
  219. if(err != ERROR_IO_PENDING) {
  220. logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(err));
  221. return false;
  222. }
  223. // Write is being done asynchronously.
  224. queue.len = packet->len;
  225. } else {
  226. // Write was completed immediately.
  227. ResetEvent(&w_overlapped);
  228. }
  229. device_total_out += packet->len;
  230. return true;
  231. }
  232. static void dump_device_stats(void) {
  233. logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
  234. logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
  235. logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  236. }
  237. const devops_t os_devops = {
  238. .setup = setup_device,
  239. .close = close_device,
  240. .read = read_packet,
  241. .write = write_packet,
  242. .dump_stats = dump_device_stats,
  243. };