aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-12-17 08:00:29 +0100
committerChristian Grothoff <christian@grothoff.org>2016-12-17 08:00:29 +0100
commit545faeda8f7fb7e03bb39602df6142f630157050 (patch)
tree26aac9f044212d5f492b4ac3d0522af136aab58e /src
parenta1e76003ca590ea8d3e9387da35d87419417abb8 (diff)
downloadgnunet-545faeda8f7fb7e03bb39602df6142f630157050.tar.gz
gnunet-545faeda8f7fb7e03bb39602df6142f630157050.zip
misc. improvements to new NAT service, starting with autoconfiguration logic
Diffstat (limited to 'src')
-rw-r--r--src/include/gnunet_nat_service.h2
-rw-r--r--src/nat/Makefile.am1
-rw-r--r--src/nat/gnunet-nat.c2
-rw-r--r--src/nat/gnunet-service-nat.c899
-rw-r--r--src/nat/gnunet-service-nat_mini.c759
-rw-r--r--src/nat/gnunet-service-nat_mini.h127
-rw-r--r--src/nat/nat.h2
-rw-r--r--src/nat/nat_api.c7
-rw-r--r--src/util/util.conf10
9 files changed, 1532 insertions, 277 deletions
diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h
index b66b44240..a42d1d7e6 100644
--- a/src/include/gnunet_nat_service.h
+++ b/src/include/gnunet_nat_service.h
@@ -83,7 +83,7 @@ enum GNUNET_NAT_AddressClass
83 /** 83 /**
84 * Addresses useful in the local wired network, 84 * Addresses useful in the local wired network,
85 * i.e. a MAC. Sensitive, but obvious to people nearby. 85 * i.e. a MAC. Sensitive, but obvious to people nearby.
86 86 *
87 * Useful for broadcasts. 87 * Useful for broadcasts.
88 */ 88 */
89 GNUNET_NAT_AC_LAN = 8, 89 GNUNET_NAT_AC_LAN = 8,
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
index c62a8d2cf..1dd8e44b9 100644
--- a/src/nat/Makefile.am
+++ b/src/nat/Makefile.am
@@ -98,6 +98,7 @@ libgnunetnatnew_la_LDFLAGS = \
98gnunet_service_nat_SOURCES = \ 98gnunet_service_nat_SOURCES = \
99 gnunet-service-nat.c \ 99 gnunet-service-nat.c \
100 gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ 100 gnunet-service-nat_stun.c gnunet-service-nat_stun.h \
101 gnunet-service-nat_mini.c gnunet-service-nat_mini.h \
101 gnunet-service-nat_helper.c gnunet-service-nat_helper.h 102 gnunet-service-nat_helper.c gnunet-service-nat_helper.h
102gnunet_service_nat_LDADD = \ 103gnunet_service_nat_LDADD = \
103 $(top_builddir)/src/util/libgnunetutil.la \ 104 $(top_builddir)/src/util/libgnunetutil.la \
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c
index 10921150d..7d10167ce 100644
--- a/src/nat/gnunet-nat.c
+++ b/src/nat/gnunet-nat.c
@@ -611,7 +611,7 @@ main (int argc,
611 gettext_noop ("which external IP and port should be used to test"), 611 gettext_noop ("which external IP and port should be used to test"),
612 GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, 612 GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
613 {'i', "in", "ADDRESS", 613 {'i', "in", "ADDRESS",
614 gettext_noop ("which IP and port are we locally using to listen to for connection reversals"), 614 gettext_noop ("which IP and port are we locally using to bind/listen to"),
615 GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr }, 615 GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr },
616 {'r', "remote", "ADDRESS", 616 {'r', "remote", "ADDRESS",
617 gettext_noop ("which remote IP and port should be asked for connection reversal"), 617 gettext_noop ("which remote IP and port should be asked for connection reversal"),
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c
index 4ad6c8d2c..af1219dc0 100644
--- a/src/nat/gnunet-service-nat.c
+++ b/src/nat/gnunet-service-nat.c
@@ -40,6 +40,7 @@
40#include "gnunet_statistics_service.h" 40#include "gnunet_statistics_service.h"
41#include "gnunet_nat_service.h" 41#include "gnunet_nat_service.h"
42#include "gnunet-service-nat_stun.h" 42#include "gnunet-service-nat_stun.h"
43#include "gnunet-service-nat_mini.h"
43#include "gnunet-service-nat_helper.h" 44#include "gnunet-service-nat_helper.h"
44#include "nat.h" 45#include "nat.h"
45#include <gcrypt.h> 46#include <gcrypt.h>
@@ -51,6 +52,11 @@
51 */ 52 */
52#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) 53#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
53 54
55/**
56 * How long do we wait until we forcefully terminate autoconfiguration?
57 */
58#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
59
54 60
55/** 61/**
56 * Internal data structure we track for each of our clients. 62 * Internal data structure we track for each of our clients.
@@ -187,6 +193,75 @@ struct StunExternalIP
187 193
188 194
189/** 195/**
196 * Context for autoconfiguration operations.
197 */
198struct AutoconfigContext
199{
200 /**
201 * Kept in a DLL.
202 */
203 struct AutoconfigContext *prev;
204
205 /**
206 * Kept in a DLL.
207 */
208 struct AutoconfigContext *next;
209
210 /**
211 * Which client asked the question.
212 */
213 struct ClientHandle *ch;
214
215 /**
216 * Configuration we are creating.
217 */
218 struct GNUNET_CONFIGURATION_Handle *c;
219
220 /**
221 * Timeout task to force termination.
222 */
223 struct GNUNET_SCHEDULER_Task *timeout_task;
224
225 /**
226 * What type of system are we on?
227 */
228 char *system_type;
229
230 /**
231 * Handle to activity to probe for our external IP.
232 */
233 struct GNUNET_NAT_ExternalHandle *probe_external;
234
235 /**
236 * #GNUNET_YES if upnpc should be used,
237 * #GNUNET_NO if upnpc should not be used,
238 * #GNUNET_SYSERR if we should simply not change the option.
239 */
240 int enable_upnpc;
241
242 /**
243 * Status code to return to the client.
244 */
245 enum GNUNET_NAT_StatusCode status_code;
246
247 /**
248 * NAT type to return to the client.
249 */
250 enum GNUNET_NAT_Type type;
251};
252
253
254/**
255 * DLL of our autoconfiguration operations.
256 */
257static struct AutoconfigContext *ac_head;
258
259/**
260 * DLL of our autoconfiguration operations.
261 */
262static struct AutoconfigContext *ac_tail;
263
264/**
190 * Timeout to use when STUN data is considered stale. 265 * Timeout to use when STUN data is considered stale.
191 */ 266 */
192static struct GNUNET_TIME_Relative stun_stale_timeout; 267static struct GNUNET_TIME_Relative stun_stale_timeout;
@@ -236,6 +311,12 @@ static struct StunExternalIP *se_head;
236 */ 311 */
237static struct StunExternalIP *se_tail; 312static struct StunExternalIP *se_tail;
238 313
314/**
315 * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
316 * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
317 */
318static int enable_upnp;
319
239 320
240/** 321/**
241 * Free the DLL starting at #lal_head. 322 * Free the DLL starting at #lal_head.
@@ -322,7 +403,9 @@ match_ipv4 (const char *network,
322 uint8_t bits) 403 uint8_t bits)
323{ 404{
324 struct in_addr net; 405 struct in_addr net;
325 406
407 if (0 == ip->s_addr)
408 return GNUNET_YES;
326 if (0 == bits) 409 if (0 == bits)
327 return GNUNET_YES; 410 return GNUNET_YES;
328 GNUNET_assert (1 == inet_pton (AF_INET, 411 GNUNET_assert (1 == inet_pton (AF_INET,
@@ -355,6 +438,10 @@ match_ipv6 (const char *network,
355 network, 438 network,
356 &net)); 439 &net));
357 memset (&mask, 0, sizeof (mask)); 440 memset (&mask, 0, sizeof (mask));
441 if (0 == memcmp (&mask,
442 ip,
443 sizeof (mask)))
444 return GNUNET_YES;
358 off = 0; 445 off = 0;
359 while (bits > 8) 446 while (bits > 8)
360 { 447 {
@@ -411,6 +498,296 @@ is_nat_v6 (const struct in6_addr *ip)
411 498
412 499
413/** 500/**
501 * Closure for #ifc_proc.
502 */
503struct IfcProcContext
504{
505
506 /**
507 * Head of DLL of local addresses.
508 */
509 struct LocalAddressList *lal_head;
510
511 /**
512 * Tail of DLL of local addresses.
513 */
514 struct LocalAddressList *lal_tail;
515
516};
517
518
519/**
520 * Callback function invoked for each interface found. Adds them
521 * to our new address list.
522 *
523 * @param cls a `struct IfcProcContext *`
524 * @param name name of the interface (can be NULL for unknown)
525 * @param isDefault is this presumably the default interface
526 * @param addr address of this interface (can be NULL for unknown or unassigned)
527 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
528 * @param netmask the network mask (can be NULL for unknown or unassigned)
529 * @param addrlen length of the address
530 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
531 */
532static int
533ifc_proc (void *cls,
534 const char *name,
535 int isDefault,
536 const struct sockaddr *addr,
537 const struct sockaddr *broadcast_addr,
538 const struct sockaddr *netmask,
539 socklen_t addrlen)
540{
541 struct IfcProcContext *ifc_ctx = cls;
542 struct LocalAddressList *lal;
543 size_t alen;
544 const struct in_addr *ip4;
545 const struct in6_addr *ip6;
546 enum GNUNET_NAT_AddressClass ac;
547
548 switch (addr->sa_family)
549 {
550 case AF_INET:
551 alen = sizeof (struct sockaddr_in);
552 ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
553 if (match_ipv4 ("127.0.0.0", ip4, 8))
554 ac = GNUNET_NAT_AC_LOOPBACK;
555 else if (is_nat_v4 (ip4))
556 ac = GNUNET_NAT_AC_LAN;
557 else
558 ac = GNUNET_NAT_AC_GLOBAL;
559 break;
560 case AF_INET6:
561 alen = sizeof (struct sockaddr_in6);
562 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
563 if (match_ipv6 ("::1", ip6, 128))
564 ac = GNUNET_NAT_AC_LOOPBACK;
565 else if (is_nat_v6 (ip6))
566 ac = GNUNET_NAT_AC_LAN;
567 else
568 ac = GNUNET_NAT_AC_GLOBAL;
569 if ( (ip6->s6_addr[11] == 0xFF) &&
570 (ip6->s6_addr[12] == 0xFE) )
571 {
572 /* contains a MAC, be extra careful! */
573 ac |= GNUNET_NAT_AC_PRIVATE;
574 }
575 break;
576#if AF_UNIX
577 case AF_UNIX:
578 GNUNET_break (0);
579 return GNUNET_OK;
580#endif
581 default:
582 GNUNET_break (0);
583 return GNUNET_OK;
584 }
585 lal = GNUNET_malloc (sizeof (*lal));
586 lal->af = addr->sa_family;
587 lal->ac = ac;
588 GNUNET_memcpy (&lal->addr,
589 addr,
590 alen);
591 GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
592 ifc_ctx->lal_tail,
593 lal);
594 return GNUNET_OK;
595}
596
597
598/**
599 * Notify client about a change in the list of addresses this peer
600 * has.
601 *
602 * @param delta the entry in the list that changed
603 * @param ch client to contact
604 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
605 * @param addr the address that changed
606 * @param addr_len number of bytes in @a addr
607 */
608static void
609notify_client (struct LocalAddressList *delta,
610 struct ClientHandle *ch,
611 int add,
612 const void *addr,
613 size_t addr_len)
614{
615 struct GNUNET_MQ_Envelope *env;
616 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
617
618 env = GNUNET_MQ_msg_extra (msg,
619 addr_len,
620 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
621 msg->add_remove = htonl (add);
622 msg->addr_class = htonl (delta->ac);
623 GNUNET_memcpy (&msg[1],
624 addr,
625 addr_len);
626 GNUNET_MQ_send (ch->mq,
627 env);
628}
629
630
631/**
632 * Check if we should bother to notify this client about this
633 * address change, and if so, do it.
634 *
635 * @param delta the entry in the list that changed
636 * @param ch client to check
637 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
638 */
639static void
640check_notify_client (struct LocalAddressList *delta,
641 struct ClientHandle *ch,
642 int add)
643{
644 size_t alen;
645 struct sockaddr_in v4;
646 struct sockaddr_in6 v6;
647
648 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
649 return;
650 switch (delta->af)
651 {
652 case AF_INET:
653 alen = sizeof (struct sockaddr_in);
654 GNUNET_memcpy (&v4,
655 &delta->addr,
656 alen);
657 for (unsigned int i=0;i<ch->num_addrs;i++)
658 {
659 const struct sockaddr_in *c4;
660
661 if (AF_INET != ch->addrs[i]->sa_family)
662 return; /* IPv4 not relevant */
663 c4 = (const struct sockaddr_in *) ch->addrs[i];
664 v4.sin_port = c4->sin_port;
665 notify_client (delta,
666 ch,
667 add,
668 &v4,
669 alen);
670 }
671 break;
672 case AF_INET6:
673 alen = sizeof (struct sockaddr_in6);
674 GNUNET_memcpy (&v6,
675 &delta->addr,
676 alen);
677 for (unsigned int i=0;i<ch->num_addrs;i++)
678 {
679 const struct sockaddr_in6 *c6;
680
681 if (AF_INET6 != ch->addrs[i]->sa_family)
682 return; /* IPv4 not relevant */
683 c6 = (const struct sockaddr_in6 *) ch->addrs[i];
684 v6.sin6_port = c6->sin6_port;
685 notify_client (delta,
686 ch,
687 add,
688 &v6,
689 alen);
690 }
691 break;
692 default:
693 GNUNET_break (0);
694 return;
695 }
696}
697
698
699/**
700 * Notify all clients about a change in the list
701 * of addresses this peer has.
702 *
703 * @param delta the entry in the list that changed
704 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
705 */
706static void
707notify_clients (struct LocalAddressList *delta,
708 int add)
709{
710 for (struct ClientHandle *ch = ch_head;
711 NULL != ch;
712 ch = ch->next)
713 check_notify_client (delta,
714 ch,
715 add);
716}
717
718
719/**
720 * Task we run periodically to scan for network interfaces.
721 *
722 * @param cls NULL
723 */
724static void
725run_scan (void *cls)
726{
727 struct IfcProcContext ifc_ctx;
728 int found;
729
730 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
731 &run_scan,
732 NULL);
733 memset (&ifc_ctx,
734 0,
735 sizeof (ifc_ctx));
736 GNUNET_OS_network_interfaces_list (&ifc_proc,
737 &ifc_ctx);
738 /* remove addresses that disappeared */
739 for (struct LocalAddressList *lal = lal_head;
740 NULL != lal;
741 lal = lal->next)
742 {
743 found = GNUNET_NO;
744 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
745 NULL != pos;
746 pos = pos->next)
747 {
748 if ( (pos->af == lal->af) &&
749 (0 == memcmp (&lal->addr,
750 &pos->addr,
751 (AF_INET == lal->af)
752 ? sizeof (struct sockaddr_in)
753 : sizeof (struct sockaddr_in6))) )
754 found = GNUNET_YES;
755 }
756 if (GNUNET_NO == found)
757 notify_clients (lal,
758 GNUNET_NO);
759 }
760
761 /* add addresses that appeared */
762 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
763 NULL != pos;
764 pos = pos->next)
765 {
766 found = GNUNET_NO;
767 for (struct LocalAddressList *lal = lal_head;
768 NULL != lal;
769 lal = lal->next)
770 {
771 if ( (pos->af == lal->af) &&
772 (0 == memcmp (&lal->addr,
773 &pos->addr,
774 (AF_INET == lal->af)
775 ? sizeof (struct sockaddr_in)
776 : sizeof (struct sockaddr_in6))) )
777 found = GNUNET_YES;
778 }
779 if (GNUNET_NO == found)
780 notify_clients (pos,
781 GNUNET_YES);
782 }
783
784 destroy_lal ();
785 lal_head = ifc_ctx.lal_head;
786 lal_tail = ifc_ctx.lal_tail;
787}
788
789
790/**
414 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. 791 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
415 * We remember the client for updates upon future NAT events. 792 * We remember the client for updates upon future NAT events.
416 * 793 *
@@ -438,7 +815,7 @@ handle_register (void *cls,
438 ch->flags = message->flags; 815 ch->flags = message->flags;
439 ch->proto = message->proto; 816 ch->proto = message->proto;
440 ch->adv_port = ntohs (message->adv_port); 817 ch->adv_port = ntohs (message->adv_port);
441 ch->num_addrs = ntohs (message->adv_port); 818 ch->num_addrs = ntohs (message->num_addrs);
442 ch->addrs = GNUNET_new_array (ch->num_addrs, 819 ch->addrs = GNUNET_new_array (ch->num_addrs,
443 struct sockaddr *); 820 struct sockaddr *);
444 left = ntohs (message->header.size) - sizeof (*message); 821 left = ntohs (message->header.size) - sizeof (*message);
@@ -490,7 +867,16 @@ handle_register (void *cls,
490 sa, 867 sa,
491 alen); 868 alen);
492 off += alen; 869 off += alen;
493 } 870 }
871 /* Actually send IP address list to client */
872 for (struct LocalAddressList *lal = lal_head;
873 NULL != lal;
874 lal = lal->next)
875 {
876 check_notify_client (lal,
877 ch,
878 GNUNET_YES);
879 }
494 GNUNET_SERVICE_client_continue (ch->client); 880 GNUNET_SERVICE_client_continue (ch->client);
495} 881}
496 882
@@ -826,344 +1212,289 @@ check_autoconfig_request (void *cls,
826 1212
827 1213
828/** 1214/**
829 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from 1215 * Stop all pending activities with respect to the @a ac
830 * client.
831 * 1216 *
832 * @param cls client who sent the message 1217 * @param ac autoconfiguration to terminate activities for
833 * @param message the message received
834 */ 1218 */
835static void 1219static void
836handle_autoconfig_request (void *cls, 1220terminate_ac_activities (struct AutoconfigContext *ac)
837 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
838{ 1221{
839 struct ClientHandle *ch = cls; 1222 if (NULL != ac->probe_external)
840 size_t left = ntohs (message->header.size) - sizeof (*message);
841 struct GNUNET_CONFIGURATION_Handle *c;
842
843 c = GNUNET_CONFIGURATION_create ();
844 if (GNUNET_OK !=
845 GNUNET_CONFIGURATION_deserialize (c,
846 (const char *) &message[1],
847 left,
848 GNUNET_NO))
849 { 1223 {
850 GNUNET_break (0); 1224 GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external);
851 GNUNET_SERVICE_client_drop (ch->client); 1225 ac->probe_external = NULL;
852 return; 1226 }
1227 if (NULL != ac->timeout_task)
1228 {
1229 GNUNET_SCHEDULER_cancel (ac->timeout_task);
1230 ac->timeout_task = NULL;
853 } 1231 }
854 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
855 "Received REQUEST_AUTO_CONFIG message from client\n");
856 // FIXME: actually handle request...
857 GNUNET_CONFIGURATION_destroy (c);
858 GNUNET_SERVICE_client_continue (ch->client);
859} 1232}
860 1233
861 1234
862/** 1235/**
863 * Task run during shutdown. 1236 * Finish handling the autoconfiguration request and send
1237 * the response to the client.
864 * 1238 *
865 * @param cls unused 1239 * @param cls the `struct AutoconfigContext` to conclude
866 */ 1240 */
867static void 1241static void
868shutdown_task (void *cls) 1242conclude_autoconfig_request (void *cls)
869{ 1243{
870 struct StunExternalIP *se; 1244 struct AutoconfigContext *ac = cls;
1245 struct ClientHandle *ch = ac->ch;
1246 struct GNUNET_NAT_AutoconfigResultMessage *arm;
1247 struct GNUNET_MQ_Envelope *env;
1248 size_t c_size;
1249 char *buf;
1250
1251 ac->timeout_task = NULL;
1252 terminate_ac_activities (ac);
1253
1254 /* Send back response */
1255 buf = GNUNET_CONFIGURATION_serialize (ac->c,
1256 &c_size);
1257 env = GNUNET_MQ_msg_extra (arm,
1258 c_size,
1259 GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
1260 arm->status_code = htonl ((uint32_t) ac->status_code);
1261 arm->type = htonl ((uint32_t) ac->type);
1262 GNUNET_memcpy (&arm[1],
1263 buf,
1264 c_size);
1265 GNUNET_free (buf);
1266 GNUNET_MQ_send (ch->mq,
1267 env);
871 1268
872 while (NULL != (se = se_head)) 1269 /* clean up */
873 { 1270 GNUNET_free (ac->system_type);
874 GNUNET_CONTAINER_DLL_remove (se_head, 1271 GNUNET_CONFIGURATION_destroy (ac->c);
875 se_tail, 1272 GNUNET_CONTAINER_DLL_remove (ac_head,
876 se); 1273 ac_tail,
877 GNUNET_SCHEDULER_cancel (se->timeout_task); 1274 ac);
878 GNUNET_free (se); 1275 GNUNET_free (ac);
879 } 1276 GNUNET_SERVICE_client_continue (ch->client);
880 if (NULL != scan_task)
881 {
882 GNUNET_SCHEDULER_cancel (scan_task);
883 scan_task = NULL;
884 }
885 if (NULL != stats)
886 {
887 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
888 stats = NULL;
889 }
890 destroy_lal ();
891} 1277}
892 1278
893 1279
894/** 1280/**
895 * Closure for #ifc_proc. 1281 * Check if all autoconfiguration operations have concluded,
1282 * and if they have, send the result back to the client.
1283 *
1284 * @param ac autoconfiguation context to check
896 */ 1285 */
897struct IfcProcContext 1286static void
1287check_autoconfig_finished (struct AutoconfigContext *ac)
898{ 1288{
899 1289 if (NULL != ac->probe_external)
900 /** 1290 return;
901 * Head of DLL of local addresses. 1291 GNUNET_SCHEDULER_cancel (ac->timeout_task);
902 */ 1292 ac->timeout_task
903 struct LocalAddressList *lal_head; 1293 = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
904 1294 ac);
905 /** 1295}
906 * Tail of DLL of local addresses.
907 */
908 struct LocalAddressList *lal_tail;
909
910};
911 1296
912 1297
913/** 1298/**
914 * Callback function invoked for each interface found. Adds them 1299 * Update ENABLE_UPNPC configuration option.
915 * to our new address list.
916 * 1300 *
917 * @param cls a `struct IfcProcContext *` 1301 * @param ac autoconfiguration to update
918 * @param name name of the interface (can be NULL for unknown)
919 * @param isDefault is this presumably the default interface
920 * @param addr address of this interface (can be NULL for unknown or unassigned)
921 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
922 * @param netmask the network mask (can be NULL for unknown or unassigned)
923 * @param addrlen length of the address
924 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
925 */ 1302 */
926static int 1303static void
927ifc_proc (void *cls, 1304update_enable_upnpc_option (struct AutoconfigContext *ac)
928 const char *name,
929 int isDefault,
930 const struct sockaddr *addr,
931 const struct sockaddr *broadcast_addr,
932 const struct sockaddr *netmask,
933 socklen_t addrlen)
934{ 1305{
935 struct IfcProcContext *ifc_ctx = cls; 1306 switch (ac->enable_upnpc)
936 struct LocalAddressList *lal;
937 size_t alen;
938 const struct in_addr *ip4;
939 const struct in6_addr *ip6;
940 enum GNUNET_NAT_AddressClass ac;
941
942 switch (addr->sa_family)
943 { 1307 {
944 case AF_INET: 1308 case GNUNET_YES:
945 alen = sizeof (struct sockaddr_in); 1309 GNUNET_CONFIGURATION_set_value_string (ac->c,
946 ip4 = &((const struct sockaddr_in *) addr)->sin_addr; 1310 "NAT",
947 if (match_ipv4 ("127.0.0.0", ip4, 8)) 1311 "ENABLE_UPNPC",
948 ac = GNUNET_NAT_AC_LOOPBACK; 1312 "YES");
949 else if (is_nat_v4 (ip4))
950 ac = GNUNET_NAT_AC_LAN;
951 else
952 ac = GNUNET_NAT_AC_GLOBAL;
953 break; 1313 break;
954 case AF_INET6: 1314 case GNUNET_NO:
955 alen = sizeof (struct sockaddr_in6); 1315 GNUNET_CONFIGURATION_set_value_string (ac->c,
956 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; 1316 "NAT",
957 if (match_ipv6 ("::1", ip6, 128)) 1317 "ENABLE_UPNPC",
958 ac = GNUNET_NAT_AC_LOOPBACK; 1318 "NO");
959 else if (is_nat_v6 (ip6)) 1319 break;
960 ac = GNUNET_NAT_AC_LAN; 1320 case GNUNET_SYSERR:
961 else 1321 /* We are unsure, do not change option */
962 ac = GNUNET_NAT_AC_GLOBAL;
963 if ( (ip6->s6_addr[11] == 0xFF) &&
964 (ip6->s6_addr[12] == 0xFE) )
965 {
966 /* contains a MAC, be extra careful! */
967 ac |= GNUNET_NAT_AC_PRIVATE;
968 }
969 break; 1322 break;
970#if AF_UNIX
971 case AF_UNIX:
972 GNUNET_break (0);
973 return GNUNET_OK;
974#endif
975 default:
976 GNUNET_break (0);
977 return GNUNET_OK;
978 } 1323 }
979 lal = GNUNET_malloc (sizeof (*lal));
980 lal->af = addr->sa_family;
981 lal->ac = ac;
982 GNUNET_memcpy (&lal->addr,
983 addr,
984 alen);
985 GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
986 ifc_ctx->lal_tail,
987 lal);
988 return GNUNET_OK;
989} 1324}
990 1325
991 1326
992/** 1327/**
993 * Notify client about a change in the list 1328 * Handle result from external IP address probe during
994 * of addresses this peer has. 1329 * autoconfiguration.
995 * 1330 *
996 * @param delta the entry in the list that changed 1331 * @param cls our `struct AutoconfigContext`
997 * @param ch client to contact 1332 * @param addr the address, NULL on errors
998 * @param add #GNUNET_YES to add, #GNUNET_NO to remove 1333 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
999 * @param addr the address that changed
1000 * @param addr_len number of bytes in @a addr
1001 */ 1334 */
1002static void 1335static void
1003notify_client (struct LocalAddressList *delta, 1336auto_external_result_cb (void *cls,
1004 struct ClientHandle *ch, 1337 const struct in_addr *addr,
1005 int add, 1338 enum GNUNET_NAT_StatusCode result)
1006 const void *addr,
1007 size_t addr_len)
1008{ 1339{
1009 struct GNUNET_MQ_Envelope *env; 1340 struct AutoconfigContext *ac = cls;
1010 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
1011 1341
1012 env = GNUNET_MQ_msg_extra (msg, 1342 ac->probe_external = NULL;
1013 addr_len, 1343 switch (result)
1014 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); 1344 {
1015 msg->add_remove = htonl (add); 1345 case GNUNET_NAT_ERROR_SUCCESS:
1016 msg->addr_class = htonl (delta->ac); 1346 ac->enable_upnpc = GNUNET_YES;
1017 GNUNET_memcpy (&msg[1], 1347 break;
1018 addr, 1348 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1019 addr_len); 1349 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1020 GNUNET_MQ_send (ch->mq, 1350 case GNUNET_NAT_ERROR_IPC_FAILURE:
1021 env); 1351 ac->enable_upnpc = GNUNET_NO; /* did not work */
1022} 1352 break;
1353 default:
1354 GNUNET_break (0); /* unexpected */
1355 ac->enable_upnpc = GNUNET_SYSERR;
1356 break;
1357 }
1358 update_enable_upnpc_option (ac);
1359 check_autoconfig_finished (ac);
1360}
1023 1361
1024 1362
1025/** 1363/**
1026 * Notify all clients about a change in the list 1364 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
1027 * of addresses this peer has. 1365 * client.
1028 * 1366 *
1029 * @param delta the entry in the list that changed 1367 * @param cls client who sent the message
1030 * @param add #GNUNET_YES to add, #GNUNET_NO to remove 1368 * @param message the message received
1031 */ 1369 */
1032static void 1370static void
1033notify_clients (struct LocalAddressList *delta, 1371handle_autoconfig_request (void *cls,
1034 int add) 1372 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
1035{ 1373{
1036 for (struct ClientHandle *ch = ch_head; 1374 struct ClientHandle *ch = cls;
1037 NULL != ch; 1375 size_t left = ntohs (message->header.size) - sizeof (*message);
1038 ch = ch->next) 1376 struct LocalAddressList *lal;
1377 struct AutoconfigContext *ac;
1378
1379 ac = GNUNET_new (struct AutoconfigContext);
1380 ac->c = GNUNET_CONFIGURATION_create ();
1381 if (GNUNET_OK !=
1382 GNUNET_CONFIGURATION_deserialize (ac->c,
1383 (const char *) &message[1],
1384 left,
1385 GNUNET_NO))
1039 { 1386 {
1040 size_t alen; 1387 GNUNET_break (0);
1041 struct sockaddr_in v4; 1388 GNUNET_SERVICE_client_drop (ch->client);
1042 struct sockaddr_in6 v6; 1389 GNUNET_CONFIGURATION_destroy (ac->c);
1043 1390 GNUNET_free (ac);
1044 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) 1391 return;
1045 continue; 1392 }
1046 switch (delta->af) 1393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1394 "Received REQUEST_AUTO_CONFIG message from client\n");
1395
1396 if (GNUNET_OK !=
1397 GNUNET_CONFIGURATION_get_value_string (ac->c,
1398 "PEER",
1399 "SYSTEM_TYPE",
1400 &ac->system_type))
1401 ac->system_type = "UNKNOWN";
1402
1403 GNUNET_CONTAINER_DLL_insert (ac_head,
1404 ac_tail,
1405 ac);
1406 ac->timeout_task
1407 = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
1408 &conclude_autoconfig_request,
1409 ac);
1410 ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
1411
1412 /* Probe for upnpc */
1413 if (GNUNET_SYSERR ==
1414 GNUNET_OS_check_helper_binary ("upnpc",
1415 GNUNET_NO,
1416 NULL))
1417 {
1418 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1419 _("UPnP client `upnpc` command not found, disabling UPnP\n"));
1420 ac->enable_upnpc = GNUNET_NO;
1421 }
1422 else
1423 {
1424 for (lal = lal_head; NULL != lal; lal = lal->next)
1425 if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN))
1426 /* we are behind NAT, useful to try upnpc */
1427 ac->enable_upnpc = GNUNET_YES;
1428 }
1429 if (GNUNET_YES == ac->enable_upnpc)
1430 {
1431 /* If we are a mobile device, always leave it on as the network
1432 may change to one that supports UPnP anytime. If we are
1433 stationary, check if our network actually supports UPnP, and if
1434 not, disable it. */
1435 if ( (0 == strcasecmp (ac->system_type,
1436 "INFRASTRUCTURE")) ||
1437 (0 == strcasecmp (ac->system_type,
1438 "DESKTOP")) )
1047 { 1439 {
1048 case AF_INET: 1440 /* Check if upnpc gives us an external IP */
1049 alen = sizeof (struct sockaddr_in); 1441 ac->probe_external
1050 GNUNET_memcpy (&v4, 1442 = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb,
1051 &delta->addr, 1443 ac);
1052 alen);
1053 for (unsigned int i=0;i<ch->num_addrs;i++)
1054 {
1055 const struct sockaddr_in *c4;
1056
1057 if (AF_INET != ch->addrs[i]->sa_family)
1058 continue; /* IPv4 not relevant */
1059 c4 = (const struct sockaddr_in *) ch->addrs[i];
1060 v4.sin_port = c4->sin_port;
1061 notify_client (delta,
1062 ch,
1063 add,
1064 &v4,
1065 alen);
1066 }
1067 break;
1068 case AF_INET6:
1069 alen = sizeof (struct sockaddr_in6);
1070 GNUNET_memcpy (&v6,
1071 &delta->addr,
1072 alen);
1073 for (unsigned int i=0;i<ch->num_addrs;i++)
1074 {
1075 const struct sockaddr_in6 *c6;
1076
1077 if (AF_INET6 != ch->addrs[i]->sa_family)
1078 continue; /* IPv4 not relevant */
1079 c6 = (const struct sockaddr_in6 *) ch->addrs[i];
1080 v6.sin6_port = c6->sin6_port;
1081 notify_client (delta,
1082 ch,
1083 add,
1084 &v6,
1085 alen);
1086 }
1087 break;
1088 default:
1089 GNUNET_break (0);
1090 continue;
1091 } 1444 }
1092 } 1445 }
1446 if (NULL == ac->probe_external)
1447 update_enable_upnpc_option (ac);
1448
1449 /* Finally, check if we are already done */
1450 check_autoconfig_finished (ac);
1093} 1451}
1094 1452
1095 1453
1096/** 1454/**
1097 * Task we run periodically to scan for network interfaces. 1455 * Task run during shutdown.
1098 * 1456 *
1099 * @param cls NULL 1457 * @param cls unused
1100 */ 1458 */
1101static void 1459static void
1102run_scan (void *cls) 1460shutdown_task (void *cls)
1103{ 1461{
1104 struct IfcProcContext ifc_ctx; 1462 struct StunExternalIP *se;
1105 int found; 1463 struct AutoconfigContext *ac;
1106 1464
1107 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, 1465 while (NULL != (ac = ac_head))
1108 &run_scan,
1109 NULL);
1110 memset (&ifc_ctx,
1111 0,
1112 sizeof (ifc_ctx));
1113 GNUNET_OS_network_interfaces_list (&ifc_proc,
1114 &ifc_ctx);
1115 for (struct LocalAddressList *lal = lal_head;
1116 NULL != lal;
1117 lal = lal->next)
1118 { 1466 {
1119 found = GNUNET_NO; 1467 GNUNET_CONTAINER_DLL_remove (ac_head,
1120 for (struct LocalAddressList *pos = ifc_ctx.lal_head; 1468 ac_tail,
1121 NULL != pos; 1469 ac);
1122 pos = pos->next) 1470 terminate_ac_activities (ac);
1123 { 1471 GNUNET_free (ac);
1124 if ( (pos->af == lal->af) &&
1125 (0 == memcmp (&lal->addr,
1126 &pos->addr,
1127 (AF_INET == lal->af)
1128 ? sizeof (struct sockaddr_in)
1129 : sizeof (struct sockaddr_in6))) )
1130 found = GNUNET_YES;
1131 }
1132 if (GNUNET_NO == found)
1133 notify_clients (lal,
1134 GNUNET_NO);
1135 } 1472 }
1136 1473 while (NULL != (se = se_head))
1137 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1138 NULL != pos;
1139 pos = pos->next)
1140 { 1474 {
1141 found = GNUNET_NO; 1475 GNUNET_CONTAINER_DLL_remove (se_head,
1142 for (struct LocalAddressList *lal = lal_head; 1476 se_tail,
1143 NULL != lal; 1477 se);
1144 lal = lal->next) 1478 GNUNET_SCHEDULER_cancel (se->timeout_task);
1145 { 1479 GNUNET_free (se);
1146 if ( (pos->af == lal->af) && 1480 }
1147 (0 == memcmp (&lal->addr, 1481 if (NULL != scan_task)
1148 &pos->addr, 1482 {
1149 (AF_INET == lal->af) 1483 GNUNET_SCHEDULER_cancel (scan_task);
1150 ? sizeof (struct sockaddr_in) 1484 scan_task = NULL;
1151 : sizeof (struct sockaddr_in6))) ) 1485 }
1152 found = GNUNET_YES; 1486 if (NULL != stats)
1153 } 1487 {
1154 if (GNUNET_NO == found) 1488 GNUNET_STATISTICS_destroy (stats,
1155 notify_clients (pos, 1489 GNUNET_NO);
1156 GNUNET_YES); 1490 stats = NULL;
1157 } 1491 }
1158
1159 destroy_lal (); 1492 destroy_lal ();
1160 lal_head = ifc_ctx.lal_head;
1161 lal_tail = ifc_ctx.lal_tail;
1162} 1493}
1163 1494
1164 1495
1165/** 1496/**
1166 * Handle network size estimate clients. 1497 * Setup NAT service.
1167 * 1498 *
1168 * @param cls closure 1499 * @param cls closure
1169 * @param c configuration to use 1500 * @param c configuration to use
@@ -1181,6 +1512,26 @@ run (void *cls,
1181 "STUN_STALE", 1512 "STUN_STALE",
1182 &stun_stale_timeout)) 1513 &stun_stale_timeout))
1183 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; 1514 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
1515
1516 /* Check for UPnP */
1517 enable_upnp
1518 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1519 "NAT",
1520 "ENABLE_UPNP");
1521 if (GNUNET_YES == enable_upnp)
1522 {
1523 /* check if it works */
1524 if (GNUNET_SYSERR ==
1525 GNUNET_OS_check_helper_binary ("upnpc",
1526 GNUNET_NO,
1527 NULL))
1528 {
1529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1530 _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
1531 enable_upnp = GNUNET_SYSERR;
1532 }
1533 }
1534
1184 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 1535 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1185 NULL); 1536 NULL);
1186 stats = GNUNET_STATISTICS_create ("nat", 1537 stats = GNUNET_STATISTICS_create ("nat",
diff --git a/src/nat/gnunet-service-nat_mini.c b/src/nat/gnunet-service-nat_mini.c
new file mode 100644
index 000000000..658ec72b7
--- /dev/null
+++ b/src/nat/gnunet-service-nat_mini.c
@@ -0,0 +1,759 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21/**
22 * @file nat/gnunet-service-nat_mini.c
23 * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_nat_service.h"
29#include "gnunet-service-nat_mini.h"
30#include "nat.h"
31
32#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
33
34/**
35 * How long do we give upnpc to create a mapping?
36 */
37#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
38
39/**
40 * How long do we give upnpc to remove a mapping?
41 */
42#define UNMAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
43
44/**
45 * How often do we check for changes in the mapping?
46 */
47#define MAP_REFRESH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
48
49
50/* ************************* external-ip calling ************************ */
51
52/**
53 * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
54 */
55struct GNUNET_NAT_ExternalHandle
56{
57
58 /**
59 * Function to call with the result.
60 */
61 GNUNET_NAT_IPCallback cb;
62
63 /**
64 * Closure for @e cb.
65 */
66 void *cb_cls;
67
68 /**
69 * Read task.
70 */
71 struct GNUNET_SCHEDULER_Task *task;
72
73 /**
74 * Handle to `external-ip` process.
75 */
76 struct GNUNET_OS_Process *eip;
77
78 /**
79 * Handle to stdout pipe of `external-ip`.
80 */
81 struct GNUNET_DISK_PipeHandle *opipe;
82
83 /**
84 * Read handle of @e opipe.
85 */
86 const struct GNUNET_DISK_FileHandle *r;
87
88 /**
89 * Number of bytes in @e buf that are valid.
90 */
91 size_t off;
92
93 /**
94 * Destination of our read operation (output of 'external-ip').
95 */
96 char buf[17];
97
98 /**
99 * Error code for better debugging and user feedback
100 */
101 enum GNUNET_NAT_StatusCode ret;
102};
103
104
105/**
106 * Read the output of `external-ip` into `buf`. When complete, parse
107 * the address and call our callback.
108 *
109 * @param cls the `struct GNUNET_NAT_ExternalHandle`
110 */
111static void
112read_external_ipv4 (void *cls)
113{
114 struct GNUNET_NAT_ExternalHandle *eh = cls;
115 ssize_t ret;
116 struct in_addr addr;
117
118 eh->task = NULL;
119 ret = GNUNET_DISK_file_read (eh->r,
120 &eh->buf[eh->off],
121 sizeof (eh->buf) - eh->off);
122 if (ret > 0)
123 {
124 /* try to read more */
125 eh->off += ret;
126 eh->task
127 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
128 eh->r,
129 &read_external_ipv4,
130 eh);
131 return;
132 }
133 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID;
134 if ( (eh->off > 7) &&
135 (eh->buf[eh->off - 1] == '\n') )
136 {
137 eh->buf[eh->off - 1] = '\0';
138 if (1 == inet_pton (AF_INET,
139 eh->buf,
140 &addr))
141 {
142 if (0 != addr.s_addr)
143 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */
144 else
145 eh->ret = GNUNET_NAT_ERROR_SUCCESS;
146 }
147 }
148 eh->cb (eh->cb_cls,
149 (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
150 eh->ret);
151 GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh);
152}
153
154
155/**
156 * (Asynchronously) signal error invoking `external-ip` to client.
157 *
158 * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed)
159 */
160static void
161signal_external_ip_error (void *cls)
162{
163 struct GNUNET_NAT_ExternalHandle *eh = cls;
164
165 eh->task = NULL;
166 eh->cb (eh->cb_cls,
167 NULL,
168 eh->ret);
169 GNUNET_free (eh);
170}
171
172
173/**
174 * Try to get the external IPv4 address of this peer.
175 *
176 * @param cb function to call with result
177 * @param cb_cls closure for @a cb
178 * @return handle for cancellation (can only be used until @a cb is called), never NULL
179 */
180struct GNUNET_NAT_ExternalHandle *
181GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
182 void *cb_cls)
183{
184 struct GNUNET_NAT_ExternalHandle *eh;
185
186 eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle);
187 eh->cb = cb;
188 eh->cb_cls = cb_cls;
189 eh->ret = GNUNET_NAT_ERROR_SUCCESS;
190 if (GNUNET_SYSERR ==
191 GNUNET_OS_check_helper_binary ("external-ip",
192 GNUNET_NO,
193 NULL))
194 {
195 LOG (GNUNET_ERROR_TYPE_INFO,
196 _("`external-ip' command not found\n"));
197 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND;
198 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
199 eh);
200 return eh;
201 }
202 LOG (GNUNET_ERROR_TYPE_DEBUG,
203 "Running `external-ip' to determine our external IP\n");
204 eh->opipe = GNUNET_DISK_pipe (GNUNET_YES,
205 GNUNET_YES,
206 GNUNET_NO,
207 GNUNET_YES);
208 if (NULL == eh->opipe)
209 {
210 eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
211 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
212 eh);
213 return eh;
214 }
215 eh->eip =
216 GNUNET_OS_start_process (GNUNET_NO,
217 0,
218 NULL,
219 eh->opipe,
220 NULL,
221 "external-ip",
222 "external-ip",
223 NULL);
224 if (NULL == eh->eip)
225 {
226 GNUNET_DISK_pipe_close (eh->opipe);
227 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED;
228 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
229 eh);
230 return eh;
231 }
232 GNUNET_DISK_pipe_close_end (eh->opipe,
233 GNUNET_DISK_PIPE_END_WRITE);
234 eh->r = GNUNET_DISK_pipe_handle (eh->opipe,
235 GNUNET_DISK_PIPE_END_READ);
236 eh->task
237 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
238 eh->r,
239 &read_external_ipv4,
240 eh);
241 return eh;
242}
243
244
245/**
246 * Cancel operation.
247 *
248 * @param eh operation to cancel
249 */
250void
251GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh)
252{
253 if (NULL != eh->eip)
254 {
255 (void) GNUNET_OS_process_kill (eh->eip,
256 SIGKILL);
257 GNUNET_OS_process_destroy (eh->eip);
258 }
259 if (NULL != eh->opipe)
260 {
261 GNUNET_DISK_pipe_close (eh->opipe);
262 eh->opipe = NULL;
263 }
264 if (NULL != eh->task)
265 {
266 GNUNET_SCHEDULER_cancel (eh->task);
267 eh->task = NULL;
268 }
269 GNUNET_free (eh);
270}
271
272
273/* ************************* upnpc calling ************************ */
274
275
276/**
277 * Handle to a mapping created with upnpc.
278 */
279struct GNUNET_NAT_MiniHandle
280{
281
282 /**
283 * Function to call on mapping changes.
284 */
285 GNUNET_NAT_MiniAddressCallback ac;
286
287 /**
288 * Closure for @e ac.
289 */
290 void *ac_cls;
291
292 /**
293 * Command used to install the map.
294 */
295 struct GNUNET_OS_CommandHandle *map_cmd;
296
297 /**
298 * Command used to refresh our map information.
299 */
300 struct GNUNET_OS_CommandHandle *refresh_cmd;
301
302 /**
303 * Command used to remove the mapping.
304 */
305 struct GNUNET_OS_CommandHandle *unmap_cmd;
306
307 /**
308 * Our current external mapping (if we have one).
309 */
310 struct sockaddr_in current_addr;
311
312 /**
313 * We check the mapping periodically to see if it
314 * still works. This task triggers the check.
315 */
316 struct GNUNET_SCHEDULER_Task *refresh_task;
317
318 /**
319 * Are we mapping TCP or UDP?
320 */
321 int is_tcp;
322
323 /**
324 * Did we succeed with creating a mapping?
325 */
326 int did_map;
327
328 /**
329 * Did we find our mapping during refresh scan?
330 */
331 int found;
332
333 /**
334 * Which port are we mapping?
335 */
336 uint16_t port;
337
338};
339
340
341/**
342 * Run "upnpc -l" to find out if our mapping changed.
343 *
344 * @param cls the `struct GNUNET_NAT_MiniHandle`
345 */
346static void
347do_refresh (void *cls);
348
349
350/**
351 * Process the output from the "upnpc -r" command.
352 *
353 * @param cls the `struct GNUNET_NAT_MiniHandle`
354 * @param line line of output, NULL at the end
355 */
356static void
357process_map_output (void *cls,
358 const char *line);
359
360
361/**
362 * Run "upnpc -r" to map our internal port.
363 *
364 * @param mini our handle
365 */
366static void
367run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini)
368{
369 char pstr[6];
370
371 GNUNET_snprintf (pstr,
372 sizeof (pstr),
373 "%u",
374 (unsigned int) mini->port);
375 mini->map_cmd
376 = GNUNET_OS_command_run (&process_map_output,
377 mini,
378 MAP_TIMEOUT,
379 "upnpc",
380 "upnpc",
381 "-r",
382 pstr,
383 mini->is_tcp ? "tcp" : "udp",
384 NULL);
385 if (NULL == mini->map_cmd)
386 {
387 mini->ac (mini->ac_cls,
388 GNUNET_SYSERR,
389 NULL,
390 0,
391 GNUNET_NAT_ERROR_UPNPC_FAILED);
392 return;
393 }
394}
395
396
397/**
398 * Process the output from "upnpc -l" to see if our
399 * external mapping changed. If so, do the notifications.
400 *
401 * @param cls the `struct GNUNET_NAT_MiniHandle`
402 * @param line line of output, NULL at the end
403 */
404static void
405process_refresh_output (void *cls,
406 const char *line)
407{
408 struct GNUNET_NAT_MiniHandle *mini = cls;
409 char pstr[9];
410 const char *s;
411 unsigned int nport;
412 struct in_addr exip;
413
414 if (NULL == line)
415 {
416 GNUNET_OS_command_stop (mini->refresh_cmd);
417 mini->refresh_cmd = NULL;
418 if (GNUNET_NO == mini->found)
419 {
420 /* mapping disappeared, try to re-create */
421 if (GNUNET_YES == mini->did_map)
422 {
423 mini->ac (mini->ac_cls,
424 GNUNET_NO,
425 (const struct sockaddr *) &mini->current_addr,
426 sizeof (mini->current_addr),
427 GNUNET_NAT_ERROR_SUCCESS);
428 mini->did_map = GNUNET_NO;
429 }
430 run_upnpc_r (mini);
431 }
432 return;
433 }
434 if (!mini->did_map)
435 return; /* never mapped, won't find our mapping anyway */
436
437 /* we're looking for output of the form:
438 * "ExternalIPAddress = 12.134.41.124" */
439
440 s = strstr (line,
441 "ExternalIPAddress = ");
442 if (NULL != s)
443 {
444 s += strlen ("ExternalIPAddress = ");
445 if (1 != inet_pton (AF_INET,
446 s,
447 &exip))
448 return; /* skip */
449 if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
450 return; /* no change */
451 /* update mapping */
452 mini->ac (mini->ac_cls,
453 GNUNET_NO,
454 (const struct sockaddr *) &mini->current_addr,
455 sizeof (mini->current_addr),
456 GNUNET_NAT_ERROR_SUCCESS);
457 mini->current_addr.sin_addr = exip;
458 mini->ac (mini->ac_cls,
459 GNUNET_YES,
460 (const struct sockaddr *) &mini->current_addr,
461 sizeof (mini->current_addr),
462 GNUNET_NAT_ERROR_SUCCESS);
463 return;
464 }
465 /*
466 * we're looking for output of the form:
467 *
468 * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
469 * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
470 *
471 * the pattern we look for is:
472 *
473 * "%s TCP PORT->STRING:OURPORT *" or
474 * "%s UDP PORT->STRING:OURPORT *"
475 */
476 GNUNET_snprintf (pstr,
477 sizeof (pstr),
478 ":%u ",
479 mini->port);
480 if (NULL == (s = strstr (line, "->")))
481 return; /* skip */
482 if (NULL == strstr (s, pstr))
483 return; /* skip */
484 if (1 !=
485 SSCANF (line,
486 (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" :
487 "%*u UDP %u->%*s:%*u %*s", &nport))
488 return; /* skip */
489 mini->found = GNUNET_YES;
490 if (nport == ntohs (mini->current_addr.sin_port))
491 return; /* no change */
492
493 /* external port changed, update mapping */
494 mini->ac (mini->ac_cls,
495 GNUNET_NO,
496 (const struct sockaddr *) &mini->current_addr,
497 sizeof (mini->current_addr),
498 GNUNET_NAT_ERROR_SUCCESS);
499 mini->current_addr.sin_port = htons ((uint16_t) nport);
500 mini->ac (mini->ac_cls,
501 GNUNET_YES,
502 (const struct sockaddr *) &mini->current_addr,
503 sizeof (mini->current_addr),
504 GNUNET_NAT_ERROR_SUCCESS);
505}
506
507
508/**
509 * Run "upnpc -l" to find out if our mapping changed.
510 *
511 * @param cls the 'struct GNUNET_NAT_MiniHandle'
512 */
513static void
514do_refresh (void *cls)
515{
516 struct GNUNET_NAT_MiniHandle *mini = cls;
517 int ac;
518
519 mini->refresh_task
520 = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
521 &do_refresh,
522 mini);
523 LOG (GNUNET_ERROR_TYPE_DEBUG,
524 "Running `upnpc' to check if our mapping still exists\n");
525 mini->found = GNUNET_NO;
526 ac = GNUNET_NO;
527 if (NULL != mini->map_cmd)
528 {
529 /* took way too long, abort it! */
530 GNUNET_OS_command_stop (mini->map_cmd);
531 mini->map_cmd = NULL;
532 ac = GNUNET_YES;
533 }
534 if (NULL != mini->refresh_cmd)
535 {
536 /* took way too long, abort it! */
537 GNUNET_OS_command_stop (mini->refresh_cmd);
538 mini->refresh_cmd = NULL;
539 ac = GNUNET_YES;
540 }
541 mini->refresh_cmd =
542 GNUNET_OS_command_run (&process_refresh_output,
543 mini,
544 MAP_TIMEOUT,
545 "upnpc",
546 "upnpc",
547 "-l",
548 NULL);
549 if (GNUNET_YES == ac)
550 mini->ac (mini->ac_cls,
551 GNUNET_SYSERR,
552 NULL,
553 0,
554 GNUNET_NAT_ERROR_UPNPC_TIMEOUT);
555}
556
557
558/**
559 * Process the output from the 'upnpc -r' command.
560 *
561 * @param cls the `struct GNUNET_NAT_MiniHandle`
562 * @param line line of output, NULL at the end
563 */
564static void
565process_map_output (void *cls,
566 const char *line)
567{
568 struct GNUNET_NAT_MiniHandle *mini = cls;
569 const char *ipaddr;
570 char *ipa;
571 const char *pstr;
572 unsigned int port;
573
574 if (NULL == line)
575 {
576 GNUNET_OS_command_stop (mini->map_cmd);
577 mini->map_cmd = NULL;
578 if (GNUNET_YES != mini->did_map)
579 mini->ac (mini->ac_cls,
580 GNUNET_SYSERR,
581 NULL, 0,
582 GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
583 if (NULL == mini->refresh_task)
584 mini->refresh_task =
585 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
586 &do_refresh,
587 mini);
588 return;
589 }
590 /*
591 * The upnpc output we're after looks like this:
592 *
593 * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
594 */
595 if ((NULL == (ipaddr = strstr (line, " "))) ||
596 (NULL == (pstr = strstr (ipaddr, ":"))) ||
597 (1 != SSCANF (pstr + 1, "%u", &port)))
598 {
599 return; /* skip line */
600 }
601 ipa = GNUNET_strdup (ipaddr + 1);
602 strstr (ipa, ":")[0] = '\0';
603 if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
604 {
605 GNUNET_free (ipa);
606 return; /* skip line */
607 }
608 GNUNET_free (ipa);
609
610 mini->current_addr.sin_port = htons (port);
611 mini->current_addr.sin_family = AF_INET;
612#if HAVE_SOCKADDR_IN_SIN_LEN
613 mini->current_addr.sin_len = sizeof (struct sockaddr_in);
614#endif
615 mini->did_map = GNUNET_YES;
616 mini->ac (mini->ac_cls, GNUNET_YES,
617 (const struct sockaddr *) &mini->current_addr,
618 sizeof (mini->current_addr),
619 GNUNET_NAT_ERROR_SUCCESS);
620}
621
622
623/**
624 * Start mapping the given port using (mini)upnpc. This function
625 * should typically not be used directly (it is used within the
626 * general-purpose #GNUNET_NAT_register() code). However, it can be
627 * used if specifically UPnP-based NAT traversal is to be used or
628 * tested.
629 *
630 * @param port port to map
631 * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
632 * @param ac function to call with mapping result
633 * @param ac_cls closure for @a ac
634 * @return NULL on error (no 'upnpc' installed)
635 */
636struct GNUNET_NAT_MiniHandle *
637GNUNET_NAT_mini_map_start (uint16_t port,
638 int is_tcp,
639 GNUNET_NAT_MiniAddressCallback ac,
640 void *ac_cls)
641{
642 struct GNUNET_NAT_MiniHandle *ret;
643
644 if (GNUNET_SYSERR ==
645 GNUNET_OS_check_helper_binary ("upnpc",
646 GNUNET_NO,
647 NULL))
648 {
649 LOG (GNUNET_ERROR_TYPE_INFO,
650 _("`upnpc' command not found\n"));
651 ac (ac_cls,
652 GNUNET_SYSERR,
653 NULL, 0,
654 GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
655 return NULL;
656 }
657 LOG (GNUNET_ERROR_TYPE_DEBUG,
658 "Running `upnpc' to install mapping\n");
659 ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
660 ret->ac = ac;
661 ret->ac_cls = ac_cls;
662 ret->is_tcp = is_tcp;
663 ret->port = port;
664 ret->refresh_task =
665 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
666 &do_refresh,
667 ret);
668 run_upnpc_r (ret);
669 return ret;
670}
671
672
673/**
674 * Process output from our 'unmap' command.
675 *
676 * @param cls the `struct GNUNET_NAT_MiniHandle`
677 * @param line line of output, NULL at the end
678 */
679static void
680process_unmap_output (void *cls,
681 const char *line)
682{
683 struct GNUNET_NAT_MiniHandle *mini = cls;
684
685 if (NULL == line)
686 {
687 LOG (GNUNET_ERROR_TYPE_DEBUG,
688 "UPnP unmap done\n");
689 GNUNET_OS_command_stop (mini->unmap_cmd);
690 mini->unmap_cmd = NULL;
691 GNUNET_free (mini);
692 return;
693 }
694 /* we don't really care about the output... */
695}
696
697
698/**
699 * Remove a mapping created with (mini)upnpc. Calling
700 * this function will give 'upnpc' 1s to remove tha mapping,
701 * so while this function is non-blocking, a task will be
702 * left with the scheduler for up to 1s past this call.
703 *
704 * @param mini the handle
705 */
706void
707GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
708{
709 char pstr[6];
710
711 if (NULL != mini->refresh_task)
712 {
713 GNUNET_SCHEDULER_cancel (mini->refresh_task);
714 mini->refresh_task = NULL;
715 }
716 if (NULL != mini->refresh_cmd)
717 {
718 GNUNET_OS_command_stop (mini->refresh_cmd);
719 mini->refresh_cmd = NULL;
720 }
721 if (NULL != mini->map_cmd)
722 {
723 GNUNET_OS_command_stop (mini->map_cmd);
724 mini->map_cmd = NULL;
725 }
726 if (GNUNET_NO == mini->did_map)
727 {
728 GNUNET_free (mini);
729 return;
730 }
731 mini->ac (mini->ac_cls,
732 GNUNET_NO,
733 (const struct sockaddr *) &mini->current_addr,
734 sizeof (mini->current_addr),
735 GNUNET_NAT_ERROR_SUCCESS);
736 /* Note: oddly enough, deletion uses the external port whereas
737 * addition uses the internal port; this rarely matters since they
738 * often are the same, but it might... */
739 GNUNET_snprintf (pstr,
740 sizeof (pstr),
741 "%u",
742 (unsigned int) ntohs (mini->current_addr.sin_port));
743 LOG (GNUNET_ERROR_TYPE_DEBUG,
744 "Unmapping port %u with UPnP\n",
745 ntohs (mini->current_addr.sin_port));
746 mini->unmap_cmd
747 = GNUNET_OS_command_run (&process_unmap_output,
748 mini,
749 UNMAP_TIMEOUT,
750 "upnpc",
751 "upnpc",
752 "-d",
753 pstr,
754 mini->is_tcp ? "tcp" : "udp",
755 NULL);
756}
757
758
759/* end of gnunet-service-nat_mini.c */
diff --git a/src/nat/gnunet-service-nat_mini.h b/src/nat/gnunet-service-nat_mini.h
new file mode 100644
index 000000000..2c0dd3445
--- /dev/null
+++ b/src/nat/gnunet-service-nat_mini.h
@@ -0,0 +1,127 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21/**
22 * @file nat/gnunet-service-nat_mini.c
23 * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_NAT_MINI_H
27#define GNUNET_SERVICE_NAT_MINI_H
28
29
30/**
31 * Signature of a callback that is given an IP address.
32 *
33 * @param cls closure
34 * @param addr the address, NULL on errors
35 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
36 */
37typedef void
38(*GNUNET_NAT_IPCallback) (void *cls,
39 const struct in_addr *addr,
40 enum GNUNET_NAT_StatusCode result);
41
42
43/**
44 * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation.
45 */
46struct GNUNET_NAT_ExternalHandle;
47
48
49/**
50 * Try to get the external IPv4 address of this peer.
51 *
52 * @param cb function to call with result
53 * @param cb_cls closure for @a cb
54 * @return handle for cancellation (can only be used until @a cb is called), NULL on error
55 */
56struct GNUNET_NAT_ExternalHandle *
57GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
58 void *cb_cls);
59
60
61/**
62 * Cancel operation.
63 *
64 * @param eh operation to cancel
65 */
66void
67GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh);
68
69
70/**
71 * Handle to a mapping created with upnpc.
72 */
73struct GNUNET_NAT_MiniHandle;
74
75
76/**
77 * Signature of the callback passed to #GNUNET_NAT_register() for
78 * a function to call whenever our set of 'valid' addresses changes.
79 *
80 * @param cls closure
81 * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
82 * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
83 * @param addr either the previous or the new public IP address
84 * @param addrlen actual length of the @a addr
85 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
86 */
87typedef void
88(*GNUNET_NAT_MiniAddressCallback) (void *cls,
89 int add_remove,
90 const struct sockaddr *addr,
91 socklen_t addrlen,
92 enum GNUNET_NAT_StatusCode result);
93
94
95/**
96 * Start mapping the given port using (mini)upnpc. This function
97 * should typically not be used directly (it is used within the
98 * general-purpose #GNUNET_NAT_register() code). However, it can be
99 * used if specifically UPnP-based NAT traversal is to be used or
100 * tested.
101 *
102 * @param port port to map
103 * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
104 * @param ac function to call with mapping result
105 * @param ac_cls closure for @a ac
106 * @return NULL on error
107 */
108struct GNUNET_NAT_MiniHandle *
109GNUNET_NAT_mini_map_start (uint16_t port,
110 int is_tcp,
111 GNUNET_NAT_MiniAddressCallback ac,
112 void *ac_cls);
113
114
115/**
116 * Remove a mapping created with (mini)upnpc. Calling
117 * this function will give 'upnpc' 1s to remove the mapping,
118 * so while this function is non-blocking, a task will be
119 * left with the scheduler for up to 1s past this call.
120 *
121 * @param mini the handle
122 */
123void
124GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini);
125
126
127#endif
diff --git a/src/nat/nat.h b/src/nat/nat.h
index 6df72c0ab..3356b19ce 100644
--- a/src/nat/nat.h
+++ b/src/nat/nat.h
@@ -78,7 +78,7 @@ enum GNUNET_NAT_RegisterFlags
78 78
79 /** 79 /**
80 * This client wants to be informed about changes to our 80 * This client wants to be informed about changes to our
81 * external addresses. 81 * applicable addresses.
82 */ 82 */
83 GNUNET_NAT_RF_ADDRESSES = 1, 83 GNUNET_NAT_RF_ADDRESSES = 1,
84 84
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c
index 58ed3e675..3fe97ed85 100644
--- a/src/nat/nat_api.c
+++ b/src/nat/nat_api.c
@@ -331,6 +331,7 @@ do_connect (void *cls)
331 nh), 331 nh),
332 GNUNET_MQ_handler_end () 332 GNUNET_MQ_handler_end ()
333 }; 333 };
334 struct GNUNET_MQ_Envelope *env;
334 335
335 nh->reconnect_task = NULL; 336 nh->reconnect_task = NULL;
336 nh->mq = GNUNET_CLIENT_connecT (nh->cfg, 337 nh->mq = GNUNET_CLIENT_connecT (nh->cfg,
@@ -339,7 +340,13 @@ do_connect (void *cls)
339 &mq_error_handler, 340 &mq_error_handler,
340 nh); 341 nh);
341 if (NULL == nh->mq) 342 if (NULL == nh->mq)
343 {
342 reconnect (nh); 344 reconnect (nh);
345 return;
346 }
347 env = GNUNET_MQ_msg_copy (nh->reg);
348 GNUNET_MQ_send (nh->mq,
349 env);
343} 350}
344 351
345 352
diff --git a/src/util/util.conf b/src/util/util.conf
index 6b9c52d00..ecc94ead0 100644
--- a/src/util/util.conf
+++ b/src/util/util.conf
@@ -48,8 +48,18 @@ GNUNET_USER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/gnunet-${USERHOME:-${USER:-use
48 48
49 49
50[PEER] 50[PEER]
51# Where do we store our private key?
51PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc 52PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc
52 53
54# What kind of system are we on? Choices are
55# INFRASTRUCTURE (always-on, grid, data center)
56# DESKTOP (sometimes-on, grid, office)
57# NOTEBOOK (sometimes-on, mobile, often limited network,
58# if on-battery than large battery)
59# MOBILE (sometimes-on, mobile, always limited network,
60# always battery limited)
61# UNKNOWN (not configured/specified/known)
62SYSTEM_TYPE = UNKNOWN
53 63
54[TESTING] 64[TESTING]
55SPEEDUP_INTERVAL = 0 ms 65SPEEDUP_INTERVAL = 0 ms