diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-12-17 08:00:29 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-12-17 08:00:29 +0100 |
commit | 545faeda8f7fb7e03bb39602df6142f630157050 (patch) | |
tree | 26aac9f044212d5f492b4ac3d0522af136aab58e /src/nat/gnunet-service-nat.c | |
parent | a1e76003ca590ea8d3e9387da35d87419417abb8 (diff) | |
download | gnunet-545faeda8f7fb7e03bb39602df6142f630157050.tar.gz gnunet-545faeda8f7fb7e03bb39602df6142f630157050.zip |
misc. improvements to new NAT service, starting with autoconfiguration logic
Diffstat (limited to 'src/nat/gnunet-service-nat.c')
-rw-r--r-- | src/nat/gnunet-service-nat.c | 899 |
1 files changed, 625 insertions, 274 deletions
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 | */ | ||
198 | struct 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 | */ | ||
257 | static struct AutoconfigContext *ac_head; | ||
258 | |||
259 | /** | ||
260 | * DLL of our autoconfiguration operations. | ||
261 | */ | ||
262 | static 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 | */ |
192 | static struct GNUNET_TIME_Relative stun_stale_timeout; | 267 | static struct GNUNET_TIME_Relative stun_stale_timeout; |
@@ -236,6 +311,12 @@ static struct StunExternalIP *se_head; | |||
236 | */ | 311 | */ |
237 | static struct StunExternalIP *se_tail; | 312 | static 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 | */ | ||
318 | static 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 | */ | ||
503 | struct 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 | */ | ||
532 | static int | ||
533 | ifc_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 | */ | ||
608 | static void | ||
609 | notify_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 | */ | ||
639 | static void | ||
640 | check_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 | */ | ||
706 | static void | ||
707 | notify_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 | */ | ||
724 | static void | ||
725 | run_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 | */ |
835 | static void | 1219 | static void |
836 | handle_autoconfig_request (void *cls, | 1220 | terminate_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 | */ |
867 | static void | 1241 | static void |
868 | shutdown_task (void *cls) | 1242 | conclude_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 | */ |
897 | struct IfcProcContext | 1286 | static void |
1287 | check_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 | */ |
926 | static int | 1303 | static void |
927 | ifc_proc (void *cls, | 1304 | update_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 | */ |
1002 | static void | 1335 | static void |
1003 | notify_client (struct LocalAddressList *delta, | 1336 | auto_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 | */ |
1032 | static void | 1370 | static void |
1033 | notify_clients (struct LocalAddressList *delta, | 1371 | handle_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 | */ |
1101 | static void | 1459 | static void |
1102 | run_scan (void *cls) | 1460 | shutdown_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", |