diff options
Diffstat (limited to 'src/nat/gnunet-service-nat.c')
-rw-r--r-- | src/nat/gnunet-service-nat.c | 274 |
1 files changed, 163 insertions, 111 deletions
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c index f4755659f..1ebf75608 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c | |||
@@ -28,10 +28,8 @@ | |||
28 | * knowledge about the local network topology. | 28 | * knowledge about the local network topology. |
29 | * | 29 | * |
30 | * TODO: | 30 | * TODO: |
31 | * - call GN_start_gnunet_nat_server_() if possible (i.e. | 31 | * - implement UPnPC/PMP-based NAT traversal |
32 | * when we find we have a non-global IPv4 address) | ||
33 | * - implement autoconfig | 32 | * - implement autoconfig |
34 | * - implmeent UPnPC/PMP-based NAT traversal | ||
35 | * - implement NEW logic for external IP detection | 33 | * - implement NEW logic for external IP detection |
36 | */ | 34 | */ |
37 | #include "platform.h" | 35 | #include "platform.h" |
@@ -91,6 +89,11 @@ struct ClientHandle | |||
91 | enum GNUNET_NAT_RegisterFlags flags; | 89 | enum GNUNET_NAT_RegisterFlags flags; |
92 | 90 | ||
93 | /** | 91 | /** |
92 | * Is any of the @e addrs in a reserved subnet for NAT? | ||
93 | */ | ||
94 | int natted_address; | ||
95 | |||
96 | /** | ||
94 | * Port we would like as we are configured to use this one for | 97 | * Port we would like as we are configured to use this one for |
95 | * advertising (in addition to the one we are binding to). | 98 | * advertising (in addition to the one we are binding to). |
96 | */ | 99 | */ |
@@ -306,6 +309,108 @@ check_register (void *cls, | |||
306 | 309 | ||
307 | 310 | ||
308 | /** | 311 | /** |
312 | * Check if @a ip is in @a network with @a bits netmask. | ||
313 | * | ||
314 | * @param network to test | ||
315 | * @param ip IP address to test | ||
316 | * @param bits bitmask for the network | ||
317 | * @return #GNUNET_YES if @a ip is in @a network | ||
318 | */ | ||
319 | static int | ||
320 | match_ipv4 (const char *network, | ||
321 | const struct in_addr *ip, | ||
322 | uint8_t bits) | ||
323 | { | ||
324 | struct in_addr net; | ||
325 | |||
326 | if (0 == bits) | ||
327 | return GNUNET_YES; | ||
328 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
329 | network, | ||
330 | &net)); | ||
331 | return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); | ||
332 | } | ||
333 | |||
334 | |||
335 | /** | ||
336 | * Check if @a ip is in @a network with @a bits netmask. | ||
337 | * | ||
338 | * @param network to test | ||
339 | * @param ip IP address to test | ||
340 | * @param bits bitmask for the network | ||
341 | * @return #GNUNET_YES if @a ip is in @a network | ||
342 | */ | ||
343 | static int | ||
344 | match_ipv6 (const char *network, | ||
345 | const struct in6_addr *ip, | ||
346 | uint8_t bits) | ||
347 | { | ||
348 | struct in6_addr net; | ||
349 | struct in6_addr mask; | ||
350 | unsigned int off; | ||
351 | |||
352 | if (0 == bits) | ||
353 | return GNUNET_YES; | ||
354 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
355 | network, | ||
356 | &net)); | ||
357 | memset (&mask, 0, sizeof (mask)); | ||
358 | off = 0; | ||
359 | while (bits > 8) | ||
360 | { | ||
361 | mask.s6_addr[off++] = 0xFF; | ||
362 | bits -= 8; | ||
363 | } | ||
364 | while (bits > 0) | ||
365 | { | ||
366 | mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; | ||
367 | bits--; | ||
368 | } | ||
369 | for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++) | ||
370 | if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != | ||
371 | (((uint32_t *) &net)[j] & ((int *) &mask)[j])) | ||
372 | return GNUNET_NO; | ||
373 | return GNUNET_YES; | ||
374 | } | ||
375 | |||
376 | |||
377 | /** | ||
378 | * Test if the given IPv4 address is in a known range | ||
379 | * for private networks. | ||
380 | * | ||
381 | * @param ip address to test | ||
382 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
383 | */ | ||
384 | static int | ||
385 | is_nat_v4 (const struct in_addr *ip) | ||
386 | { | ||
387 | return | ||
388 | match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ | ||
389 | match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ | ||
390 | match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ | ||
391 | match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ | ||
392 | match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ | ||
393 | } | ||
394 | |||
395 | |||
396 | /** | ||
397 | * Test if the given IPv6 address is in a known range | ||
398 | * for private networks. | ||
399 | * | ||
400 | * @param ip address to test | ||
401 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
402 | */ | ||
403 | static int | ||
404 | is_nat_v6 (const struct in6_addr *ip) | ||
405 | { | ||
406 | return | ||
407 | match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ | ||
408 | match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ | ||
409 | match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ | ||
410 | } | ||
411 | |||
412 | |||
413 | /** | ||
309 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. | 414 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. |
310 | * We remember the client for updates upon future NAT events. | 415 | * We remember the client for updates upon future NAT events. |
311 | * | 416 | * |
@@ -352,10 +457,22 @@ handle_register (void *cls, | |||
352 | switch (sa->sa_family) | 457 | switch (sa->sa_family) |
353 | { | 458 | { |
354 | case AF_INET: | 459 | case AF_INET: |
355 | alen = sizeof (struct sockaddr_in); | 460 | { |
461 | const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa; | ||
462 | |||
463 | alen = sizeof (struct sockaddr_in); | ||
464 | if (is_nat_v4 (&s4->sin_addr)) | ||
465 | ch->natted_address = GNUNET_YES; | ||
466 | } | ||
356 | break; | 467 | break; |
357 | case AF_INET6: | 468 | case AF_INET6: |
358 | alen = sizeof (struct sockaddr_in6); | 469 | { |
470 | const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa; | ||
471 | |||
472 | alen = sizeof (struct sockaddr_in6); | ||
473 | if (is_nat_v6 (&s6->sin6_addr)) | ||
474 | ch->natted_address = GNUNET_YES; | ||
475 | } | ||
359 | break; | 476 | break; |
360 | #if AF_UNIX | 477 | #if AF_UNIX |
361 | case AF_UNIX: | 478 | case AF_UNIX: |
@@ -418,7 +535,30 @@ static void | |||
418 | notify_clients_stun_change (const struct sockaddr_in *ip, | 535 | notify_clients_stun_change (const struct sockaddr_in *ip, |
419 | int add) | 536 | int add) |
420 | { | 537 | { |
421 | /* FIXME: notify clients about add/drop */ | 538 | for (struct ClientHandle *ch = ch_head; |
539 | NULL != ch; | ||
540 | ch = ch->next) | ||
541 | { | ||
542 | struct sockaddr_in v4; | ||
543 | struct GNUNET_NAT_AddressChangeNotificationMessage *msg; | ||
544 | struct GNUNET_MQ_Envelope *env; | ||
545 | |||
546 | if (! ch->natted_address) | ||
547 | continue; | ||
548 | v4 = *ip; | ||
549 | v4.sin_port = htons (ch->adv_port); | ||
550 | env = GNUNET_MQ_msg_extra (msg, | ||
551 | sizeof (v4), | ||
552 | GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); | ||
553 | msg->add_remove = htonl ((int32_t) add); | ||
554 | msg->addr_class = htonl (GNUNET_NAT_AC_GLOBAL_EXTERN | | ||
555 | GNUNET_NAT_AC_GLOBAL); | ||
556 | GNUNET_memcpy (&msg[1], | ||
557 | &v4, | ||
558 | sizeof (v4)); | ||
559 | GNUNET_MQ_send (ch->mq, | ||
560 | env); | ||
561 | } | ||
422 | } | 562 | } |
423 | 563 | ||
424 | 564 | ||
@@ -599,6 +739,9 @@ handle_request_connection_reversal (void *cls, | |||
599 | size_t remote_sa_len = ntohs (message->remote_addr_size); | 739 | size_t remote_sa_len = ntohs (message->remote_addr_size); |
600 | const struct sockaddr *local_sa = (const struct sockaddr *) &buf[0]; | 740 | const struct sockaddr *local_sa = (const struct sockaddr *) &buf[0]; |
601 | const struct sockaddr *remote_sa = (const struct sockaddr *) &buf[local_sa_len]; | 741 | const struct sockaddr *remote_sa = (const struct sockaddr *) &buf[local_sa_len]; |
742 | const struct sockaddr_in *l4 = NULL; | ||
743 | const struct sockaddr_in *r4; | ||
744 | int ret; | ||
602 | 745 | ||
603 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 746 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
604 | "Received REQUEST CONNECTION REVERSAL message from client\n"); | 747 | "Received REQUEST CONNECTION REVERSAL message from client\n"); |
@@ -611,6 +754,7 @@ handle_request_connection_reversal (void *cls, | |||
611 | GNUNET_SERVICE_client_drop (ch->client); | 754 | GNUNET_SERVICE_client_drop (ch->client); |
612 | return; | 755 | return; |
613 | } | 756 | } |
757 | l4 = (const struct sockaddr_in *) local_sa; | ||
614 | break; | 758 | break; |
615 | case AF_INET6: | 759 | case AF_INET6: |
616 | if (local_sa_len != sizeof (struct sockaddr_in6)) | 760 | if (local_sa_len != sizeof (struct sockaddr_in6)) |
@@ -619,6 +763,9 @@ handle_request_connection_reversal (void *cls, | |||
619 | GNUNET_SERVICE_client_drop (ch->client); | 763 | GNUNET_SERVICE_client_drop (ch->client); |
620 | return; | 764 | return; |
621 | } | 765 | } |
766 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
767 | _("Connection reversal for IPv6 not supported yet\n")); | ||
768 | ret = GNUNET_SYSERR; | ||
622 | break; | 769 | break; |
623 | default: | 770 | default: |
624 | GNUNET_break (0); | 771 | GNUNET_break (0); |
@@ -634,6 +781,10 @@ handle_request_connection_reversal (void *cls, | |||
634 | GNUNET_SERVICE_client_drop (ch->client); | 781 | GNUNET_SERVICE_client_drop (ch->client); |
635 | return; | 782 | return; |
636 | } | 783 | } |
784 | r4 = (const struct sockaddr_in *) remote_sa; | ||
785 | ret = GN_request_connection_reversal (&l4->sin_addr, | ||
786 | ntohs (l4->sin_port), | ||
787 | &r4->sin_addr); | ||
637 | break; | 788 | break; |
638 | case AF_INET6: | 789 | case AF_INET6: |
639 | if (remote_sa_len != sizeof (struct sockaddr_in6)) | 790 | if (remote_sa_len != sizeof (struct sockaddr_in6)) |
@@ -642,15 +793,18 @@ handle_request_connection_reversal (void *cls, | |||
642 | GNUNET_SERVICE_client_drop (ch->client); | 793 | GNUNET_SERVICE_client_drop (ch->client); |
643 | return; | 794 | return; |
644 | } | 795 | } |
796 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
797 | _("Connection reversal for IPv6 not supported yet\n")); | ||
798 | ret = GNUNET_SYSERR; | ||
645 | break; | 799 | break; |
646 | default: | 800 | default: |
647 | GNUNET_break (0); | 801 | GNUNET_break (0); |
648 | GNUNET_SERVICE_client_drop (ch->client); | 802 | GNUNET_SERVICE_client_drop (ch->client); |
649 | return; | 803 | return; |
650 | } | 804 | } |
651 | /* FIXME: actually run the logic by | 805 | if (GNUNET_OK != ret) |
652 | calling 'GN_request_connection_reversal()' */ | 806 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, |
653 | 807 | _("Connection reversal request failed\n")); | |
654 | GNUNET_SERVICE_client_continue (ch->client); | 808 | GNUNET_SERVICE_client_continue (ch->client); |
655 | } | 809 | } |
656 | 810 | ||
@@ -757,108 +911,6 @@ struct IfcProcContext | |||
757 | 911 | ||
758 | 912 | ||
759 | /** | 913 | /** |
760 | * Check if @a ip is in @a network with @a bits netmask. | ||
761 | * | ||
762 | * @param network to test | ||
763 | * @param ip IP address to test | ||
764 | * @param bits bitmask for the network | ||
765 | * @return #GNUNET_YES if @a ip is in @a network | ||
766 | */ | ||
767 | static int | ||
768 | match_ipv4 (const char *network, | ||
769 | const struct in_addr *ip, | ||
770 | uint8_t bits) | ||
771 | { | ||
772 | struct in_addr net; | ||
773 | |||
774 | if (0 == bits) | ||
775 | return GNUNET_YES; | ||
776 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
777 | network, | ||
778 | &net)); | ||
779 | return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); | ||
780 | } | ||
781 | |||
782 | |||
783 | /** | ||
784 | * Check if @a ip is in @a network with @a bits netmask. | ||
785 | * | ||
786 | * @param network to test | ||
787 | * @param ip IP address to test | ||
788 | * @param bits bitmask for the network | ||
789 | * @return #GNUNET_YES if @a ip is in @a network | ||
790 | */ | ||
791 | static int | ||
792 | match_ipv6 (const char *network, | ||
793 | const struct in6_addr *ip, | ||
794 | uint8_t bits) | ||
795 | { | ||
796 | struct in6_addr net; | ||
797 | struct in6_addr mask; | ||
798 | unsigned int off; | ||
799 | |||
800 | if (0 == bits) | ||
801 | return GNUNET_YES; | ||
802 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
803 | network, | ||
804 | &net)); | ||
805 | memset (&mask, 0, sizeof (mask)); | ||
806 | off = 0; | ||
807 | while (bits > 8) | ||
808 | { | ||
809 | mask.s6_addr[off++] = 0xFF; | ||
810 | bits -= 8; | ||
811 | } | ||
812 | while (bits > 0) | ||
813 | { | ||
814 | mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; | ||
815 | bits--; | ||
816 | } | ||
817 | for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++) | ||
818 | if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != | ||
819 | (((uint32_t *) &net)[j] & ((int *) &mask)[j])) | ||
820 | return GNUNET_NO; | ||
821 | return GNUNET_YES; | ||
822 | } | ||
823 | |||
824 | |||
825 | /** | ||
826 | * Test if the given IPv4 address is in a known range | ||
827 | * for private networks. | ||
828 | * | ||
829 | * @param ip address to test | ||
830 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
831 | */ | ||
832 | static int | ||
833 | is_nat_v4 (const struct in_addr *ip) | ||
834 | { | ||
835 | return | ||
836 | match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ | ||
837 | match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ | ||
838 | match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ | ||
839 | match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ | ||
840 | match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ | ||
841 | } | ||
842 | |||
843 | |||
844 | /** | ||
845 | * Test if the given IPv6 address is in a known range | ||
846 | * for private networks. | ||
847 | * | ||
848 | * @param ip address to test | ||
849 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
850 | */ | ||
851 | static int | ||
852 | is_nat_v6 (const struct in6_addr *ip) | ||
853 | { | ||
854 | return | ||
855 | match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ | ||
856 | match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ | ||
857 | match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ | ||
858 | } | ||
859 | |||
860 | |||
861 | /** | ||
862 | * Callback function invoked for each interface found. Adds them | 914 | * Callback function invoked for each interface found. Adds them |
863 | * to our new address list. | 915 | * to our new address list. |
864 | * | 916 | * |