From 23205f9cdd229343a83d8c5a6317d9187e90c3d4 Mon Sep 17 00:00:00 2001 Message-Id: <23205f9cdd229343a83d8c5a6317d9187e90c3d4.1716352331.git.mdw@distorted.org.uk> From: Mark Wooding Date: Tue, 25 Sep 2007 22:42:24 +0100 Subject: [PATCH] totally untested multicast support Organization: Straylight/Edgeware From: Richard Kettlewell --- clients/playrtp.c | 30 +++++++++++++++++++++++++++++- doc/disorder-playrtp.1.in | 3 +++ doc/disorder_config.5.in | 5 +++++ lib/configuration.c | 2 ++ lib/configuration.h | 10 ++++++++-- server/speaker-network.c | 33 +++++++++++++++++++++++++++++++-- 6 files changed, 78 insertions(+), 5 deletions(-) diff --git a/clients/playrtp.c b/clients/playrtp.c index d5668b7..8931b6e 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -291,6 +291,7 @@ static const struct option options[] = { { "max", required_argument, 0, 'x' }, { "buffer", required_argument, 0, 'b' }, { "rcvbuf", required_argument, 0, 'R' }, + { "multicast", required_argument, 0, 'M' }, { 0, 0, 0, 0 } }; @@ -858,6 +859,7 @@ static void help(void) { " --buffer, -b FRAMES Buffer high water mark\n" " --max, -x FRAMES Buffer maximum size\n" " --rcvbuf, -R BYTES Socket receive buffer size\n" + " --multicast, -M GROUP Join multicast group\n" " --help, -h Display usage message\n" " --version, -V Display version number\n" ); @@ -879,6 +881,9 @@ int main(int argc, char **argv) { char *sockname; int rcvbuf, target_rcvbuf = 131072; socklen_t len; + char *multicast_group = 0; + struct ip_mreq mreq; + struct ipv6_mreq mreq6; static const struct addrinfo prefs = { AI_PASSIVE, @@ -893,7 +898,7 @@ int main(int argc, char **argv) { mem_init(); if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale"); - while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:", options, 0)) >= 0) { + while((n = getopt_long(argc, argv, "hVdD:m:b:x:L:R:M:", options, 0)) >= 0) { switch(n) { case 'h': help(); case 'V': version(); @@ -904,6 +909,7 @@ int main(int argc, char **argv) { case 'x': maxbuffer = 2 * atol(optarg); break; case 'L': logfp = fopen(optarg, "w"); break; case 'R': target_rcvbuf = atoi(optarg); break; + case 'M': multicast_group = optarg; break; default: fatal(0, "invalid option"); } } @@ -924,6 +930,28 @@ int main(int argc, char **argv) { fatal(errno, "error creating socket"); if(bind(rtpfd, res->ai_addr, res->ai_addrlen) < 0) fatal(errno, "error binding socket to %s", sockname); + if(multicast_group) { + if((n = getaddrinfo(multicast_group, 0, &prefs, &res))) + fatal(0, "getaddrinfo %s: %s", multicast_group, gai_strerror(n)); + switch(res->ai_family) { + case PF_INET: + mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; + mreq.imr_interface.s_addr = 0; /* use primary interface */ + if(setsockopt(rtpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof mreq) < 0) + fatal(errno, "error calling setsockopt IP_ADD_MEMBERSHIP"); + break; + case PF_INET6: + mreq6.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + memset(&mreq6.ipv6mr_interface, 0, sizeof mreq6.ipv6mr_interface); + if(setsockopt(rtpfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq6, sizeof mreq6) < 0) + fatal(errno, "error calling setsockopt IPV6_JOIN_GROUP"); + break; + default: + fatal(0, "unsupported address family %d", res->ai_family); + } + } len = sizeof rcvbuf; if(getsockopt(rtpfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) < 0) fatal(errno, "error calling getsockopt SO_RCVBUF"); diff --git a/doc/disorder-playrtp.1.in b/doc/disorder-playrtp.1.in index 18ca149..7ff59b9 100644 --- a/doc/disorder-playrtp.1.in +++ b/doc/disorder-playrtp.1.in @@ -50,6 +50,9 @@ is four times the \fB--buffer\fR value. .B --rcvbuf \fIBYTES\fR, \fB-R \fIBYTES\fR Specifies socket receive buffer size. .TP +.B --multicast \fIGROUP\fR, \fB-M \fIGROUP\fR +Specifies a multicast group to join. +.TP .B --help\fR, \fB-h Display a usage message. .TP diff --git a/doc/disorder_config.5.in b/doc/disorder_config.5.in index cc8bd39..0cbd76a 100644 --- a/doc/disorder_config.5.in +++ b/doc/disorder_config.5.in @@ -212,6 +212,11 @@ Determines whether the server locks against concurrent operation. Default is The path to the mixer device, if you want access to the volume control, e.g. \fB/dev/mixer\fR. .TP +.B multicast_ttl \fIHOPS\fR +Set the maximum number of hops to send multicast packets. This only applies is +\fBspeaker_backend\fR is set to \fBnetwork\fR and \fBbroadcast\fR is actually a +multicast address. +.TP .B namepart \fIPART\fR \fIREGEXP\fR \fISUBST\fR [\fICONTEXT\fR [\fIREFLAGS\fR]] Determines how to extract trackname part \fIPART\fR from a track name (with the collection root part removed). diff --git a/lib/configuration.c b/lib/configuration.c index a125bc7..0627514 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -835,6 +835,7 @@ static const struct conf conf[] = { { C(listen), &type_stringlist, validate_addrport }, { C(lock), &type_boolean, validate_any }, { C(mixer), &type_string, validate_ischr }, + { C(multicast_ttl), &type_integer, validate_non_negative }, { C(namepart), &type_namepart, validate_any }, { C2(nice, nice_rescan), &type_integer, validate_non_negative }, { C(nice_rescan), &type_integer, validate_non_negative }, @@ -973,6 +974,7 @@ static struct config *config_default(void) { c->sample_format.byte_format = AO_FMT_NATIVE; c->queue_pad = 10; c->speaker_backend = -1; + c->multicast_ttl = 1; return c; } diff --git a/lib/configuration.h b/lib/configuration.h index 2975557..eabbcce 100644 --- a/lib/configuration.h +++ b/lib/configuration.h @@ -220,8 +220,14 @@ struct config { const char *device; struct transformlist transform; /* path name transformations */ - struct stringlist broadcast; /* audio broadcast address */ - struct stringlist broadcast_from; /* audio broadcast source address */ + /** @brief Address to send audio data to */ + struct stringlist broadcast; + + /** @brief Source address for network audio transmission */ + struct stringlist broadcast_from; + + /** @brief TTL for multicast packets */ + long multicast_ttl; /* derived values: */ int nparts; /* number of distinct name parts */ diff --git a/server/speaker-network.c b/server/speaker-network.c index fab81a8..affc1a1 100644 --- a/server/speaker-network.c +++ b/server/speaker-network.c @@ -119,8 +119,37 @@ static void network_init(void) { res->ai_socktype, res->ai_protocol)) < 0) fatal(errno, "error creating broadcast socket"); - if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0) - fatal(errno, "error setting SO_BROADCAST on broadcast socket"); + if((res->ai_family == PF_INET + && IN_MULTICAST( + ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr) + )) + || (res->ai_family == PF_INET6 + && IN6_IS_ADDR_MULTICAST( + &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr + ))) { + /* Multicasting */ + switch(res->ai_family) { + case PF_INET: { + const int mttl = config->multicast_ttl; + if(setsockopt(bfd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, sizeof mttl) < 0) + fatal(errno, "error setting IP_MULTICAST_TTL on multicast socket"); + break; + } + case PF_INET6: { + const int mttl = config->multicast_ttl; + if(setsockopt(bfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &mttl, sizeof mttl) < 0) + fatal(errno, "error setting IPV6_MULTICAST_HOPS on multicast socket"); + break; + } + default: + fatal(0, "unsupported address family %d", res->ai_family); + } + } else { + /* Presumably just broadcasting */ + if(setsockopt(bfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0) + fatal(errno, "error setting SO_BROADCAST on broadcast socket"); + } len = sizeof sndbuf; if(getsockopt(bfd, SOL_SOCKET, SO_SNDBUF, &sndbuf, &len) < 0) -- [mdw]