From: Andre Noll Date: Mon, 16 Feb 2009 22:38:16 +0000 (+0100) Subject: Merge commit 'fml/master' X-Git-Tag: v0.3.4~57^2~19 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=6ba0a5055cfdec25d54d90a56f9345d5d4be09be;hp=89a8b08bf41981eaf21c733400d8a14f93c90159 Merge commit 'fml/master' --- diff --git a/acl.c b/acl.c index 72869ee8..14edcf7d 100644 --- a/acl.c +++ b/acl.c @@ -73,14 +73,13 @@ no_match: * \param addr The address to add. * \param netmask The netmask to use for this entry. */ -static void acl_add_entry(struct list_head *acl, struct in_addr addr, - int netmask) +static void acl_add_entry(struct list_head *acl, char *addr, int netmask) { struct access_info *ai = para_malloc(sizeof(struct access_info)); - ai->addr = addr; + + inet_pton(AF_INET, addr, &ai->addr); ai->netmask = netmask; - PARA_INFO_LOG("adding %s/%i to access list\n", inet_ntoa(ai->addr), - ai->netmask); + PARA_INFO_LOG("adding %s/%i to access list\n", addr, ai->netmask); para_list_add(&ai->node, acl); } @@ -91,21 +90,18 @@ static void acl_add_entry(struct list_head *acl, struct in_addr addr, * \param addr The address to delete. * \param netmask The netmask of the entry to be removed from the list. */ -static void acl_del_entry(struct list_head *acl, struct in_addr addr, - int netmask) +static void acl_del_entry(struct list_head *acl, char *addr, int netmask) { struct access_info *ai, *tmp; list_for_each_entry_safe(ai, tmp, acl, node) { - char *nad = para_strdup(inet_ntoa(ai->addr)); - if (!strcmp(nad, inet_ntoa(addr)) && + if (!strcmp(addr, inet_ntoa(ai->addr)) && ai->netmask == netmask) { PARA_NOTICE_LOG("removing %s/%i from access list\n", - nad, ai->netmask); + addr, ai->netmask); list_del(&ai->node); free(ai); } - free(nad); } } @@ -146,18 +142,17 @@ void acl_init(struct list_head *acl, char * const *acl_info, int num) for (i = 0; i < num; i++) { char *arg = para_strdup(acl_info[i]); char *p = strchr(arg, '/'); - struct in_addr addr; int netmask; if (!p) goto err; *p = '\0'; - if (!inet_pton(AF_INET, arg, &addr)) + if (!is_valid_ipv4_address(arg)) goto err; netmask = atoi(++p); if (netmask < 0 || netmask > 32) goto err; - acl_add_entry(acl, addr, netmask); + acl_add_entry(acl, arg, netmask); goto success; err: PARA_CRIT_LOG("syntax error: %s\n", acl_info[i]); @@ -193,8 +188,8 @@ int acl_check_access(int fd, struct list_head *acl, int default_deny) * \param acl The access control list. * \param default_deny Whether \a acl is a whitelist. */ -void acl_allow(struct in_addr addr, int netmask, - struct list_head *acl, int default_deny) +void acl_allow(char *addr, int netmask, + struct list_head *acl, int default_deny) { if (default_deny) acl_add_entry(acl, addr, netmask); @@ -205,13 +200,13 @@ void acl_allow(struct in_addr addr, int netmask, /** * Deny access for a range of IP addresses. * - * \param addr The address to permit. - * \param netmask The netmask of the entry to be permitted. + * \param addr The address to deny. + * \param netmask The netmask of the entry to be denied. * \param acl The access control list. * \param default_deny Whether \a acl is a whitelist. */ -void acl_deny(struct in_addr addr, int netmask, - struct list_head *acl, int default_deny) +void acl_deny(char *addr, int netmask, + struct list_head *acl, int default_deny) { acl_allow(addr, netmask, acl, !default_deny); } diff --git a/acl.h b/acl.h index 60185937..e6090a88 100644 --- a/acl.h +++ b/acl.h @@ -9,7 +9,5 @@ void acl_init(struct list_head *acl, char * const *acl_info, int num); char *acl_get_contents(struct list_head *acl); int acl_check_access(int fd, struct list_head *acl, int default_deny); -void acl_allow(struct in_addr addr, int netmask, - struct list_head *acl, int default_deny); -void acl_deny(struct in_addr addr, int netmask, - struct list_head *acl, int default_deny); +void acl_allow(char *addr, int mask, struct list_head *acl, int default_deny); +void acl_deny(char *addr, int mask, struct list_head *acl, int default_deny); diff --git a/command.c b/command.c index 9abe04a3..988eae64 100644 --- a/command.c +++ b/command.c @@ -192,7 +192,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman case SENDER_ALLOW: if (argc != 4 && argc != 5) return -E_COMMAND_SYNTAX; - if (!inet_pton(AF_INET, argv[3], &scd->addr)) + if (!is_valid_ipv4_address(argv[3])) return -E_COMMAND_SYNTAX; scd->netmask = 32; if (argc == 5) { @@ -200,6 +200,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman if (scd->netmask < 0 || scd->netmask > 32) return -E_COMMAND_SYNTAX; } + strncpy(scd->host, argv[3], sizeof(scd->host)); break; case SENDER_ADD: case SENDER_DELETE: @@ -254,7 +255,7 @@ int com_sender(int fd, int argc, char * const * argv) usleep(100 * 1000); continue; } - mmd->sender_cmd_data = scd; + memcpy(&mmd->sender_cmd_data, &scd, sizeof(scd)); mutex_unlock(mmd_mutex); break; } diff --git a/configure.ac b/configure.ac index afb993af..13ec59ba 100644 --- a/configure.ac +++ b/configure.ac @@ -267,6 +267,20 @@ fi CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" + +########################################################################### ip_mreqn +AC_MSG_CHECKING(for struct ip_mreqn (UDPv4 multicast)) +AC_TRY_LINK([ + #include + #include +],[ + struct ip_mreqn mn; + mn.imr_ifindex = 0; +],[have_ip_mreqn=yes],[have_ip_mreqn=no]) +AC_MSG_RESULT($have_ip_mreqn) +if test ${have_ip_mreqn} = yes; then + AC_DEFINE(HAVE_IP_MREQN, 1, define to 1 you have struct ip_mreqn) +fi ########################################################################### osx AC_MSG_CHECKING(for CoreAudio (MacOs)) diff --git a/ggo/config_file.m4 b/ggo/config_file.m4 index 9e2ca407..318ba7a6 100644 --- a/ggo/config_file.m4 +++ b/ggo/config_file.m4 @@ -1,7 +1,7 @@ option "config_file" c #~~~~~~~~~~~~~~~~~~~~~ -"(default='DEFAULT_CONFIG_FILE'" +"(default='DEFAULT_CONFIG_FILE')" string typestr="filename" optional details=" diff --git a/ggo/loglevel.m4 b/ggo/loglevel.m4 index 0badbf4c..162d030b 100644 --- a/ggo/loglevel.m4 +++ b/ggo/loglevel.m4 @@ -11,7 +11,7 @@ details=" value. debug: Produces really noisy output. - info: Still noisy, but won't fill up the disk quicky. + info: Still noisy, but won't fill up the disk quickly. notice: Indicates normal, but significant event. warning: Unexpected events that can be handled. error: Unhandled error condition. diff --git a/ggo/server.m4 b/ggo/server.m4 index 30c3b41a..8aa753b9 100644 --- a/ggo/server.m4 +++ b/ggo/server.m4 @@ -271,6 +271,12 @@ int typestr="port" default="8000" optional +option "udp_mcast_iface" - +#~~~~~~~~~~~~~~~~~~~~~~~~~~ +"outgoing udp multicast interface" +string +optional + option "udp_header_interval" H #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "duration for sending header" @@ -292,11 +298,19 @@ option "udp_ttl" t #~~~~~~~~~~~~~~~~~ "set time to live value" int typestr="num" -default="10" +default="-1" optional details=" - This option instructs the udp sender to set the time to live to - \"num\" for the sending udp socket. Only useful for multicast - udp streaming. + This option applies exclusively to multicast UDPv4/v6 streaming. + + For the sending UDPv4 socket it sets the multicast Time-To-Live + value to \"num\". Traditional TTL scope values are: 0=host, + 1=network, 32=same site, 64=same region, 128=same continent, + 255=unrestricted. Please note however that this scoping is not + a good solution: RFC 2365 e.g. presents a better alternative. + + When using UDPv6 multicasting, the option sets the number of + multicast hops (as described in RFC 3493); a value of -1 + allows the kernel to auto-select the hop value. " diff --git a/ggo/udp_recv.ggo b/ggo/udp_recv.ggo index 6ad09f47..f19d3194 100644 --- a/ggo/udp_recv.ggo +++ b/ggo/udp_recv.ggo @@ -11,3 +11,7 @@ option "port" p "udp port" int typestr="portnumber" default="8000" optional + +option "iface" I "receiving udp multicast interface" +string +optional diff --git a/net.c b/net.c index 312a6854..b510dc18 100644 --- a/net.c +++ b/net.c @@ -755,142 +755,3 @@ out: free(buf); return ret; } - -static int resolve(const char *hostname, unsigned short port, - struct sockaddr_in *addr) -{ - struct hostent *host; - - assert(hostname); - host = gethostbyname(hostname); - if (!host) - return -ERRNO_TO_PARA_ERROR(h_errno); - if (addr) { - memcpy(&addr->sin_addr, host->h_addr_list[0], host->h_length); - addr->sin_port = port; - } - return 1; -} - -/* - * Create an UDP socket. - * - * If the given address is a multicast adress, the socket will be set - * to use the multicast TTL ttl and sets the datagrams to loop back. - * - * \return The fd of the socket on success, negative on errors. - */ -static int create_udp_socket(struct sockaddr_in *addr, - unsigned short port, unsigned char ttl) -{ - int ret, fd, yes = 1; - - assert(addr); - ret = socket(PF_INET, SOCK_DGRAM, 0); - if (ret < 0) - return -ERRNO_TO_PARA_ERROR(errno); - fd = ret; - /* reuse addresses */ - ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - addr->sin_family = AF_INET; - addr->sin_port = htons(port); - /* set the TTL and turn on multicast loop */ - if (IN_MULTICAST(htonl(addr->sin_addr.s_addr))) { - unsigned char loop = 1; - ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, - sizeof(ttl)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, - sizeof(loop)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - } - return fd; -err: - close(fd); - return ret; -} - -/** - * Create and connect a sending UDP socket. - * - * \param hostname Where to send to (name or IPv4 address). - * \param port The udp port to use. - * \param ttl Time to live (only relevant for multicast). - * - * \return The fd of the socket on success, negative on error. - */ -int create_udp_send_socket(char *hostname, unsigned short port, - unsigned char ttl) -{ - struct sockaddr_in addr; - int fd, ret = resolve(hostname, port, &addr); - - if (ret < 0) - return ret; - ret = create_udp_socket(&addr, port, ttl); - if (ret < 0) - return ret; - fd = ret; - ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret >= 0) - return fd; - ret = -ERRNO_TO_PARA_ERROR(errno); - close(fd); - return ret; -} - -/** - * Create and bind a receiving UDP socket. - * - * Bind the created UDP socket to \a hostname, and add multicast membership if - * hostname is a multicast hostname. - * - * \param hostname Name or IPv4 address to receive from. - * \param port The udp port. - * - * \return The fd of the socket on success, negative on errors. - */ -int create_udp_recv_socket(char *hostname, unsigned short port) -{ - struct sockaddr_in addr; - int fd, ret = resolve(hostname, port, &addr); - - if (ret < 0) - memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); - ret = create_udp_socket(&addr, port, 1); - if (ret < 0) - return ret; - fd = ret; - ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - /* Add multicast membership */ - if (IN_MULTICAST(htonl(addr.sin_addr.s_addr))) { - struct ip_mreq mreq; - - mreq.imr_multiaddr.s_addr = addr.sin_addr.s_addr; - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)); - if (ret < 0) { - ret = -ERRNO_TO_PARA_ERROR(errno); - goto err; - } - } - return fd; -err: - close(fd); - return ret; -} diff --git a/net.h b/net.h index 7cfee2eb..8ec9fa52 100644 --- a/net.h +++ b/net.h @@ -3,6 +3,7 @@ * * Licensed under the GPL v2. For licencing details see COPYING. */ +#include /** \file net.h exported symbols from net.c */ @@ -24,6 +25,20 @@ #endif /** \endcond */ +/** + * Ensure that string conforms to the IPv4 address format. + * + * \param address The address string to check. + * + * \return 1 if \a address conforms to the IPv4 address format, else 0. + */ +_static_inline_ bool is_valid_ipv4_address(const char *address) +{ + struct in_addr test_it; + + return inet_pton(AF_INET, address, &test_it) != 0; +} + /** * Generic socket creation (passive and active sockets). */ @@ -61,6 +76,3 @@ int recv_pattern(int fd, const char *pattern, size_t bufsize); void enable_crypt(int fd, crypt_function *recv_f, crypt_function *send_f, void *private_data); void disable_crypt(int fd); -int create_udp_recv_socket(char *hostname, unsigned short port); -int create_udp_send_socket(char *hostname, unsigned short port, - unsigned char ttl); diff --git a/send_common.c b/send_common.c index d1dcaeb1..f570273f 100644 --- a/send_common.c +++ b/send_common.c @@ -259,7 +259,7 @@ char *get_sender_info(struct sender_status *ss, char *name) void generic_com_allow(struct sender_command_data *scd, struct sender_status *ss) { - acl_allow(scd->addr, scd->netmask, &ss->acl, ss->default_deny); + acl_allow(scd->host, scd->netmask, &ss->acl, ss->default_deny); } /** @@ -273,7 +273,7 @@ void generic_com_allow(struct sender_command_data *scd, void generic_com_deny(struct sender_command_data *scd, struct sender_status *ss) { - acl_deny(scd->addr, scd->netmask, &ss->acl, ss->default_deny); + acl_deny(scd->host, scd->netmask, &ss->acl, ss->default_deny); } /** diff --git a/server.h b/server.h index b133d82e..8d1d6752 100644 --- a/server.h +++ b/server.h @@ -36,6 +36,8 @@ struct sender_command_data{ int sender_num; /** Used for the allow/deny/add/remove subcommands. */ struct in_addr addr; + /** Used for the allow/deny/add/remove subcommands. */ + char host[256]; /** Used for allow/deny. */ int netmask; /** The port number for add/remove. */ diff --git a/udp_header.h b/udp_header.h index 83157e6b..7e94b584 100644 --- a/udp_header.h +++ b/udp_header.h @@ -5,6 +5,7 @@ */ /** \file udp_header.h some macros used by udp_send.c and udp_recv.c. */ +#include /** * Number of bytes of the paraslash udp header. diff --git a/udp_recv.c b/udp_recv.c index bdc25411..9ea35d8d 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -217,19 +217,89 @@ static void *udp_recv_parse_config(int argc, char **argv) return NULL; } +/* + * Perform AF-independent joining of multicast receive addresses. + * + * \param fd Bound socket descriptor. + * \param iface The receiving multicast interface, or NULL for the default. + * + * \return Zero if okay, negative on error. + */ +static int mcast_receiver_setup(int fd, const char *iface) +{ + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int id = iface == NULL ? 0 : if_nametoindex(iface); + + if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) + goto err; + + if (iface != NULL && id == 0) + PARA_WARNING_LOG("could not resolve interface %s, using default", iface); + + switch (ss.ss_family) { + case AF_INET: + if (IN_MULTICAST(htonl(((struct sockaddr_in *)&ss)->sin_addr.s_addr))) { +#ifdef HAVE_IP_MREQN + struct ip_mreqn m4; + + m4.imr_address.s_addr = INADDR_ANY; + m4.imr_ifindex = id; +#else + struct ip_mreq m4; + + m4.imr_interface.s_addr = INADDR_ANY; + if (id != 0) + PARA_ERROR_LOG("Setting IPv4 receiver mcast interface not supported."); + +#endif + m4.imr_multiaddr = ((struct sockaddr_in *)&ss)->sin_addr; + + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &m4, sizeof(m4)) < 0) + break; + } + return 0; + case AF_INET6: + if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)&ss)->sin6_addr)) { + struct ipv6_mreq m6; + + memset(&m6, 0, sizeof(m6)); + memcpy(&m6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)&ss)->sin6_addr, 16); + m6.ipv6mr_interface = id; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6, sizeof(m6)) < 0) + break; + } + return 0; + default: + PARA_ERROR_LOG("address family %d not supported", ss.ss_family); + return -E_ADDRESS_LOOKUP; + } +err: + return -ERRNO_TO_PARA_ERROR(errno); +} + static int udp_recv_open(struct receiver_node *rn) { struct private_udp_recv_data *purd; struct udp_recv_args_info *c = rn->conf; + char *iface = c->iface_given ? c->iface_arg : NULL; int ret; rn->buf = para_calloc(UDP_RECV_CHUNK_SIZE); rn->private_data = para_calloc(sizeof(struct private_udp_recv_data)); purd = rn->private_data; - ret = create_udp_recv_socket(c->host_arg, c->port_arg); + + ret = makesock(AF_UNSPEC, IPPROTO_UDP, 1, c->host_arg, c->port_arg); if (ret < 0) goto err; purd->fd = ret; + + ret = mcast_receiver_setup(purd->fd, iface); + if (ret < 0) { + close(purd->fd); + return ret; + } + ret = mark_fd_nonblocking(purd->fd); if (ret < 0) goto err; diff --git a/udp_send.c b/udp_send.c index 3804c39c..140458e7 100644 --- a/udp_send.c +++ b/udp_send.c @@ -68,20 +68,107 @@ static void udp_delete_target(struct udp_target *ut, const char *msg) free(ut); } +/** + * Perform AF-independent multicast sender setup. + * + * \param fd The connected socket descriptor. + * \param ttl UDPv4 multicast TTL or UDPv6 multicast number of hops. + * Use -1 to mean default, 0..255 otherwise. + * \param iface The outgoing multicast interface, or NULL for the default. + * + * \return Zero if okay, negative on error. + */ +static int mcast_sender_setup(struct udp_target *ut, int ttl, char *iface) +{ + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + + const int on = 1; + int id = iface == NULL ? 0 : if_nametoindex(iface); + + if (getpeername(ut->fd, (struct sockaddr *)&ss, &sslen) < 0) + goto err; + + if (iface != NULL && id == 0) + PARA_WARNING_LOG("could not resolve interface %s, using default", iface); + + /* RFC 3493, 5.2: -1 means 'use kernel default' */ + if (ttl < 0 || ttl > 255) + ttl = -1; + + switch (ss.ss_family) { + case AF_INET: + if (!IN_MULTICAST(htonl(((struct sockaddr_in *)&ss)->sin_addr.s_addr))) + return 0; + if (id != 0) { +#ifdef HAVE_IP_MREQN + struct ip_mreqn mn; + + memset(&mn, 0, sizeof(mn)); + mn.imr_ifindex = id; + if (setsockopt(ut->fd, IPPROTO_IP, IP_MULTICAST_IF, &mn, sizeof(mn)) < 0) + goto err; +#else + PARA_ERROR_LOG("No support for setting outgoing IPv4 mcast interface."); +#endif + } + /* + * Enable receiving multicast messages generated on the local host + * At least on Linux, this is enabled by default. + */ + if (setsockopt(ut->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on)) < 0) + break; + + /* Default: use local subnet (do not flood out into the WAN) */ + if (ttl == -1) + ttl = 1; + if (setsockopt(ut->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + break; + return 0; + case AF_INET6: + if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)&ss)->sin6_addr)) + return 0; + if (id != 0 && + setsockopt(ut->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &id, sizeof(id)) < 0) + break; + if (setsockopt(ut->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)) < 0) + break; + if (setsockopt(ut->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) + break; + return 0; + default: + PARA_ERROR_LOG("address family %d not supported", ss.ss_family); + return -E_ADDRESS_LOOKUP; + } +err: + return -ERRNO_TO_PARA_ERROR(errno); +} + /** The maximal size of the per-target chunk queue. */ #define UDP_CQ_BYTES 40000 static int udp_init_session(struct udp_target *ut) { int ret; + char *iface = NULL; if (ut->fd >= 0) /* nothing to do */ return 0; - ret = create_udp_send_socket(TARGET_ADDR(ut), ut->port, - conf.udp_ttl_arg); + + ret = makesock(AF_UNSPEC, IPPROTO_UDP, 0, TARGET_ADDR(ut), ut->port); if (ret < 0) return ret; ut->fd = ret; + + if (conf.udp_mcast_iface_given) + iface = conf.udp_mcast_iface_arg; + + ret = mcast_sender_setup(ut, conf.udp_ttl_arg, iface); + if (ret < 0) { + close(ut->fd); + return ret; + } + ret = mark_fd_nonblocking(ut->fd); if (ret < 0) { close(ut->fd);