51
51
#include <pthread.h>
52
52
#include <stdalign.h>
53
53
54
+ #ifndef _WIN32
55
+ #include <ifaddrs.h>
56
+ #endif
57
+
54
58
#include "debug.h"
55
59
#include "host.h"
56
60
#include "memory.h"
71
75
72
76
#define DEFAULT_MAX_UDP_READER_QUEUE_LEN (1920/3*8*1080/1152) //< 10-bit FullHD frame divided by 1280 MTU packets (minus headers)
73
77
78
+ static unsigned get_ifindex (const char * iface );
74
79
static int resolve_address (socket_udp * s , const char * addr , uint16_t tx_port );
75
80
static void * udp_reader (void * arg );
81
+ static void udp_leave_mcast_grp4 (unsigned long addr , int fd ,
82
+ const char iface []);
76
83
77
84
#define IPv4 4
78
85
#define IPv6 6
@@ -167,7 +174,7 @@ struct socket_udp_local {
167
174
struct _socket_udp {
168
175
struct sockaddr_storage sock ;
169
176
socklen_t sock_len ;
170
- unsigned int ifindex ; ///< iface index for multicast
177
+ char iface [ IF_NAMESIZE ] ; ///< iface for multicast
171
178
172
179
struct socket_udp_local * local ;
173
180
bool local_is_slave ; // whether is the local
@@ -364,16 +371,70 @@ static bool udp_addr_valid4(const char *dst)
364
371
return false;
365
372
}
366
373
367
- static bool udp_join_mcast_grp4 (unsigned long addr , int rx_fd , int tx_fd , int ttl , unsigned int ifindex )
374
+ /**
375
+ * @returns 1. 0 (htonl(INADDR_ANY)) if iface is "";
376
+ * 2. representation of the address if iface is an adress
377
+ * 3a. [Windows only] interface index
378
+ * 3b. [otherwise] iface local address
379
+ */
380
+ static in_addr_t
381
+ get_iface_local_addr4 (const char iface [])
382
+ {
383
+ if (iface [0 ] == '\0' ) {
384
+ return htonl (INADDR_ANY );
385
+ }
386
+
387
+ // address specified by bind address
388
+ struct in_addr iface_addr ;
389
+ if (inet_pton (AF_INET , iface , & iface_addr ) == 1 ) {
390
+ return iface_addr .s_addr ;
391
+ }
392
+
393
+ unsigned int ifindex = get_ifindex (iface );
394
+ if (ifindex == (unsigned ) -1 ) {
395
+ return htonl (INADDR_ANY );
396
+ }
397
+
398
+ #ifdef _WIN32
399
+ // Windows allow the interface specification by ifindex
400
+ return htonl (ifindex );
401
+ #else
402
+ struct ifaddrs * a = NULL ;
403
+ getifaddrs (& a );
404
+ struct ifaddrs * p = a ;
405
+ while (NULL != p ) {
406
+ if (p -> ifa_addr != NULL && p -> ifa_addr -> sa_family == AF_INET &&
407
+ strcmp (iface , p -> ifa_name ) == 0 ) {
408
+ struct sockaddr_in * sin = (void * ) p -> ifa_addr ;
409
+ in_addr_t ret = sin -> sin_addr .s_addr ;
410
+ freeifaddrs (a );
411
+ return ret ;
412
+ }
413
+ p = p -> ifa_next ;
414
+ }
415
+ freeifaddrs (a );
416
+ MSG (ERROR , "Interface %s has assigned no IPv4 address!\n" , iface );
417
+ return htonl (INADDR_ANY );
418
+ #endif
419
+ }
420
+
421
+ static bool
422
+ udp_join_mcast_grp4 (unsigned long addr , int rx_fd , int tx_fd , int ttl ,
423
+ const char iface [])
368
424
{
369
425
if (IN_MULTICAST (ntohl (addr ))) {
370
426
#ifndef _WIN32
371
427
char loop = 1 ;
372
428
#endif
373
429
struct ip_mreq imr ;
374
430
431
+ in_addr_t iface_addr = get_iface_local_addr4 (iface );
432
+ char buf [IN4_MAX_ASCII_LEN + 1 ] = "(err. unknown)" ;
433
+ inet_ntop (AF_INET , & iface_addr , buf , sizeof buf );
434
+ MSG (INFO , "mcast4 iface bound to: %s\n" , buf );
435
+
375
436
imr .imr_multiaddr .s_addr = addr ;
376
- imr .imr_interface .s_addr = ifindex ;
437
+ imr .imr_interface .s_addr = iface_addr ;
377
438
378
439
if (SETSOCKOPT
379
440
(rx_fd , IPPROTO_IP , IP_ADD_MEMBERSHIP , (char * )& imr ,
@@ -401,20 +462,21 @@ static bool udp_join_mcast_grp4(unsigned long addr, int rx_fd, int tx_fd, int tt
401
462
}
402
463
if (SETSOCKOPT
403
464
(tx_fd , IPPROTO_IP , IP_MULTICAST_IF ,
404
- (char * )& ifindex , sizeof ( ifindex ) ) != 0 ) {
465
+ (char * ) & iface_addr , sizeof iface_addr ) != 0 ) {
405
466
socket_error ("setsockopt IP_MULTICAST_IF" );
406
467
return false;
407
468
}
408
469
}
409
470
return true;
410
471
}
411
472
412
- static void udp_leave_mcast_grp4 (unsigned long addr , int fd )
473
+ static void
474
+ udp_leave_mcast_grp4 (unsigned long addr , int fd , const char iface [])
413
475
{
414
476
if (IN_MULTICAST (ntohl (addr ))) {
415
477
struct ip_mreq imr ;
416
478
imr .imr_multiaddr .s_addr = addr ;
417
- imr .imr_interface .s_addr = INADDR_ANY ;
479
+ imr .imr_interface .s_addr = get_iface_local_addr4 ( iface ) ;
418
480
if (SETSOCKOPT
419
481
(fd , IPPROTO_IP , IP_DROP_MEMBERSHIP , (char * )& imr ,
420
482
sizeof (struct ip_mreq )) != 0 ) {
@@ -470,16 +532,32 @@ static bool udp_addr_valid6(const char *dst)
470
532
return false;
471
533
}
472
534
473
- static bool udp_join_mcast_grp6 (struct in6_addr sin6_addr , int rx_fd , int tx_fd , int ttl , unsigned int ifindex )
535
+ static bool
536
+ udp_join_mcast_grp6 (struct in6_addr sin6_addr , int rx_fd , int tx_fd , int ttl ,
537
+ const char iface [])
474
538
{
475
539
#ifdef HAVE_IPv6
540
+ // macOS handles v4-mapped addresses transparently as other v6 addrs;
541
+ // Linux/Win need to use the v4 sockpts
542
+ #ifdef __APPLE__
543
+ in_addr_t v4mapped = 0 ;
544
+ memcpy ((void * ) & v4mapped , sin6_addr .s6_addr + 12 , sizeof v4mapped );
545
+ if (IN6_IS_ADDR_MULTICAST (& sin6_addr ) ||
546
+ (IN6_IS_ADDR_V4MAPPED (& sin6_addr ) &&
547
+ IN_MULTICAST (ntohl (v4mapped )))) {
548
+ #else
476
549
if (IN6_IS_ADDR_MULTICAST (& sin6_addr )) {
550
+ #endif
477
551
unsigned int loop = 1 ;
478
552
struct ipv6_mreq imr ;
479
553
#ifdef MUSICA_IPV6
480
554
imr .i6mr_interface = 1 ;
481
555
imr .i6mr_multiaddr = sin6_addr ;
482
556
#else
557
+ unsigned int ifindex = get_ifindex (iface );
558
+ if (ifindex == (unsigned ) -1 ) {
559
+ return false;
560
+ }
483
561
imr .ipv6mr_multiaddr = sin6_addr ;
484
562
imr .ipv6mr_interface = ifindex ;
485
563
#endif
@@ -508,28 +586,61 @@ static bool udp_join_mcast_grp6(struct in6_addr sin6_addr, int rx_fd, int tx_fd,
508
586
MSG (WARNING , "Using multicast but not setting TTL.\n" );
509
587
}
510
588
if (ifindex != 0 ) {
589
+ #ifdef __APPLE__
590
+ MSG (WARNING ,
591
+ "Interface specification may not work "
592
+ "with IPv6 sockets on macOS.\n%s\n" ,
593
+ IN6_IS_ADDR_MULTICAST (& sin6_addr )
594
+ ? "Please contact us for details or resolution."
595
+ : "Use '-4' to enforce IPv4 socket" );
596
+ #endif
511
597
if (SETSOCKOPT (tx_fd , IPPROTO_IPV6 , IPV6_MULTICAST_IF ,
512
598
(char * ) & ifindex ,
513
599
sizeof (ifindex )) != 0 ) {
514
600
socket_error ("setsockopt IPV6_MULTICAST_IF" );
515
601
return false;
516
602
}
517
603
}
604
+
605
+ return true;
606
+ }
607
+ if (IN6_IS_ADDR_V4MAPPED (& sin6_addr )) {
608
+ in_addr_t v4mapped = 0 ;
609
+ memcpy ((void * ) & v4mapped , sin6_addr .s6_addr + 12 ,
610
+ sizeof v4mapped );
611
+ return udp_join_mcast_grp4 (v4mapped , rx_fd , tx_fd , ttl ,
612
+ iface );
518
613
}
519
614
520
615
return true;
616
+ #else
617
+ return false;
521
618
#endif
522
619
}
523
620
524
- static void udp_leave_mcast_grp6 (struct in6_addr sin6_addr , int fd , unsigned int ifindex )
621
+ static void
622
+ udp_leave_mcast_grp6 (struct in6_addr sin6_addr , int fd , const char iface [])
525
623
{
526
624
#ifdef HAVE_IPv6
625
+ // see udp_join_mcast_grp for mac/Linux+Win difference
626
+ #ifdef __APPLE__
627
+ in_addr_t v4mapped = 0 ;
628
+ memcpy ((void * ) & v4mapped , sin6_addr .s6_addr + 12 , sizeof v4mapped );
629
+ if (IN6_IS_ADDR_MULTICAST (& sin6_addr ) ||
630
+ (IN6_IS_ADDR_V4MAPPED (& sin6_addr ) &&
631
+ IN_MULTICAST (ntohl (v4mapped )))) {
632
+ #else
527
633
if (IN6_IS_ADDR_MULTICAST (& sin6_addr )) {
634
+ #endif
528
635
struct ipv6_mreq imr ;
529
636
#ifdef MUSICA_IPV6
530
637
imr .i6mr_interface = 1 ;
531
638
imr .i6mr_multiaddr = sin6_addr ;
532
639
#else
640
+ unsigned int ifindex = get_ifindex (iface );
641
+ if (ifindex == (unsigned ) -1 ) {
642
+ return ;
643
+ }
533
644
imr .ipv6mr_multiaddr = sin6_addr ;
534
645
imr .ipv6mr_interface = ifindex ;
535
646
#endif
@@ -539,7 +650,13 @@ static void udp_leave_mcast_grp6(struct in6_addr sin6_addr, int fd, unsigned int
539
650
sizeof (struct ipv6_mreq )) != 0 ) {
540
651
socket_error ("setsockopt IPV6_DROP_MEMBERSHIP" );
541
652
}
653
+ } else if (IN6_IS_ADDR_V4MAPPED (& sin6_addr )) {
654
+ in_addr_t v4mapped = 0 ;
655
+ memcpy ((void * ) & v4mapped , sin6_addr .s6_addr + 12 ,
656
+ sizeof v4mapped );
657
+ udp_leave_mcast_grp4 (v4mapped , fd , iface );
542
658
}
659
+
543
660
#else
544
661
UNUSED (s );
545
662
#endif /* HAVE_IPv6 */
@@ -764,6 +881,47 @@ static bool set_sock_opts_and_bind(fd_t fd, bool ipv6, uint16_t rx_port, int ttl
764
881
return true;
765
882
}
766
883
884
+ static unsigned
885
+ get_ifindex (const char * iface )
886
+ {
887
+ if (iface [0 ] == '\0' ) {
888
+ return 0 ; // default
889
+ }
890
+ if (strcmp (iface , "help" ) == 0 ) {
891
+ printf (
892
+ "mcast interface specification can be one of following:\n" );
893
+ printf ( "1. interface name\n" );
894
+ printf ( "2. interface index\n" );
895
+ printf ( "3. bind address (IPv4 only)\n" );
896
+ printf ("\n" );
897
+ return (unsigned ) -1 ;
898
+ }
899
+ const unsigned ret = if_nametoindex (iface );
900
+ if (ret != 0 ) {
901
+ return ret ;
902
+ }
903
+ // check if the value isn't the index itself
904
+ char * endptr = NULL ;
905
+ const long val = strtol (iface , & endptr , 0 );
906
+ if (* endptr == '\0' && val >= 0 && (unsigned ) val <= UINT_MAX ) {
907
+ char buf [IF_NAMESIZE ];
908
+ if (if_indextoname (val , buf ) != NULL ) {
909
+ MSG (INFO , "Using mcast interface %s\n" , buf );
910
+ return val ;
911
+ }
912
+ }
913
+ struct in_addr iface_addr ;
914
+ if (inet_pton (AF_INET , iface , & iface_addr ) == 1 ) {
915
+ error_msg ("Interface identified with addres %s not allowed "
916
+ "here. Try '-4'...\n" ,
917
+ iface );
918
+ return (unsigned ) -1 ;
919
+ }
920
+ error_msg ("Illegal interface specification '%s': %s\n" , iface ,
921
+ ug_strerror (errno ));
922
+ return (unsigned ) -1 ;
923
+ }
924
+
767
925
ADD_TO_PARAM ("udp-queue-len" ,
768
926
"* udp-queue-len=<l>\n"
769
927
" Use different queue size than default DEFAULT_MAX_UDP_READER_QUEUE_LEN\n" );
@@ -814,12 +972,7 @@ socket_udp *udp_init_if(const char *addr, const char *iface, uint16_t rx_port,
814
972
goto error ;
815
973
}
816
974
if (iface != NULL ) {
817
- if ((s -> ifindex = if_nametoindex (iface )) == 0 ) {
818
- error_msg ("Illegal interface specification\n" );
819
- goto error ;
820
- }
821
- } else {
822
- s -> ifindex = 0 ;
975
+ snprintf_ch (s -> iface , "%s" , iface );
823
976
}
824
977
#ifdef _WIN32
825
978
if (!is_host_loopback (addr )) {
@@ -877,12 +1030,16 @@ socket_udp *udp_init_if(const char *addr, const char *iface, uint16_t rx_port,
877
1030
878
1031
switch (s -> local -> mode ) {
879
1032
case IPv4 :
880
- if (!udp_join_mcast_grp4 (((struct sockaddr_in * )& s -> sock )-> sin_addr .s_addr , s -> local -> rx_fd , s -> local -> tx_fd , ttl , s -> ifindex )) {
1033
+ if (!udp_join_mcast_grp4 (
1034
+ ((struct sockaddr_in * ) & s -> sock )-> sin_addr .s_addr ,
1035
+ s -> local -> rx_fd , s -> local -> tx_fd , ttl , s -> iface )) {
881
1036
goto error ;
882
1037
}
883
1038
break ;
884
1039
case IPv6 :
885
- if (!udp_join_mcast_grp6 (((struct sockaddr_in6 * )& s -> sock )-> sin6_addr , s -> local -> rx_fd , s -> local -> tx_fd , ttl , s -> ifindex )) {
1040
+ if (!udp_join_mcast_grp6 (
1041
+ ((struct sockaddr_in6 * ) & s -> sock )-> sin6_addr ,
1042
+ s -> local -> rx_fd , s -> local -> tx_fd , ttl , s -> iface )) {
886
1043
goto error ;
887
1044
}
888
1045
break ;
@@ -981,10 +1138,14 @@ void udp_exit(socket_udp * s)
981
1138
}
982
1139
switch (s -> local -> mode ) {
983
1140
case IPv4 :
984
- udp_leave_mcast_grp4 (((struct sockaddr_in * )& s -> sock )-> sin_addr .s_addr , s -> local -> rx_fd );
1141
+ udp_leave_mcast_grp4 (
1142
+ ((struct sockaddr_in * ) & s -> sock )-> sin_addr .s_addr ,
1143
+ s -> local -> rx_fd , s -> iface );
985
1144
break ;
986
1145
case IPv6 :
987
- udp_leave_mcast_grp6 (((struct sockaddr_in6 * )& s -> sock )-> sin6_addr , s -> local -> rx_fd , s -> ifindex );
1146
+ udp_leave_mcast_grp6 (
1147
+ ((struct sockaddr_in6 * ) & s -> sock )-> sin6_addr ,
1148
+ s -> local -> rx_fd , s -> iface );
988
1149
break ;
989
1150
}
990
1151
0 commit comments