]> git.ipfire.org Git - people/stevee/aiccu.git/blame - common/ayiya.c
Add setup script functionality to Linux client
[people/stevee/aiccu.git] / common / ayiya.c
CommitLineData
d98f6a46
SS
1/**********************************************************
2 SixXS - Automatic IPv6 Connectivity Configuration Utility
3***********************************************************
4 Copyright 2003-2005 SixXS - http://www.sixxs.net
5***********************************************************
6 common/ayiya.c - AYIYA - Anything In Anything
7***********************************************************
8 $Author: jeroen $
9 $Id: ayiya.c,v 1.15 2007-01-07 16:17:48 jeroen Exp $
10 $Date: 2007-01-07 16:17:48 $
11**********************************************************/
12
13#include "aiccu.h"
14#include "ayiya.h"
15#include "tic.h"
16#include "tun.h"
17
18struct pseudo_ayh
19{
20 struct ayiyahdr ayh;
21 struct in6_addr identity;
22 sha1_byte hash[SHA1_DIGEST_LENGTH];
23 char payload[2048];
24};
25
26struct in_addr ayiya_ipv4_pop; /* IPv4 remote endpoint */
27struct in6_addr ayiya_ipv6_local, /* IPv6 local endpoint */
28 ayiya_ipv6_pop; /* IPv6 remote endpoint */
29sha1_byte ayiya_hash[SHA1_DIGEST_LENGTH]; /* SHA1 Hash of the shared secret. */
30
31TLSSOCKET ayiya_socket = NULL;
32
33static const char reader_name[] = "tundev->tun";
34static const char writer_name[] = "tun->tundev";
35static const char beat_name[] = "beat";
36
37void ayiya_log(int level, const char *what, struct sockaddr_storage *clientaddr, socklen_t addrlen, const char *fmt, ...);
38void ayiya_log(int level, const char *what, struct sockaddr_storage *clientaddr, socklen_t addrlen, const char *fmt, ...)
39{
40 char buf[1024];
41 char clienthost[NI_MAXHOST];
42 char clientservice[NI_MAXSERV];
43 va_list ap;
44
45 /* Clear them just in case */
46 memset(buf, 0, sizeof(buf));
47 memset(clienthost, 0, sizeof(clienthost));
48 memset(clientservice, 0, sizeof(clientservice));
49
50 if (clientaddr)
51 {
52 int ret;
53 ret = getnameinfo((struct sockaddr *)clientaddr, addrlen,
54 clienthost, sizeof(clienthost),
55 clientservice, sizeof(clientservice),
56 NI_NUMERICHOST|NI_NUMERICSERV);
57 if (ret != 0)
58 {
59 dolog(LOG_ERR, "ayiya_log() getnameinfo() ret: %d, errno: %u, %s\n", ret, errno, strerror(errno));
60 }
61 }
62
63 /* Print the host+port this is coming from */
64 snprintf(buf, sizeof(buf), "[AYIYA-%s]%s%s%s%s : ",
65 what,
66 clientaddr ? " [" : "",
67 clientaddr ? clienthost : "" ,
68 clientaddr ? "]:" : "",
69 clientservice ? clientservice : "");
70
71 /* Print the log message behind it */
72 va_start(ap, fmt);
73 vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap);
74 va_end(ap);
75
76 /* Actually Log it */
77 dolog(level, buf);
78}
79
80/* Tun -> Socket */
81void ayiya_reader(char *buf, unsigned int length);
82void ayiya_reader(char *buf, unsigned int length)
83{
84 struct pseudo_ayh *s = (struct pseudo_ayh *)buf, s2;
85 int lenout;
86 SHA_CTX sha1;
87 sha1_byte hash[SHA1_DIGEST_LENGTH];
88 struct sockaddr_in target;
89
90 /* We tunnel over IPv4 */
91 memcpy(&target.sin_addr, &ayiya_ipv4_pop, sizeof(target.sin_addr));
92 target.sin_family = AF_INET;
93 target.sin_port = htons(atoi(AYIYA_PORT));
94
95 /* Prefill some standard AYIYA values */
96 memset(&s, 0, sizeof(s));
97 s2.ayh.ayh_idlen = 4; /* 2^4 = 16 bytes = 128 bits (IPv6 address) */
98 s2.ayh.ayh_idtype = ayiya_id_integer;
99 s2.ayh.ayh_siglen = 5; /* 5*4 = 20 bytes = 160 bits (SHA1) */
100 s2.ayh.ayh_hshmeth = ayiya_hash_sha1;
101 s2.ayh.ayh_autmeth = ayiya_auth_sharedsecret;
102 s2.ayh.ayh_opcode = ayiya_op_forward;
103 s2.ayh.ayh_nextheader = IPPROTO_IPV6;
104
105 /* Our IPv6 side of this tunnel */
106 memcpy(&s2.identity, &ayiya_ipv6_local, sizeof(s2.identity));
107
108 /* The payload */
109 memcpy(&s2.payload, buf, length);
110
111 /* Fill in the current time */
112 s2.ayh.ayh_epochtime = htonl((u_long)time(NULL));
113
114 /*
115 * The hash of the shared secret needs to be in the
116 * spot where we later put the complete hash
117 */
118 memcpy(&s2.hash, ayiya_hash, sizeof(s2.hash));
119
120 /* Generate a SHA1 */
121 SHA1_Init(&sha1);
122 /* Hash the complete AYIYA packet */
123 SHA1_Update(&sha1, (sha1_byte *)&s2, sizeof(s2)-sizeof(s2.payload)+length);
124 /* Store the hash in the packets hash */
125 SHA1_Final(hash, &sha1);
126
127 /* Store the hash in the actual packet */
128 memcpy(&s2.hash, &hash, sizeof(s2.hash));
129
130 /* Send it onto the network */
131 length = sizeof(s2)-sizeof(s2.payload)+length;
132#if defined(_FREEBSD) || defined(_DFBSD) || defined(_OPENBSD) || defined(_DARWIN) || defined(_NETBSD)
133 lenout = send(ayiya_socket->socket, (const char *)&s2, length, 0);
134#else
135 lenout = sendto(ayiya_socket->socket, (const char *)&s2, length, 0, (struct sockaddr *)&target, sizeof(target));
136#endif
137 if (lenout < 0)
138 {
139 ayiya_log(LOG_ERR, reader_name, NULL, 0, "Error (%d) while sending %u bytes to network: %s (%d)\n", lenout, length, strerror(errno), errno);
140 }
141 else if (length != (unsigned int)lenout)
142 {
143 ayiya_log(LOG_ERR, reader_name, NULL, 0, "Only %u of %u bytes sent to network: %s (%s)\n", lenout, length, strerror(errno), errno);
144 }
145}
146
147struct tun_reader ayiya_tun = { (TUN_PROCESS)ayiya_reader };
148
149/* Socket -> Tun */
150#ifndef _WIN32
151void *ayiya_writer(void UNUSED *arg);
152void *ayiya_writer(void UNUSED *arg)
153#else
154DWORD WINAPI ayiya_writer(LPVOID arg);
155DWORD WINAPI ayiya_writer(LPVOID arg)
156#endif
157{
158 unsigned char buf[2048];
159 struct pseudo_ayh *s = (struct pseudo_ayh *)buf;
160 struct sockaddr_storage ci;
161 socklen_t cl;
162 int i, n;
163 unsigned int payloadlen = 0;
164 SHA_CTX sha1;
165 sha1_byte their_hash[SHA1_DIGEST_LENGTH],
166 our_hash[SHA1_DIGEST_LENGTH];
167
168 ayiya_log(LOG_INFO, writer_name, NULL, 0, "(Socket to TUN) started\n");
169
170 /* Tun/TAP device is now running */
171 g_aiccu->tunrunning = true;
172
173 while (true)
174 {
175 cl = sizeof(ci);
176 memset(buf, 0, sizeof(buf));
177 n = recvfrom(ayiya_socket->socket, (char *)buf, sizeof(buf), 0, (struct sockaddr *)&ci, &cl);
178
179 if (n < 0) continue;
180
181 if (n < (int)sizeof(struct ayiyahdr))
182 {
183 ayiya_log(LOG_WARNING, writer_name, &ci, cl, "Received packet is too short");
184 continue;
185 }
186
187 if ( s->ayh.ayh_idlen != 4 ||
188 s->ayh.ayh_idtype != ayiya_id_integer ||
189 s->ayh.ayh_siglen != 5 ||
190 s->ayh.ayh_hshmeth != ayiya_hash_sha1 ||
191 s->ayh.ayh_autmeth != ayiya_auth_sharedsecret ||
192 (s->ayh.ayh_nextheader != IPPROTO_IPV6 &&
193 s->ayh.ayh_nextheader != IPPROTO_NONE) ||
194 (s->ayh.ayh_opcode != ayiya_op_forward &&
195 s->ayh.ayh_opcode != ayiya_op_echo_request &&
196 s->ayh.ayh_opcode != ayiya_op_echo_request_forward))
197 {
198 /* Invalid AYIYA packet */
199 ayiya_log(LOG_ERR, writer_name, &ci, cl, "Dropping invalid AYIYA packet\n");
200 ayiya_log(LOG_ERR, writer_name, &ci, cl, "idlen: %u != %u\n", s->ayh.ayh_idlen, 4);
201 ayiya_log(LOG_ERR, writer_name, &ci, cl, "idtype: %u != %u\n", s->ayh.ayh_idtype, ayiya_id_integer);
202 ayiya_log(LOG_ERR, writer_name, &ci, cl, "siglen: %u != %u\n", s->ayh.ayh_siglen, 5);
203 ayiya_log(LOG_ERR, writer_name, &ci, cl, "hshmeth: %u != %u\n", s->ayh.ayh_hshmeth, ayiya_hash_sha1);
204 ayiya_log(LOG_ERR, writer_name, &ci, cl, "autmeth: %u != %u\n", s->ayh.ayh_autmeth, ayiya_auth_sharedsecret);
205 ayiya_log(LOG_ERR, writer_name, &ci, cl, "nexth : %u != %u || %u\n", s->ayh.ayh_nextheader, IPPROTO_IPV6, IPPROTO_NONE);
206 ayiya_log(LOG_ERR, writer_name, &ci, cl, "opcode : %u != %u || %u || %u\n", s->ayh.ayh_opcode, ayiya_op_forward, ayiya_op_echo_request, ayiya_op_echo_request_forward);
207 continue;
208 }
209
210 if (memcmp(&s->identity, &ayiya_ipv6_pop, sizeof(s->identity)) != 0)
211 {
212 memset(buf, 0, sizeof(buf));
213 inet_ntop(AF_INET6, &s->identity, (char *)&buf, sizeof(buf));
214 ayiya_log(LOG_WARNING, writer_name, &ci, cl, "Received packet from a wrong identity \"%s\"\n", buf);
215 continue;
216 }
217
218 /* Verify the epochtime */
219 i = tic_checktime(ntohl(s->ayh.ayh_epochtime));
220 if (i != 0)
221 {
222 memset(buf, 0, sizeof(buf));
223 inet_ntop(AF_INET6, &s->identity, (char *)&buf, sizeof(buf));
224 ayiya_log(LOG_WARNING, writer_name, &ci, cl, "Time is %d seconds off for %s\n", i, buf);
225 continue;
226 }
227
228 /* How long is the payload? */
229 payloadlen = n - (sizeof(*s) - sizeof(s->payload));
230
231 /* Save their hash */
232 memcpy(&their_hash, &s->hash, sizeof(their_hash));
233
234 /* Copy in our SHA1 hash */
235 memcpy(&s->hash, &ayiya_hash, sizeof(s->hash));
236
237 /* Generate a SHA1 of the header + identity + shared secret */
238 SHA1_Init(&sha1);
239 /* Hash the Packet */
240 SHA1_Update(&sha1, (sha1_byte *)s, n);
241 /* Store the hash */
242 SHA1_Final(our_hash, &sha1);
243
244 memcpy(&s->hash, &our_hash, sizeof(s->hash));
245
246 /* Compare the SHA1's */
247 if (memcmp(&their_hash, &our_hash, sizeof(their_hash)) != 0)
248 {
249 ayiya_log(LOG_WARNING, writer_name, &ci, cl, "Incorrect Hash received\n");
250 continue;
251 }
252
253 if (s->ayh.ayh_nextheader == IPPROTO_IPV6)
254 {
255 /* Verify that this is really IPv6 */
256 if (s->payload[0] >> 4 != 6)
257 {
258 ayiya_log(LOG_ERR, writer_name, &ci, cl, "Received packet didn't start with a 6, thus is not IPv6\n");
259 continue;
260 }
261
262 /* Forward the packet to the kernel */
263 tun_write(s->payload, payloadlen);
264 }
265 }
266
267 /* Tun/TAP device is not running anymore */
268 g_aiccu->tunrunning = false;
269
270#ifndef _WIN32
271 return NULL;
272#else
273 return 0;
274#endif
275}
276
277/* Construct a beat and send it outwards */
278void ayiya_beat(void)
279{
280 SHA_CTX sha1;
281 sha1_byte hash[SHA1_DIGEST_LENGTH];
282 struct sockaddr_in target;
283 struct pseudo_ayh s;
284 int lenout, n;
285
286 /* We tunnel over IPv4 */
287 memcpy(&target.sin_addr, &ayiya_ipv4_pop, sizeof(target.sin_addr));
288 target.sin_family = AF_INET;
289 target.sin_port = htons(atoi(AYIYA_PORT));
290
291 /* Prefill some standard AYIYA values */
292 memset(&s, 0, sizeof(s));
293 s.ayh.ayh_idlen = 4; /* 2^4 = 16 bytes = 128 bits (IPv6 address) */
294 s.ayh.ayh_idtype = ayiya_id_integer;
295 s.ayh.ayh_siglen = 5; /* 5*4 = 20 bytes = 160 bits (SHA1) */
296 s.ayh.ayh_hshmeth = ayiya_hash_sha1;
297 s.ayh.ayh_autmeth = ayiya_auth_sharedsecret;
298 s.ayh.ayh_opcode = ayiya_op_noop;
299 s.ayh.ayh_nextheader = IPPROTO_NONE;
300
301 /* Our IPv6 side of this tunnel */
302 memcpy(&s.identity, &ayiya_ipv6_local, sizeof(s.identity));
303
304 /* No Payload */
305
306 /* Fill in the current time */
307 s.ayh.ayh_epochtime = htonl((u_long)time(NULL));
308
309 /* Our IPv6 side of this tunnel */
310 memcpy(&s.identity, &ayiya_ipv6_local, sizeof(s.identity));
311
312 /*
313 * The hash of the shared secret needs to be in the
314 * spot where we later put the complete hash
315 */
316 memcpy(&s.hash, ayiya_hash, sizeof(s.hash));
317
318 /* Generate a SHA1 */
319 SHA1_Init(&sha1);
320 /* Hash the complete AYIYA packet */
321 SHA1_Update(&sha1, (sha1_byte *)&s, sizeof(s)-sizeof(s.payload));
322 /* Store the hash in the packets hash */
323 SHA1_Final(hash, &sha1);
324
325 /* Store the hash in the actual packet */
326 memcpy(&s.hash, &hash, sizeof(s.hash));
327
328 /* Send it onto the network */
329 n = sizeof(s)-sizeof(s.payload);
330#if defined(_FREEBSD) || defined(_DFBSD) || defined(_OPENBSD) || defined(_DARWIN) || defined(_NETBSD)
331 lenout = send(ayiya_socket->socket, (const char *)&s, (unsigned int)n, 0);
332#else
333 lenout = sendto(ayiya_socket->socket, (const char *)&s, (unsigned int)n, 0, (struct sockaddr *)&target, sizeof(target));
334#endif
335 if (lenout < 0)
336 {
337 ayiya_log(LOG_ERR, beat_name, NULL, 0, "Error (%d) while sending %u bytes sent to network: %s (%d)\n", lenout, n, strerror(errno), errno);
338 }
339 else if (n != lenout)
340 {
341 ayiya_log(LOG_ERR, beat_name, NULL, 0, "Only %u of %u bytes sent to network: %s (%d)\n", lenout, n, strerror(errno), errno);
342 }
343}
344
345bool ayiya(struct TIC_Tunnel *hTunnel)
346{
347 SHA_CTX sha1;
348 struct addrinfo hints, *res, *ressave;
349#ifndef _WIN32
350 pthread_t thread;
351#else
352 DWORD pID;
353 HANDLE h;
354#endif
355
356 /* Setup the tunnel */
357 if (!tun_start(&ayiya_tun)) return false;
358
359 /* Resolve hTunnel entries */
360 memset(&hints, 0, sizeof(struct addrinfo));
361 hints.ai_family = AF_INET;
362 hints.ai_socktype = SOCK_DGRAM;
363
364 if (getaddrinfo(hTunnel->sIPv4_POP, AYIYA_PORT, &hints, &res) != 0)
365 {
366 dolog(LOG_ERR, "Couldn't resolve PoP IPv4 %s\n", hTunnel->sIPv4_POP);
367 return false;
368 }
369 ressave = res;
370 while (res)
371 {
372 if (res->ai_family != AF_INET)
373 {
374 res = res->ai_next;
375 continue;
376 }
377 memcpy(&ayiya_ipv4_pop, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4);
378 break;
379 }
380 freeaddrinfo(ressave);
381 if (res == NULL)
382 {
383 dolog(LOG_ERR, "No valid IPv4 address for PoP address %s could be found\n", hTunnel->sIPv4_POP);
384 return false;
385 }
386
387 memset(&hints, 0, sizeof(struct addrinfo));
388 hints.ai_family = AF_INET6;
389 if (getaddrinfo(hTunnel->sIPv6_Local, NULL, &hints, &res) != 0)
390 {
391 dolog(LOG_ERR, "Couldn't resolve Local IPv6 %s\n", hTunnel->sIPv6_Local);
392 return false;
393 }
394 ressave = res;
395 while (res)
396 {
397 if (res->ai_family != AF_INET6)
398 {
399 res = res->ai_next;
400 continue;
401 }
402 memcpy(&ayiya_ipv6_local, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16);
403 break;
404 }
405 freeaddrinfo(ressave);
406 if (res == NULL)
407 {
408 dolog(LOG_ERR, "No valid IPv6 address for Local IPv6 address %s could be found\n", hTunnel->sIPv6_Local);
409 return false;
410 }
411
412 memset(&hints, 0, sizeof(struct addrinfo));
413 hints.ai_family = AF_INET6;
414 if (getaddrinfo(hTunnel->sIPv6_POP, NULL, &hints, &res) != 0)
415 {
416 dolog(LOG_ERR, "Couldn't resolve Local IPv6 %s\n", hTunnel->sIPv6_POP);
417 return false;
418 }
419 ressave = res;
420 while (res)
421 {
422 if (res->ai_family != AF_INET6)
423 {
424 res = res->ai_next;
425 continue;
426 }
427 memcpy(&ayiya_ipv6_pop, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16);
428 break;
429 }
430 freeaddrinfo(ressave);
431 if (res == NULL)
432 {
433 dolog(LOG_ERR, "No valid IPv6 address for POP IPv6 address %s could be found\n", hTunnel->sIPv6_POP);
434 return false;
435 }
436
437 if (!hTunnel->sPassword)
438 {
439 dolog(LOG_ERR, "A password is required for using AYIYA tunnels\n");
440 return false;
441 }
442
443 /* Generate a SHA1 of the shared secret */
444 SHA1_Init(&sha1);
445 SHA1_Update(&sha1, (const sha1_byte *)hTunnel->sPassword, (unsigned int)strlen(hTunnel->sPassword));
446 SHA1_Final(ayiya_hash, &sha1);
447
448 /* Setup listening socket */
449 ayiya_socket = connect_client(hTunnel->sIPv4_POP , AYIYA_PORT, AF_INET, SOCK_DGRAM);
450 if (!ayiya_socket)
451 {
452 ayiya_log(LOG_ERR, "start", NULL, 0, "Connection error:: could not create connection to AYIYA server\n");
453 return false;
454 }
455
456 /* Let AICCU configure the thing */
457 if (!aiccu_setup(hTunnel, false))
458 {
459 return false;
460 }
461
462 /* Show that we have started */
463 ayiya_log(LOG_INFO, "start", NULL, 0, "Anything in Anything (%s)\n", AYIYA_VERSION);
464
465 /* Launch a thread for reader */
466#ifndef _WIN32
467 pthread_create(&thread, NULL, ayiya_writer, (void *)hTunnel);
468#else
469 h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ayiya_writer, hTunnel, 0, &pID);
470#endif
471
472 return true;
473}