]>
Commit | Line | Data |
---|---|---|
d98f6a46 SS |
1 | /********************************************************** |
2 | SixXS - Automatic IPv6 Connectivity Configuration Utility | |
3 | *********************************************************** | |
4 | Copyright 2003-2005 SixXS - http://www.sixxs.net | |
5 | *********************************************************** | |
6 | common/tun.c - Tunnel Device Handling | |
7 | *********************************************************** | |
8 | $Author: jeroen $ | |
9 | $Id: tun.c,v 1.14 2007-01-11 00:29:18 jeroen Exp $ | |
10 | $Date: 2007-01-11 00:29:18 $ | |
11 | **********************************************************/ | |
12 | ||
13 | #include "tun.h" | |
14 | #include "aiccu.h" | |
15 | ||
16 | /* The tun/tap device HANDLE */ | |
17 | #ifndef _WIN32 | |
18 | int tun_fd; | |
19 | ||
20 | /* | |
21 | * HAS_IFHEAD -> Tunnel Device produces packets with a tun_pi in the front | |
22 | * NEED_IFHEAD -> Tunnel Device produces packets with a tun_pi in the front, but it is not active per default | |
23 | */ | |
24 | ||
25 | #else | |
26 | HANDLE device_handle = INVALID_HANDLE_VALUE; | |
27 | #define ETH_P_IPV6 0x86dd | |
28 | #define ETH_ALEN 6 | |
29 | struct ether_header | |
30 | { | |
31 | uint8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ | |
32 | uint8_t ether_shost[ETH_ALEN]; /* source ether addr */ | |
33 | uint16_t ether_type; /* packet type ID field */ | |
34 | }; | |
35 | ||
36 | /* Tap device constants which we use */ | |
37 | #define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) | |
38 | #define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED) | |
39 | #define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED) | |
40 | #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED) | |
41 | #define TAP_REGISTRY_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" | |
42 | #define TAP_ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" | |
43 | #define TAP_DEVICE_DIR "\\\\.\\Global\\" | |
44 | #define TAP_WIN32_MIN_MAJOR 8 | |
45 | #define TAP_WIN32_MIN_MINOR 1 | |
46 | #define TAP_COMPONENT_ID1 "tap0801" /* Original Tun/Tap driver ID */ | |
47 | #define TAP_COMPONENT_ID2 "tap0802" /* Windows Vista marked 801 as broken, thus use another ID */ | |
48 | ||
49 | #endif | |
50 | ||
51 | void tun_log(int level, const char *what, const char *fmt, ...); | |
52 | void tun_log(int level, const char *what, const char *fmt, ...) | |
53 | { | |
54 | char buf[1024]; | |
55 | va_list ap; | |
56 | ||
57 | /* Clear them just in case */ | |
58 | memset(buf, 0, sizeof(buf)); | |
59 | ||
60 | snprintf(buf, sizeof(buf), "[tun-%s] ", what); | |
61 | ||
62 | /* Print the log message behind it */ | |
63 | va_start(ap, fmt); | |
64 | vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap); | |
65 | va_end(ap); | |
66 | ||
67 | /* Actually Log it */ | |
68 | dolog(level, buf); | |
69 | } | |
70 | ||
71 | static const char reader_name[] = "tundev->tun"; | |
72 | static const char writer_name[] = "tun->tundev"; | |
73 | ||
74 | #ifdef _WIN32 | |
75 | /* Windows doesn't have writev() but does have WSASend */ | |
76 | int writev(SOCKET sock, const struct iovec *vector, DWORD count) | |
77 | { | |
78 | DWORD sent; | |
79 | WSASend(sock, (LPWSABUF)vector, count, &sent, 0, NULL, NULL); | |
80 | return sent; | |
81 | } | |
82 | ||
83 | uint16_t inchksum(const void *data, uint32_t length); | |
84 | uint16_t inchksum(const void *data, uint32_t length) | |
85 | { | |
86 | register long sum = 0; | |
87 | register const uint16_t *wrd = (const uint16_t *)data; | |
88 | register long slen = (long)length; | |
89 | ||
90 | while (slen >= 2) | |
91 | { | |
92 | sum += *wrd++; | |
93 | slen-=2; | |
94 | } | |
95 | ||
96 | if (slen > 0) sum+=*(const uint8_t *)wrd; | |
97 | ||
98 | while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); | |
99 | ||
100 | return (uint16_t)sum; | |
101 | } | |
102 | ||
103 | uint16_t ipv6_checksum(const struct ip6_hdr *ip6, uint8_t protocol, const void *data, const uint16_t length); | |
104 | uint16_t ipv6_checksum(const struct ip6_hdr *ip6, uint8_t protocol, const void *data, const uint16_t length) | |
105 | { | |
106 | struct | |
107 | { | |
108 | uint16_t length; | |
109 | uint16_t zero1; | |
110 | uint8_t zero2; | |
111 | uint8_t next; | |
112 | } pseudo; | |
113 | register uint32_t chksum = 0; | |
114 | ||
115 | pseudo.length = htons(length); | |
116 | pseudo.zero1 = 0; | |
117 | pseudo.zero2 = 0; | |
118 | pseudo.next = protocol; | |
119 | ||
120 | /* IPv6 Source + Dest */ | |
121 | chksum = inchksum(&ip6->ip6_src, sizeof(ip6->ip6_src) + sizeof(ip6->ip6_dst)); | |
122 | chksum += inchksum(&pseudo, sizeof(pseudo)); | |
123 | chksum += inchksum(data, length); | |
124 | ||
125 | /* Wrap in the carries to reduce chksum to 16 bits. */ | |
126 | chksum = (chksum >> 16) + (chksum & 0xffff); | |
127 | chksum += (chksum >> 16); | |
128 | ||
129 | /* Take ones-complement and replace 0 with 0xFFFF. */ | |
130 | chksum = (uint16_t) ~chksum; | |
131 | if (chksum == 0UL) chksum = 0xffffUL; | |
132 | return (uint16_t)chksum; | |
133 | } | |
134 | #endif | |
135 | ||
136 | /* | |
137 | * Tun -> Socket | |
138 | * | |
139 | * Needs to be started in a separate thread | |
140 | * This gets done by tun_start() | |
141 | * | |
142 | */ | |
143 | #ifndef _WIN32 | |
144 | void *tun_reader(void *arg); | |
145 | void *tun_reader(void *arg) | |
146 | #else | |
147 | DWORD WINAPI tun_reader(LPVOID arg); | |
148 | DWORD WINAPI tun_reader(LPVOID arg) | |
149 | #endif | |
150 | { | |
151 | unsigned char buf[2048]; | |
152 | ||
153 | /* The function that actually does something with the buffer */ | |
154 | struct tun_reader *tun = (struct tun_reader *)arg; | |
155 | ||
156 | #ifdef _WIN32 | |
157 | DWORD n, lenin; | |
158 | OVERLAPPED overlapped; | |
159 | unsigned int errcount = 0; | |
160 | ||
161 | struct nd_sol | |
162 | { | |
163 | struct ip6_hdr ip; | |
164 | struct icmp6_hdr icmp; | |
165 | struct nd_neighbor_solicit sol; | |
166 | } *solic = (struct nd_sol *)&buf[sizeof(struct ether)]; | |
167 | ||
168 | struct nd_adv | |
169 | { | |
170 | struct ip6_hdr ip; | |
171 | struct icmp6_hdr icmp; | |
172 | struct nd_neighbor_advert adv; | |
173 | } advert; | |
174 | ||
175 | /* Create an event for overlapped results */ | |
176 | overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
177 | #else | |
178 | ssize_t n; | |
179 | #endif | |
180 | ||
181 | /* Forever */ | |
182 | while (true) | |
183 | { | |
184 | #ifndef _WIN32 | |
185 | n = read(tun_fd, buf, sizeof(buf)); | |
186 | if (n <= 0) | |
187 | { | |
188 | /* Only report issues when the tunnel is actually up and running */ | |
189 | if (g_aiccu->tunrunning) tun_log(LOG_ERR, reader_name, "Read error on Tun Device: %s (%d)\n", strerror(errno), errno); | |
190 | continue; | |
191 | } | |
192 | ||
193 | #if defined(NEED_IFHEAD) || defined(HAS_IFHEAD) | |
194 | /* get the tun_pi struct out of there */ | |
195 | memmove(&buf, &buf[4], n-4); | |
196 | n-=4; | |
197 | #endif | |
198 | ||
199 | tun->function((char *)buf, (unsigned int)n); | |
200 | #else /* Windows */ | |
201 | overlapped.Offset = 0; | |
202 | overlapped.OffsetHigh = 0; | |
203 | ||
204 | memset(buf,0,sizeof(buf)); | |
205 | n = ReadFile(device_handle, buf, sizeof(buf), &lenin, &overlapped); | |
206 | if (!n) | |
207 | { | |
208 | while (!n && GetLastError() == ERROR_IO_PENDING) | |
209 | { | |
210 | if (WaitForSingleObject(overlapped.hEvent, 20000) == WAIT_OBJECT_0) | |
211 | { | |
212 | n = GetOverlappedResult(device_handle, &overlapped, &lenin, FALSE); | |
213 | } | |
214 | } | |
215 | ||
216 | if (!n) | |
217 | { | |
218 | tun_log(LOG_ERR, reader_name, "Error reading from device: %u, %s (%d)\n", GetLastError(), strerror(errno), errno); | |
219 | errcount++; | |
220 | if (errcount > 10) break; | |
221 | continue; | |
222 | } | |
223 | } | |
224 | ||
225 | /* Check for neighbour discovery packets (ICMPv6, ND_SOL, hop=255) | |
226 | * (XXX: doesn't check for a chain, but ND is usually without) | |
227 | */ | |
228 | if ( solic->ip.ip6_ctlun.ip6_un1.ip6_un1_nxt == IPPROTO_ICMPV6 && | |
229 | solic->icmp.icmp6_type == ND_NEIGHBOR_SOLICIT && | |
230 | solic->ip.ip6_ctlun.ip6_un1.ip6_un1_hlim == 255) | |
231 | { | |
232 | /* Ignore unspecified ND's as they are used for DAD */ | |
233 | if (IN6_IS_ADDR_UNSPECIFIED(&solic->ip.ip6_src)) continue; | |
234 | ||
235 | /* Create our reply */ | |
236 | memset(&advert, 0, sizeof(advert)); | |
237 | advert.ip.ip6_ctlun.ip6_un2_vfc = 6 << 4; | |
238 | advert.ip.ip6_ctlun.ip6_un1.ip6_un1_flow = solic->ip.ip6_ctlun.ip6_un1.ip6_un1_flow; | |
239 | advert.ip.ip6_ctlun.ip6_un1.ip6_un1_plen = htons(sizeof(advert.icmp) + sizeof(advert.adv)); | |
240 | advert.ip.ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6; | |
241 | advert.ip.ip6_ctlun.ip6_un1.ip6_un1_hlim = 255; | |
242 | ||
243 | /* Swap src/dst */ | |
244 | memcpy(&advert.ip.ip6_src, &solic->sol.nd_ns_target, sizeof(advert.ip.ip6_src)); | |
245 | memcpy(&advert.ip.ip6_dst, &solic->ip.ip6_src, sizeof(advert.ip.ip6_dst)); | |
246 | ||
247 | /* ICMP Neighbour Advertisement */ | |
248 | advert.icmp.icmp6_type = ND_NEIGHBOR_ADVERT; | |
249 | advert.icmp.icmp6_code = 0; | |
250 | advert.icmp.icmp6_dataun.icmp6_un_data8[0] = 0xe0; | |
251 | memcpy(&advert.adv.nd_na_target, &solic->sol.nd_ns_target, sizeof(advert.adv.nd_na_target)); | |
252 | /* Fake MAC address */ | |
253 | advert.adv.nd_no_type = 2; | |
254 | advert.adv.nd_no_len = 1; | |
255 | advert.adv.nd_no_mac[0] = 0x00; | |
256 | advert.adv.nd_no_mac[1] = 0xff; | |
257 | advert.adv.nd_no_mac[2] = 0x25; | |
258 | advert.adv.nd_no_mac[3] = 0x02; | |
259 | advert.adv.nd_no_mac[4] = 0x19; | |
260 | advert.adv.nd_no_mac[5] = 0x78; | |
261 | ||
262 | /* ICMP has a checksum */ | |
263 | advert.icmp.icmp6_cksum = ipv6_checksum(&advert.ip, IPPROTO_ICMPV6, (uint8_t *)&advert.icmp, sizeof(advert.icmp) + sizeof(advert.adv)); | |
264 | ||
265 | /* We'll need to answer this back to the TAP device */ | |
266 | tun_write((char *)&advert, (unsigned int)sizeof(advert)); | |
267 | continue; | |
268 | } | |
269 | tun->function((char *)&buf[sizeof(struct ether)], (unsigned int)lenin - sizeof(struct ether)); | |
270 | #endif | |
271 | } | |
272 | ||
273 | D(dolog(LOG_DEBUG, "TUN Reader stopping\n")); | |
274 | #ifndef _WIN32 | |
275 | return NULL; | |
276 | #else | |
277 | return 0; | |
278 | #endif | |
279 | } | |
280 | ||
281 | /* Socket -> Tun */ | |
282 | void tun_write(char *buf, unsigned int length) | |
283 | { | |
284 | unsigned int c = 0; | |
285 | #ifndef _WIN32 | |
286 | #ifdef linux | |
287 | struct iovec dat[2]; | |
288 | struct tun_pi pi; | |
289 | memset(&pi, 0, sizeof(pi)); | |
290 | ||
291 | pi.proto = htons(ETH_P_IPV6); | |
292 | ||
293 | dat[0].iov_base = π | |
294 | dat[0].iov_len = sizeof(pi); | |
295 | dat[1].iov_base = buf; | |
296 | dat[1].iov_len = length; | |
297 | ||
298 | length += sizeof(pi); | |
299 | ||
300 | /* Forward the packet to the kernel */ | |
301 | c = writev(tun_fd, dat, 2); | |
302 | ||
303 | #else /* *BSD/Darwin */ | |
304 | ||
305 | uint32_t type = htonl(AF_INET6); | |
306 | struct iovec dat[2]; | |
307 | ||
308 | dat[0].iov_base = (void *)&type; | |
309 | dat[0].iov_len = sizeof(type); | |
310 | dat[1].iov_base = buf; | |
311 | dat[1].iov_len = length; | |
312 | ||
313 | length += sizeof(type); | |
314 | ||
315 | /* Forward the packet to the kernel */ | |
316 | c = writev(tun_fd, dat, 2); | |
317 | ||
318 | #endif | |
319 | ||
320 | if (c != length) | |
321 | { | |
322 | tun_log(LOG_ERR, writer_name, "Error while writing to TUN: %u != %u\n", c, length); | |
323 | } | |
324 | ||
325 | #else /* Windows */ | |
326 | DWORD n, lenout; | |
327 | OVERLAPPED overlapped; | |
328 | unsigned char mbuf[4096]; | |
329 | ||
330 | struct ether *eth = (struct ether *)mbuf; | |
331 | ||
332 | /* Sent the packet outbound */ | |
333 | overlapped.Offset = 0; | |
334 | overlapped.OffsetHigh = 0; | |
335 | overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
336 | ||
337 | memset(mbuf,0,sizeof(mbuf)); | |
338 | eth->ether_dhost[0] = htons(0x3333); | |
339 | eth->ether_dhost[1] = htons(0xff00); | |
340 | eth->ether_dhost[2] = htons(0x0002); | |
341 | eth->ether_shost[0] = htons(0x00ff); | |
342 | eth->ether_shost[1] = htons(0x5342); | |
343 | eth->ether_shost[2] = htons(0x2768); | |
344 | eth->ether_type = htons(ETH_P_IPV6); | |
345 | memcpy(&mbuf[sizeof(*eth)],buf,length); | |
346 | ||
347 | n = WriteFile(device_handle, mbuf, sizeof(*eth)+length, &lenout, &overlapped); | |
348 | if (!n && GetLastError() == ERROR_IO_PENDING) | |
349 | { | |
350 | WaitForSingleObject(overlapped.hEvent, INFINITE); | |
351 | n = GetOverlappedResult(device_handle, &overlapped, &lenout, FALSE); | |
352 | } | |
353 | ||
354 | if (!n) | |
355 | { | |
356 | tun_log(LOG_ERR, writer_name, "Error writing to device: %u, %s (%d)\n", GetLastError(), strerror(errno), errno); | |
357 | } | |
358 | #endif | |
359 | } | |
360 | ||
361 | #ifdef _WIN32 | |
362 | ||
363 | struct tap_reg | |
364 | { | |
365 | char *guid; | |
366 | struct tap_reg *next; | |
367 | }; | |
368 | ||
369 | struct panel_reg | |
370 | { | |
371 | char *name; | |
372 | char *guid; | |
373 | struct panel_reg *next; | |
374 | }; | |
375 | ||
376 | /* Get a working tunnel adapter */ | |
377 | struct tap_reg *get_tap_reg(void) | |
378 | { | |
379 | HKEY adapter_key; | |
380 | LONG status; | |
381 | DWORD len; | |
382 | struct tap_reg *first = NULL; | |
383 | struct tap_reg *last = NULL; | |
384 | int i = 0; | |
385 | ||
386 | status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY, 0, KEY_READ, &adapter_key); | |
387 | if (status != ERROR_SUCCESS) | |
388 | { | |
389 | dolog(LOG_ERR, "Error opening registry key: %s\n", TAP_ADAPTER_KEY); | |
390 | return NULL; | |
391 | } | |
392 | ||
393 | while (true) | |
394 | { | |
395 | char enum_name[256]; | |
396 | char unit_string[256]; | |
397 | HKEY unit_key; | |
398 | char component_id_string[] = "ComponentId"; | |
399 | char component_id[256]; | |
400 | char net_cfg_instance_id_string[] = "NetCfgInstanceId"; | |
401 | char net_cfg_instance_id[256]; | |
402 | DWORD data_type; | |
403 | ||
404 | len = sizeof(enum_name); | |
405 | status = RegEnumKeyEx(adapter_key, i, enum_name, &len, NULL, NULL, NULL, NULL); | |
406 | if (status == ERROR_NO_MORE_ITEMS) break; | |
407 | else if (status != ERROR_SUCCESS) | |
408 | { | |
409 | dolog(LOG_ERR, "Error enumerating registry subkeys of key: %s (t0)\n", TAP_ADAPTER_KEY); | |
410 | break; | |
411 | } | |
412 | ||
413 | snprintf(unit_string, sizeof(unit_string), "%s\\%s", TAP_ADAPTER_KEY, enum_name); | |
414 | status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key); | |
415 | if (status != ERROR_SUCCESS) | |
416 | { | |
417 | dolog(LOG_WARNING, "Error opening registry key: %s (t1)\n", unit_string); | |
418 | } | |
419 | else | |
420 | { | |
421 | len = sizeof(component_id); | |
422 | status = RegQueryValueEx(unit_key, component_id_string, NULL, &data_type, (LPBYTE)component_id, &len); | |
423 | if (status != ERROR_SUCCESS || data_type != REG_SZ) | |
424 | { | |
425 | dolog(LOG_WARNING, "Error opening registry key: %s\\%s (t2)\n", unit_string, component_id_string); | |
426 | } | |
427 | else | |
428 | { | |
429 | len = sizeof(net_cfg_instance_id); | |
430 | status = RegQueryValueEx(unit_key, net_cfg_instance_id_string, NULL, &data_type, (LPBYTE)net_cfg_instance_id, &len); | |
431 | if (status == ERROR_SUCCESS && data_type == REG_SZ) | |
432 | { | |
433 | if ( strcmp(component_id, TAP_COMPONENT_ID1) == 0 || | |
434 | strcmp(component_id, TAP_COMPONENT_ID2) == 0) | |
435 | { | |
436 | struct tap_reg *reg = (struct tap_reg *)malloc(sizeof(*reg)); | |
437 | memset(reg, 0, sizeof(*reg)); | |
438 | reg->guid = strdup(net_cfg_instance_id); | |
439 | ||
440 | if (!first) first = reg; | |
441 | if (last) last->next = reg; | |
442 | last = reg; | |
443 | } | |
444 | } | |
445 | } | |
446 | ||
447 | RegCloseKey(unit_key); | |
448 | } | |
449 | i++; | |
450 | } | |
451 | ||
452 | RegCloseKey(adapter_key); | |
453 | return first; | |
454 | } | |
455 | ||
456 | void free_tap_reg(struct tap_reg *tap_reg) | |
457 | { | |
458 | struct tap_reg *tr, *tr1; | |
459 | ||
460 | for (tr = tap_reg; tr != NULL; tr = tr1) | |
461 | { | |
462 | tr1 = tr->next; | |
463 | free(tr->guid); | |
464 | free(tr); | |
465 | } | |
466 | } | |
467 | ||
468 | void free_panel_reg(struct panel_reg *panel_reg) | |
469 | { | |
470 | struct panel_reg *pr, *pr1; | |
471 | ||
472 | for (pr = panel_reg; pr != NULL; pr = pr1) | |
473 | { | |
474 | pr1 = pr->next; | |
475 | free(pr->guid); | |
476 | free(pr->name); | |
477 | free(pr); | |
478 | } | |
479 | } | |
480 | ||
481 | ||
482 | /* Collect GUID's and names of all the Connections that are available */ | |
483 | struct panel_reg *get_panel_reg(void) | |
484 | { | |
485 | LONG status; | |
486 | HKEY network_connections_key; | |
487 | DWORD len; | |
488 | struct panel_reg *first = NULL; | |
489 | struct panel_reg *last = NULL; | |
490 | int i = 0; | |
491 | ||
492 | status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_REGISTRY_KEY, 0, KEY_READ, &network_connections_key); | |
493 | ||
494 | if (status != ERROR_SUCCESS) | |
495 | { | |
496 | dolog(LOG_ERR, "Error opening registry key: %s (p0)\n", TAP_REGISTRY_KEY); | |
497 | return NULL; | |
498 | } | |
499 | ||
500 | while (true) | |
501 | { | |
502 | char enum_name[256]; | |
503 | char connection_string[256]; | |
504 | HKEY connection_key; | |
505 | char name_data[256]; | |
506 | DWORD name_type; | |
507 | const char name_string[] = "Name"; | |
508 | ||
509 | len = sizeof(enum_name); | |
510 | status = RegEnumKeyEx(network_connections_key, i, enum_name, &len, NULL, NULL, NULL, NULL); | |
511 | if (status == ERROR_NO_MORE_ITEMS) break; | |
512 | else if (status != ERROR_SUCCESS) | |
513 | { | |
514 | dolog(LOG_ERR, "Error enumerating registry subkeys of key: %s (p1)\n", TAP_REGISTRY_KEY); | |
515 | break; | |
516 | } | |
517 | ||
518 | i++; | |
519 | ||
520 | if (enum_name[0] != '{') continue; | |
521 | ||
522 | snprintf(connection_string, sizeof(connection_string), "%s\\%s\\Connection", TAP_REGISTRY_KEY, enum_name); | |
523 | ||
524 | status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ, &connection_key); | |
525 | if (status != ERROR_SUCCESS) | |
526 | { | |
527 | dolog(LOG_WARNING, "Error opening registry key: %s (p2)\n", connection_string); | |
528 | } | |
529 | else | |
530 | { | |
531 | len = sizeof(name_data); | |
532 | status = RegQueryValueEx(connection_key, name_string, NULL, &name_type, (LPBYTE)name_data, &len); | |
533 | ||
534 | if (status != ERROR_SUCCESS || name_type != REG_SZ) | |
535 | { | |
536 | dolog(LOG_WARNING, "Error opening registry key: %s\\%s\\%s (p3)\n", TAP_REGISTRY_KEY, (LPBYTE)connection_string, name_string); | |
537 | } | |
538 | else | |
539 | { | |
540 | struct panel_reg *reg = (struct panel_reg *)malloc(sizeof(*reg)); | |
541 | memset(reg, 0, sizeof(*reg)); | |
542 | reg->name = strdup(name_data); | |
543 | reg->guid = strdup(enum_name); | |
544 | ||
545 | /* link into return list */ | |
546 | if (!first) first = reg; | |
547 | if (last) last->next = reg; | |
548 | last = reg; | |
549 | } | |
550 | ||
551 | RegCloseKey(connection_key); | |
552 | } | |
553 | } | |
554 | ||
555 | RegCloseKey(network_connections_key); | |
556 | ||
557 | return first; | |
558 | } | |
559 | ||
560 | void tun_list_tap_adapters(void) | |
561 | { | |
562 | int links; | |
563 | struct tap_reg *tap_reg = get_tap_reg(), *tr, *tr1; | |
564 | struct panel_reg *panel_reg = get_panel_reg(), *pr; | |
565 | ||
566 | dolog(LOG_INFO, "Available TAP-WIN32 adapters [name, GUID]:\n"); | |
567 | ||
568 | /* loop through each TAP-Win32 adapter registry entry */ | |
569 | for (tr = tap_reg; tr != NULL; tr = tr->next) | |
570 | { | |
571 | links = 0; | |
572 | ||
573 | /* loop through each network connections entry in the control panel */ | |
574 | for (pr = panel_reg; pr != NULL; pr = pr->next) | |
575 | { | |
576 | if (strcmp(tr->guid, pr->guid) == 0) | |
577 | { | |
578 | dolog(LOG_INFO, "'%s' %s\n", pr->name, tr->guid); | |
579 | links++; | |
580 | } | |
581 | } | |
582 | ||
583 | if (links > 1) | |
584 | { | |
585 | dolog(LOG_WARNING, "*** Adapter with GUID %s has %u links from the Network Connections control panel, it should only be 1\n", tr->guid, links); | |
586 | } | |
587 | else if (links == 0) | |
588 | { | |
589 | dolog(LOG_WARNING, "[NULL] %s\n", tr->guid); | |
590 | dolog(LOG_WARNING, "*** Adapter with GUID %s doesn't have a link from the control panel\n", tr->guid); | |
591 | } | |
592 | ||
593 | /* check for TAP-Win32 adapter duplicated GUIDs */ | |
594 | for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next) | |
595 | { | |
596 | if (tr != tr1 && strcmp(tr->guid, tr1->guid) == 0) | |
597 | { | |
598 | dolog(LOG_WARNING, "*** Duplicate Adapter GUID %s\n", tr->guid); | |
599 | } | |
600 | } | |
601 | } | |
602 | ||
603 | free_tap_reg(tap_reg); | |
604 | free_panel_reg(panel_reg); | |
605 | } | |
606 | ||
607 | bool tun_fixup_adapters(void) | |
608 | { | |
609 | int links, count = 0, found = 0; | |
610 | struct tap_reg *tap_reg = get_tap_reg(), *tr = NULL, *tr1 = NULL; | |
611 | struct panel_reg *panel_reg = get_panel_reg(), *pr = NULL, *first = NULL, *prf = NULL; | |
612 | bool ok; | |
613 | ||
614 | /* loop through each TAP-Win32 adapter registry entry */ | |
615 | for (tr = tap_reg; tr != NULL; tr = tr->next) | |
616 | { | |
617 | links = 0; | |
618 | ok = true; | |
619 | ||
620 | /* loop through each network connections entry in the control panel */ | |
621 | for (pr = panel_reg; pr != NULL; pr = pr->next) | |
622 | { | |
623 | if (strcmp(tr->guid, pr->guid) == 0) | |
624 | { | |
625 | links++; | |
626 | prf = pr; | |
627 | ||
628 | /* Is this the one wanted by the user? */ | |
629 | if (strcasecmp(g_aiccu->ipv6_interface, pr->name) == 0) found++; | |
630 | } | |
631 | } | |
632 | ||
633 | if (links > 1) | |
634 | { | |
635 | dolog(LOG_WARNING, "*** Adapter with GUID %s has %u links from the Network Connections control panel, it should only be 1\n", tr->guid, links); | |
636 | ok = false; | |
637 | } | |
638 | else if (links == 0) | |
639 | { | |
640 | dolog(LOG_WARNING, "[NULL] %s\n", tr->guid); | |
641 | dolog(LOG_WARNING, "*** Adapter with GUID %s doesn't have a link from the control panel\n", tr->guid); | |
642 | ok = false; | |
643 | } | |
644 | ||
645 | /* check for TAP-Win32 adapter duplicated GUIDs */ | |
646 | for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next) | |
647 | { | |
648 | if (tr != tr1 && strcmp(tr->guid, tr1->guid) == 0) | |
649 | { | |
650 | dolog(LOG_WARNING, "*** Duplicate Adapter GUID %s\n", tr->guid); | |
651 | ok = false; | |
652 | } | |
653 | } | |
654 | ||
655 | if (ok) | |
656 | { | |
657 | count++; | |
658 | first = prf; | |
659 | } | |
660 | } | |
661 | ||
662 | ok = false; | |
663 | ||
664 | /* When the user didn't configure us correctly and we find a single TAP interface, just rename it */ | |
665 | if (found == 0 && count == 1 && first) | |
666 | { | |
667 | dolog(LOG_INFO, "Renaming adapter '%s' to '%s' and using it\n", first->name, g_aiccu->ipv6_interface); | |
668 | aiccu_win32_rename_adapter(first->name); | |
669 | ok = true; | |
670 | } | |
671 | else if (found == 1 && count == 1) | |
672 | { | |
673 | D(dolog(LOG_DEBUG, "Using configured interface %s\n", g_aiccu->ipv6_interface)); | |
674 | ok = true; | |
675 | } | |
676 | else | |
677 | { | |
678 | ok = false; | |
679 | dolog(LOG_WARNING, "Found = %u, Count = %u\n", found, count); | |
680 | } | |
681 | ||
682 | free_tap_reg(tap_reg); | |
683 | free_panel_reg(panel_reg); | |
684 | ||
685 | return ok; | |
686 | } | |
687 | ||
688 | #endif | |
689 | ||
690 | bool tun_start(struct tun_reader *tun) | |
691 | { | |
692 | #ifndef _WIN32 | |
693 | pthread_t thread; | |
694 | #ifdef linux | |
695 | struct ifreq ifr; | |
696 | ||
697 | /* Create a new tap device */ | |
698 | tun_fd = open("/dev/net/tun", O_RDWR); | |
699 | if (tun_fd == -1) | |
700 | { | |
701 | tun_log(LOG_ERR, "start", "Couldn't open device %s: %s (%d)\n", "/dev/net/tun", strerror(errno), errno); | |
702 | return false; | |
703 | } | |
704 | ||
705 | memset(&ifr, 0, sizeof(ifr)); | |
706 | /* Request a TUN device */ | |
707 | ifr.ifr_flags = IFF_TUN; | |
708 | /* Set the interface name */ | |
709 | strncpy(ifr.ifr_name, g_aiccu->ipv6_interface, sizeof(ifr.ifr_name)); | |
710 | ||
711 | if (ioctl(tun_fd, TUNSETIFF, &ifr)) | |
712 | { | |
713 | tun_log(LOG_ERR, "start", "Couldn't set interface name to %s: %s (%d)\n", | |
714 | g_aiccu->ipv6_interface, strerror(errno), errno); | |
715 | return false; | |
716 | } | |
717 | ||
718 | #else /* *BSD/Darwin */ | |
719 | ||
720 | char buf[128]; | |
721 | unsigned int i; | |
722 | int mode = IFF_MULTICAST | IFF_POINTOPOINT; | |
723 | ||
724 | /* Try the configured interface */ | |
725 | tun_log(LOG_DEBUG, "start", "Trying Configured TUN/TAP interface %s...\n", g_aiccu->ipv6_interface); | |
726 | snprintf(buf, sizeof(buf), "/dev/%s", g_aiccu->ipv6_interface); | |
727 | tun_fd = open(buf, O_RDWR); | |
728 | if (tun_fd < 0) | |
729 | { | |
730 | /* Fall back to trying all /dev/tun* devices */ | |
731 | for (i = 0; i < 256; ++i) | |
732 | { | |
733 | snprintf(buf, sizeof(buf), "/dev/tun%u", i); | |
734 | tun_log(LOG_DEBUG, "start", "Trying TUN/TAP interface %s...\n", &buf[8]); | |
735 | tun_fd = open(buf, O_RDWR); | |
736 | if (tun_fd >= 0) | |
737 | { | |
738 | /* Copy over the name of the interface so that configging goes okay */ | |
739 | if (g_aiccu->ipv6_interface) free(g_aiccu->ipv6_interface); | |
740 | snprintf(buf, sizeof(buf), "tun%u", i); | |
741 | g_aiccu->ipv6_interface = strdup(buf); | |
742 | } | |
743 | break; | |
744 | } | |
745 | } | |
746 | ||
747 | if (tun_fd < 0) | |
748 | { | |
749 | tun_log(LOG_ERR, "start", "Couldn't open device %s or /dev/tun*: %s (%d)\n", g_aiccu->ipv6_interface, strerror(errno), errno); | |
750 | return false; | |
751 | } | |
752 | ||
753 | tun_log(LOG_DEBUG, "start", "Using TUN/TAP interface %s\n", g_aiccu->ipv6_interface); | |
754 | ||
755 | #ifndef _FREEBSD | |
756 | #ifndef _DARWIN | |
757 | #ifndef _AIX | |
758 | tun_log(LOG_DEBUG, "start", "Setting TUNSIFMODE for %s\n", g_aiccu->ipv6_interface); | |
759 | if (ioctl(tun_fd, TUNSIFMODE, &mode, sizeof(mode)) == -1) | |
760 | { | |
761 | tun_log(LOG_ERR, "start", "Couldn't set interface %s's TUNSIFMODE to MULTICAST|POINTOPOINT: %s (%d)\n", | |
762 | g_aiccu->ipv6_interface, strerror(errno), errno); | |
763 | close(tun_fd); | |
764 | tun_fd = -1; | |
765 | return false; | |
766 | } | |
767 | #endif | |
768 | #endif | |
769 | #endif | |
770 | ||
771 | #ifdef NEED_IFHEAD | |
772 | tun_log(LOG_DEBUG, "start", "Setting TUNSIFHEAD for %s\n", g_aiccu->ipv6_interface); | |
773 | mode = 1; | |
774 | if (ioctl(tun_fd, TUNSIFHEAD, &mode, sizeof(mode)) == -1) | |
775 | { | |
776 | tun_log(LOG_ERR, "start", "Couldn't set interface %s's TUNSIFHEAD to enabled: %s (%d)\n", | |
777 | g_aiccu->ipv6_interface, strerror(errno), errno); | |
778 | close(tun_fd); | |
779 | tun_fd = -1; | |
780 | return false; | |
781 | } | |
782 | #endif | |
783 | ||
784 | #endif /* linux */ | |
785 | ||
786 | ||
787 | #else /* Windows */ | |
788 | ||
789 | HKEY key; | |
790 | DWORD pID; | |
791 | HANDLE h; | |
792 | int i; | |
793 | ||
794 | char adapterid[1024]; | |
795 | char tapname[1024]; | |
796 | DWORD len; | |
797 | ||
798 | if (!tun_fixup_adapters()) | |
799 | { | |
800 | tun_log(LOG_ERR, "start", "TAP-Win32 Adapter not configured properly...\n"); | |
801 | return false; | |
802 | } | |
803 | ||
804 | /* Open registry and look for network adapters */ | |
805 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_REGISTRY_KEY, 0, KEY_READ, &key)) | |
806 | { | |
807 | tun_log(LOG_ERR, "start", "Could not open the networking registry key\n"); | |
808 | return false; | |
809 | } | |
810 | ||
811 | for (i = 0; device_handle == INVALID_HANDLE_VALUE; i++) | |
812 | { | |
813 | len = sizeof(adapterid); | |
814 | if (RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) break; | |
815 | ||
816 | snprintf(tapname, sizeof(tapname), TAP_DEVICE_DIR "%s.tap", adapterid); | |
817 | tun_log(LOG_DEBUG, "start", "Trying %s\n", tapname); | |
818 | device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); | |
819 | ||
820 | if (device_handle != INVALID_HANDLE_VALUE) | |
821 | { | |
822 | unsigned long status, info[3] = {0,0,0}; | |
823 | ||
824 | /* get driver version info */ | |
825 | if (DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info), &len, NULL)) | |
826 | { | |
827 | D(tun_log(LOG_DEBUG, "start", "TAP-Win32 Driver Version %d.%d %s", (int)info[0], (int)info[1], info[2] ? "(DEBUG)" : "")); | |
828 | } | |
829 | ||
830 | if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR))) | |
831 | { | |
832 | tun_log(LOG_ERR, "start", "A TAP-Win32 driver is required that is at least version %d.%d -- If you recently upgraded your Tap32 driver, a reboot is probably required at this point to get Windows to see the new driver.", TAP_WIN32_MIN_MAJOR, TAP_WIN32_MIN_MINOR); | |
833 | CloseHandle(device_handle); | |
834 | device_handle = INVALID_HANDLE_VALUE; | |
835 | continue; | |
836 | } | |
837 | ||
838 | /* Note: we use TAP mode on Windows, not TUN */ | |
839 | ||
840 | /* Try to mark the device as 'up */ | |
841 | status = true; | |
842 | DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL); | |
843 | } | |
844 | } | |
845 | ||
846 | RegCloseKey(key); | |
847 | ||
848 | if (device_handle == INVALID_HANDLE_VALUE) | |
849 | { | |
850 | tun_log(LOG_ERR, "start", "No working Tap device found!\n"); | |
851 | return false; | |
852 | } | |
853 | ||
854 | #endif /* _WIN32 */ | |
855 | ||
856 | /* Launch a thread for reader */ | |
857 | #ifndef _WIN32 | |
858 | pthread_create(&thread, NULL, tun_reader, (void *)tun); | |
859 | #else | |
860 | h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, tun, 0, &pID); | |
861 | #endif | |
862 | ||
863 | /* We now return, the real tunneling tool can call tun_write() when it wants */ | |
864 | ||
865 | return true; | |
866 | } |