Incorporated router.c -- doh!
[public-tsp] / router.c
1 /* pubTSP/router.c -- traffic forwarding daemon for public TSP service
2  *
3  * This is an implementation of the profile that makes TSP service publicly
4  * usable, that is without authentication.  However to avoid abuse of such
5  * a service, it is not anonymous -- IPv6 addresses contain the IPv4 address
6  * and port.
7  *
8  * Interestingly, as a side-effect of this design the router daemon can be
9  * stateless.  Any further requirements that are stateful are most likely
10  * filtering, and that can be solved in stateful firewall configuration.
11  *
12  * The intention of TSP is to enable IPv4-only hosts to connecto to
13  * IPv6 services; the public TSP profile adds to that the ability to
14  * do it in a temporary manner.
15  *
16  * TODO: Should we translate ICMPv4 --> ICMPv6?
17  *
18  * From: Rick van Rein <rick@openfortress.nl>
19  */
20
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/time.h>
33 #include <sys/select.h>
34 #include <sys/ioctl.h>
35
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <netinet/ip6.h>
39 #include <netinet/udp.h>
40 #include <arpa/inet.h>
41
42 #include <linux/if.h>
43 #include <linux/if_tun.h>
44 #include <linux/if_ether.h>
45
46
47 struct tsphdr {
48         u_int32_t seqnum;
49         u_int32_t timestamp;
50 };
51
52
53 #define TUNNEL_CAPABILITIES "CAPABILITIES TUNNEL=V6UDPV4 AUTH=ANONYMOUS"
54
55 #define MTU 1280
56
57 /*
58  * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
59  * the -t option leads to an error, or to an attempt to setup the tunnel.
60  * The setup_tunnel() function used for that is defined per platform, such
61  * as for LINUX.  Remember to maintain the manpage's optionality for -t.
62  */
63 #undef HAVE_SETUP_TUNNEL
64 #ifdef LINUX
65 #  define HAVE_SETUP_TUNNEL
66 #endif
67
68
69 /* Global variables */
70
71 char *program;
72
73 int v4sox = -1;
74 int v6sox = -1;
75
76 char *v4server = NULL;
77 char *v6server = NULL;
78 char *v6prefix = NULL;
79
80 struct sockaddr_in  v4name;
81 struct sockaddr_in6 v6name;
82
83 struct in6_addr v6listen;
84
85
86 struct {
87         struct tun_pi tun;
88         struct tsphdr tsp;
89         union {
90                 u_int8_t data [MTU];
91                 struct ip6_hdr v6hdr;
92         } udata;
93         u_int8_t zerobyte;
94 } v4data6;
95
96 #define v4tunpi6        (v4data6.tun)
97 #define v4data          ((u_int8_t *) &v4data6.tsp)
98 #define v4tsphdr        (&v4data6.tsp)
99 #define v4tspcmd        (v4data6.udata.data)
100 #define v4hdr6          (&v4data6.udata.v6hdr)
101 #define v4src6          (&v4data6.udata.v6hdr.ip6_src)
102 #define v4dst6          (&v4data6.udata.v6hdr.ip6_dst)
103
104
105 struct {
106         struct tun_pi tun;
107         union {
108                 u_int8_t data [MTU];
109                 struct ip6_hdr v6hdr;
110         } udata;
111         u_int8_t zero;
112 } v6data6;
113
114 #define v6data          (v6data6.udata.data)
115 #define v6tuncmd        (v6data6.tun)
116 #define v6hdr6          (&v6data6.udata.v6hdr)
117 #define v6src6          (&v6data6.udata.v6hdr.ip6_src)
118 #define v6dst6          (&v6data6.udata.v6hdr.ip6_dst)
119
120
121 /*
122  *
123  * Driver routines
124  *
125  */
126
127 #ifdef LINUX
128 /* Implement the setup_tunnel() command for Linux.
129  * Return 1 on success, 0 on failure.
130  */
131 int setup_tunnel (void) {
132         v6sox = open ("/dev/net/tun", O_RDWR);
133         if (v6sox == -1) {
134                 fprintf (stderr, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
135                 return 0;
136         }
137         int ok = 1;
138         struct ifreq ifreq;
139         memset (&ifreq, 0, sizeof (ifreq));
140         ifreq.ifr_flags = IFF_TUN;
141         if (ok && ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1) {
142                 ok = 0;
143         }
144         ifreq.ifr_name [IFNAMSIZ] = 0;
145         char cmd [512+1];
146         snprintf (cmd, 512, "/sbin/ip -6 addr add %s dev %s", v6prefix, ifreq.ifr_name);
147         if (ok && system (cmd) != 0) {
148                 ok = 0;
149         }
150         snprintf (cmd, 512, "/sbin/ip link set %s up", ifreq.ifr_name);
151         if (ok && system (cmd) != 0) {
152                 ok = 0;
153         }
154         if (!ok) {
155                 close (v6sox);  /* This removes the tunnel interface */
156         }
157         return ok;
158 }
159 #endif /* LINUX */
160
161
162 /*
163  *
164  * Command functions
165  *
166  */
167
168
169 /* Send a reply to a tunnel command back to the most recent sender.
170  * This is a textual protocol, so reply is a NUL-terminated string
171  * and "\r\n" will be postfixed.
172  */
173 void tspcmd_reply (char *reply) {
174         if (reply != (char *) v4tspcmd) {
175                 strncpy (v4tspcmd, reply, MTU-1);
176         } else {
177                 v4tspcmd [MTU-1] = 0;
178         }
179         strncat (v4tspcmd, "\r\n", MTU+1);
180         sendto (v4sox, v4data, sizeof (struct tsphdr) + strlen (v4tspcmd),                      MSG_DONTWAIT, (struct sockaddr *) &v4name, sizeof (v4name));
181 }
182
183
184 /* Send an info message, in response to a creation request on the
185  * tunnel.  The contents of this message are constant, in support of
186  * the stateless implementation of this daemon.  The address and port
187  * of the IPv4 sender are taken into account, but the information sent
188  * over XML is not.
189  */
190 void tspcmd_create (void) {
191         char v4client [INET_ADDRSTRLEN];
192         char v6client [INET6_ADDRSTRLEN+1];
193         inet_ntop (AF_INET, &v4name.sin_addr, v4client, sizeof (v4client));
194         snprintf (v6client, sizeof (v6client)-1,
195                 "%x:%x:%x:%x:%x:%x:%x::",
196                         v6listen.s6_addr16 [0],
197                         v6listen.s6_addr16 [1],
198                         v6listen.s6_addr16 [2],
199                         v6listen.s6_addr16 [3],
200                         ntohl (v4name.sin_addr.s_addr) >> 16,
201                         ntohl (v4name.sin_addr.s_addr) & 0x0000ffff,
202                         ntohs (v4name.sin_port));
203         snprintf (v4tspcmd, MTU-1,
204 "Content-length: 0000\r\n"
205 "200 OK\r\n"
206 "<tunnel action=\"info\" type=\"v6udpv4\" lifetime=\"86400\">\r\n"
207 "  <server>\r\n"
208 "    <address type=\"ipv4\">%s</address>\r\n"
209 "    <address type=\"ipv6\">%s</address>\r\n"
210 "  </server>\r\n"
211 "  <client>\r\n"
212 "    <address type=\"ipv4\">%s</address>\r\n"
213 "    <address type=\"ipv6\">%s</address>\r\n"
214 "    <keepalive interval=\"30\">\r\n"
215 "      <address type=\"ipv6\">%s</address>\r\n"
216 "    </keepalive>\r\n"
217 "  </client>\r\n"
218 "</tunnel>"
219                 , v4server, v6server, v4client, v6client, v6server);
220         tspcmd_reply (v4tspcmd);
221 }
222
223
224 /* Handle the IPv4 message pointed at by msg as a tunnel command.
225  */
226 void handle_4to6_tspcmd (ssize_t v4tspcmdlen) {
227         //
228         // Tunnel data is textual, append '\0' and ensure that's all
229         v4tspcmd [v4tspcmdlen] = 0;
230         if (strlen (v4tspcmd) != v4tspcmdlen) {
231                 // Tricky package contains '\0' -- drop silently
232                 return;
233         }
234         //
235         // Handle VERSION= interaction
236         if (strncmp (v4tspcmd, "VERSION=", 8) == 0) {
237                 if (strcmp (v4tspcmd + 8, "2.0\r\n") == 0) {
238                         tspcmd_reply (TUNNEL_CAPABILITIES);
239                 } else {
240                         tspcmd_reply ("302 Unsupported client version");
241                 }
242         }
243         //
244         // Handle AUTHENTICATE command
245         else if (strncmp (v4tspcmd, "AUTHENTICATE ", 13)) {
246                 if (strcmp (v4tspcmd + 13, "ANONYMOUS\r\n") == 0) {
247                         tspcmd_reply ("200 Success\r\n");
248                 } else {
249                         tspcmd_reply ("300 Only ANONYMOUS authentication is supported");
250                 }
251         }
252         //
253         // Handle XML prefixed with "content-length:"
254         else if (strncmp (v4tspcmd, "content-length:", 15) == 0) {
255                 // Hoping to get away with not parsing XML:
256                 if (strstr (v4tspcmd, "create")) {
257                         tspcmd_create ();
258                 } else {
259                         tspcmd_reply ("200 Success");
260                 }
261         }
262         //
263         // Reject any further commands loudly
264         else {
265                 tspcmd_reply ("400 Go away");
266         }
267 }
268
269
270 /* Handle the IPv4 message pointed at by msg, checking if the IPv4:port
271  * data matches the lower half of the IPv6 sender address.  Drop silently
272  * if this is not the case.  TODO: or send ICMP?
273  */
274 void handle_4to6_payload (ssize_t v4datalen) {
275         //
276         // Ensure that the lower half of the IPv6 sender address is ok
277         if (v4src6->s6_addr32 [2] != v4name.sin_addr.s_addr) {
278                 return;
279         }
280         if (v4src6->s6_addr16 [6] != v4name.sin_port) {
281                 return;
282         }
283 #if 0
284         if (v4src6->s6_addr16 [7] != htons (0x0000)) {
285                 return;
286         }
287 #endif
288         //
289         // Ensure that the top half of the IPv6 address is ok
290         // Note that this implies rejection of ::1/128, fe80::/10 and fec0::/10
291         if (memcmp (v4src6, &v6listen, 8) != 0) {
292                 return;
293         }
294         if (v4src6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
295                 return;
296         }
297         if (v4src6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
298                 return;
299         }
300         //
301         // Send the unwrapped IPv6 message out over v6sox
302         memcpy (&v6name.sin6_addr, v4dst6, sizeof (v6name.sin6_addr));
303 printf ("Sending IPv6, result = %d\n",
304         sendto (v6sox,
305                         &v4data6, sizeof (struct tun_pi) + v4datalen,
306                         MSG_DONTWAIT,
307                         (struct sockaddr *) &v6name, sizeof (v6name)));
308 }
309
310 /* Receive a tunnel package, and route it to either the handler for the
311  * tunnel protocol, or to the handler that checks and then unpacks the
312  * contained IPv6.
313  */
314 void handle_4to6 (void) {
315         u_int8_t buf [1501];
316         ssize_t buflen;
317         size_t adrlen = sizeof (v4name);
318         //
319         // Receive IPv4 package, which may be tunneled or a tunnel request
320         buflen = recvfrom (v4sox,
321                         v4data, sizeof (struct tsphdr) + MTU,
322                         MSG_DONTWAIT,
323                         (struct sockaddr *) &v4name, &adrlen
324                 );
325         if (buflen == -1) {
326                 printf ("%s: Error receiving IPv4-side package: %s",
327                                 program, strerror (errno));
328                 return;
329         }
330         //
331         // Ensure complete headers
332         if (buflen < (sizeof (struct iphdr)
333                         + sizeof (struct udphdr)
334                         + sizeof (struct ip6_hdr) + 1)) {
335                 return;
336         }
337         int flag = v4data [0] & 0xf0;
338         switch (v4data [0] & 0xf0) {
339         case 0xf0:
340                 /* Handle as a tunnel command package */
341                 handle_4to6_tspcmd (buflen - sizeof (struct tsphdr));
342                 return;
343         case 0x60:
344                 /* Handle as a tunneled IPv6 package */
345                 handle_4to6_payload (buflen);
346                 return;
347         default:
348                 /* Silently ignore wrong types of packages */
349                 return;
350         }
351 }
352
353
354 /* Receive an IPv6 package, check its address and pickup IPv4 address and
355  * port, then package it as a tunnel message and forward it to IPv4:port.
356  */
357 void handle_6to4 (void) {
358         //
359         // Receive the IPv6 package and ensure a consistent size
360         size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
361         if (rawlen == -1) {
362                 return;
363         }
364         if (rawlen < sizeof (struct tun_pi) + sizeof (struct ip6_hdr) + 1) {
365                 return;
366         }
367         if (v6tuncmd.proto != htons (ETH_P_IPV6)) {
368                 return;
369         }
370 printf ("Received IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
371         //
372         // Ensure that the incoming IPv6 address is properly formatted
373         // Note that this avoids access to ::1/128, fe80::/10, fec0::/10
374         if (memcmp (v6dst6, &v6listen, 8) != 0) {
375                 return;
376         }
377         if (v6dst6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
378                 return;
379         }
380         if (v6dst6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
381                 return;
382         }
383 #if 0
384         if (v6dst6->s6_addr16 [7] != htons (0x0000)) {
385                 return;
386         }
387 #endif
388         //
389         // Harvest socket address data from destination IPv6, then send
390         v4name.sin_family = AF_INET;
391         v4name.sin_addr.s_addr = v6dst6->s6_addr32 [2];
392         v4name.sin_port = v6dst6->s6_addr16 [6];
393 printf ("Sending IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\n",
394 ((u_int8_t *) &v4name.sin_addr.s_addr) [0],
395 ((u_int8_t *) &v4name.sin_addr.s_addr) [1],
396 ((u_int8_t *) &v4name.sin_addr.s_addr) [2],
397 ((u_int8_t *) &v4name.sin_addr.s_addr) [3],
398 ntohs (v4name.sin_port),
399         sendto (v4sox,
400                         v6data,
401                         rawlen - sizeof (struct tun_pi),
402                         MSG_DONTWAIT,
403                         (struct sockaddr *) &v4name, sizeof (v4name)));
404 }
405
406
407 /* Run the daemon core code, passing information between IPv4 and IPv6 and
408  * responding to tunnel requests if so requested.
409  */
410 void run_daemon (void) {
411         fd_set io;
412         FD_ZERO (&io);
413         FD_SET (v4sox, &io);
414         FD_SET (v6sox, &io);
415         int nfds = (v4sox < v6sox)? (v6sox + 1): (v4sox + 1);
416         while (1) {
417                 select (nfds, &io, NULL, NULL, NULL);
418                 if (FD_ISSET (v4sox, &io)) {
419                         handle_4to6 ();
420                 } else {
421                         FD_SET (v4sox, &io);
422                 }
423                 if (FD_ISSET (v6sox, &io)) {
424                         handle_6to4 ();
425                 } else {
426                         FD_SET (v6sox, &io);
427                 }
428         }
429 }
430
431
432 /* Option descriptive data structures */
433
434 char *short_opt = "l:L:t:h";
435
436 struct option long_opt [] = {
437         { "v4listen", 1, NULL, 'l' },
438         { "v6prefix", 1, NULL, 'L' },
439         { "tundev", 1, NULL, 't' },
440         { "help", 0, NULL, 'h' },
441         { NULL, 0, NULL, 0 }    /* Array termination */
442 };
443
444 /* Parse commandline arguments (and start to process them).
445  * Return 1 on success, 0 on failure.
446  */
447 int process_args (int argc, char *argv []) {
448         int ok = 1;
449         int help = (argc == 1);
450         int done = 0;
451         while (!done) {
452                 switch (getopt_long (argc, argv, short_opt, long_opt, NULL)) {
453                 case -1:
454                         done = 1;
455                         if (optind != argc) {
456                                 fprintf (stderr, "%s: Extra arguments not permitted: %s...\n", program, argv [optind]);
457                                 ok = 0;
458                         }
459                         break;
460                 case 'l':
461                         if (v4sox != -1) {
462                                 ok = 0;
463                                 fprintf (stderr, "%s: Only one -l argument is permitted\n");
464                                 break;
465                         }
466                         v4server = optarg;
467                         if (inet_pton (AF_INET, optarg, &v4name.sin_addr) <= 0) {
468                                 ok = 0;
469                                 fprintf (stderr, "%s: Failed to parse IPv4 address %s\n", program, optarg);
470                                 break;
471                         }
472                         v4sox = socket (AF_INET, SOCK_DGRAM, 0);
473                         if (v4sox == -1) {
474                                 ok = 0;
475                                 fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
476                                 break;
477                         }
478                         if (bind (v4sox, (struct sockaddr *) &v4name, sizeof (v4name)) != 0) {
479                                 ok = 0;
480                                 fprintf (stderr, "%s: Failed to bind to UDPv4 %s:%d: %s\n", program, optarg, ntohs (v4name.sin_port), strerror (errno));
481                                 break;
482                         }
483                         break;
484                 case 'L':
485                         if (v6server) {
486                                 ok = 0;
487                                 fprintf (stderr, "%s: Only one -L argument is permitted\n");
488                                 break;
489                         }
490                         char *slash64 = strchr (optarg, '/');
491                         if (!slash64 || strcmp (slash64, "/64") != 0) {
492                                 ok = 0;
493                                 fprintf (stderr, "%s: The -L argument must be an explicit /64 prefix and not %s\n", program, slash64? slash64: "implied");
494                                 break;
495                         }
496                         *slash64 = 0;
497                         v6server = strdup (optarg);
498                         *slash64 = '/';
499                         v6prefix = optarg;
500                         if (!v6server || inet_pton (AF_INET6, v6server, &v6listen) <= 0) {
501                                 ok = 0;
502                                 fprintf (stderr, "%s: Failed to parse IPv6 prefix %s\n", optarg);
503                                 break;
504                         }
505                         if (v6listen.s6_addr32 [2] || v6listen.s6_addr32 [3]) {
506                                 ok = 0;
507                                 fprintf (stderr, "%s: IPv6 prefix contains bits beyond its /64 prefix: %s\n", program, optarg);
508                                 break;
509                         }
510                         break;
511                 case 't':
512                         if (v6sox != -1) {
513                                 ok = 0;
514                                 fprintf (stderr, "%s: Multiple -t arguments are not permitted\n");
515                                 break;
516                         }
517                         v6sox = open (optarg, O_RDWR);
518                         if (v6sox == -1) {
519                                 ok = 0;
520                                 fprintf (stderr, "%s: Failed to open tunnel device %s: %s\n", program, optarg, strerror (errno));
521                                 break;
522                         }
523                         break;
524                 default:
525                         ok = 0;
526                         help = 1;
527                         /* continue into 'h' to produce usage information */
528                 case 'h':
529                         help = 1;
530                         break;
531                 }
532                 if (help || !ok) {
533                         done = 1;
534                 }
535         }
536         if (help) {
537 #ifdef HAVE_SETUP_TUNNEL
538                 fprintf (stderr, "Usage: %s [-t /dev/tunX] -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
539 #else
540                 fprintf (stderr, "Usage: %s -t /dev/tunX -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
541 #endif
542                 return ok;
543         }
544         if (!ok) {
545                 return 0;
546         }
547         if (v4sox == -1) {
548                 fprintf (stderr, "%s: Use -l to specify an IPv4 address for the tunnel interface\n", program);
549                 return 0;
550         }
551         if (!v6server) {
552                 fprintf (stderr, "%s: Use -L to specify a /64 prefix on the IPv6 side\n", program);
553                 return 0;
554         }
555 #ifdef HAVE_SETUP_TUNNEL
556         if (v6sox == -1) {
557                 if (geteuid () != 0) {
558                         fprintf (stderr, "%s: You should be root, or use -t to specify an accessible tunnel device\n", program);
559                         return 0;
560                 }
561                 ok = setup_tunnel ();
562         }
563 #else /* ! HAVE_SETUP_TUNNEL */
564         if (v6sox == -1) {
565                 fprintf (stderr, "%s: You must specify a tunnel device with -t that is accessible to you\n", program);
566                 return 0;
567         }
568 #endif /* HAVE_SETUP_TUNNEL */
569         return ok;
570 }
571
572
573 /* The main program parses commandline arguments and forks off the daemon
574  */
575 int main (int argc, char *argv []) {
576         //
577         // Initialise
578         program = argv [0];
579         memset (&v4name, 0, sizeof (v4name));
580         memset (&v6name, 0, sizeof (v6name));
581         v4name.sin_family  = AF_INET ;
582         v6name.sin6_family = AF_INET6;
583         v4name.sin_port = htons (3653); /* TSP standard port */
584         v4tunpi6.flags = 0;
585         v4tunpi6.proto = htons (ETH_P_IPV6);
586         //
587         // Parse commandline arguments
588         if (!process_args (argc, argv)) {
589                 exit (1);
590         }
591         //
592         // Start the main daemon process
593         switch (fork ()) {
594         case -1:                /* Error forking */
595                 fprintf (stderr, "%s: Failed to fork: %s\n", program, strerror (errno));
596                 exit (1);
597         case 0:                 /* Child process */
598                 close (0);
599                 //TODO: tmp.support for ^printf// close (1);
600                 close (2);
601                 setsid ();
602                 run_daemon ();
603                 break;
604         default:                /* Parent process */
605                 close (v4sox);
606                 close (v6sox);
607                 break;
608         }
609         //
610         // Report successful creation of the daemon
611         return 0;
612 }