diff --git a/Makefile.linux b/Makefile.linux index cf2f5e8..8b61a6f 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -76,7 +76,7 @@ all: $(DAEMONS) $(EXECS) $(DYNAMICS) install: $(DAEMONS) $(EXECS) /usr/sbin/adduser --quiet --system --group radio /usr/sbin/adduser --quiet --system --ingroup radio aprsfeed - rsync -a aux/98-sockbuf.conf /etc/sysctl.d + rsync -a aux/98-sockbuf.conf aux/50-multicast.conf /etc/sysctl.d sysctl --quiet -p /etc/sysctl.d/98-sockbuf.conf rsync -a $(DAEMONS) $(DAEMONDIR) rsync -a $(EXECS) start-ka9q-horus.sh $(BINDIR) diff --git a/audio.c b/audio.c index 2fb3187..f4a06e3 100644 --- a/audio.c +++ b/audio.c @@ -304,7 +304,10 @@ int flush_output(struct channel * chan,bool marker,bool complete){ if(chan->output.rp >= chan->output.queue_size) chan->output.rp -= chan->output.queue_size; - int r = sendto(Output_fd,&packet,bytes + (dp - packet),0,(struct sockaddr *)&chan->output.dest_socket,sizeof(chan->output.dest_socket)); + int r = sendto(Output_fd_lo,&packet,bytes + (dp - packet),0,(struct sockaddr *)&chan->output.dest_socket,sizeof(chan->output.dest_socket)); + if(Mcast_ttl > 0) + r = sendto(Output_fd,&packet,bytes + (dp - packet),0,(struct sockaddr *)&chan->output.dest_socket,sizeof(chan->output.dest_socket)); + chan->output.rtp.bytes += bytes; chan->output.rtp.packets++; chan->output.rtp.seq++; diff --git a/control.c b/control.c index d8c692f..b7d4d28 100644 --- a/control.c +++ b/control.c @@ -59,7 +59,7 @@ int Mcast_ttl = DEFAULT_MCAST_TTL; int IP_tos = DEFAULT_IP_TOS; float Blocktime; int Overlap; -int Output_fd,Status_fd; +int Output_fd,Status_fd,Output_fd_lo; const char *App_path; int Verbose; @@ -386,6 +386,14 @@ int main(int argc,char *argv[]){ exit(EX_OSERR); // let systemd restart us } fcntl(Output_fd,F_SETFL,O_NONBLOCK); // Just drop instead of blocking real time + Output_fd_lo = socket(AF_INET,SOCK_DGRAM,0); // Eventually intended for all output with sendto() + if(Output_fd_lo < 0){ + fprintf(stdout,"can't create output socket: %s\n",strerror(errno)); + exit(EX_OSERR); // let systemd restart us + } + setup_loopback(Output_fd_lo); + + if(target == NULL){ // Use avahi browser to find a radiod instance to control @@ -662,10 +670,16 @@ int main(int argc,char *argv[]){ wprintw(Debug_win,"sent command len %d\n",command_len); screen_update_needed = true; // show local change right away #endif - if(sendto(Output_fd, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len){ + if(sendto(Output_fd_lo, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len){ wprintw(Debug_win,"command send error: %s\n",strerror(errno)); screen_update_needed = true; // show local change right away } + if(Mcast_ttl > 0){ + if(sendto(Output_fd, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len){ + wprintw(Debug_win,"command send error: %s\n",strerror(errno)); + screen_update_needed = true; // show local change right away + } + } // This will elicit an answer, defer the next poll next_radio_poll = now + radio_poll_interval + arc4random_uniform(random_interval) - random_interval/2; } @@ -1670,8 +1684,11 @@ static int send_poll(int ssrc){ encode_int(&bp,OUTPUT_SSRC,ssrc); // poll specific SSRC, or request ssrc list with ssrc = 0 encode_eol(&bp); int const command_len = bp - cmdbuffer; - if(sendto(Output_fd, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len) + if(sendto(Output_fd_lo, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len) return -1; + if(Mcast_ttl > 0) + if(sendto(Output_fd, cmdbuffer, command_len, 0, (struct sockaddr *)&Metadata_dest_socket,sizeof(struct sockaddr)) != command_len) + return -1; return 0; } diff --git a/main.c b/main.c index 50aa376..d41bd0b 100644 --- a/main.c +++ b/main.c @@ -110,7 +110,8 @@ static int64_t Starttime; // System clock at timestamp 0, for RTCP static pthread_t Status_thread; struct sockaddr_storage Metadata_dest_socket; // Dest of global metadata static char const *Metadata_dest_string; // DNS name of default multicast group for status/commands -int Output_fd = -1; // Unconnected socket used for all multicast output +int Output_fd = -1; // Unconnected socket used for other hosts +int Output_fd_lo = -1; // Socket for loopback struct channel Template; // If a channel is tuned to 0 Hz and then not polled for this many seconds, destroy it // Must be computed at run time because it depends on the block time @@ -438,12 +439,25 @@ static int loadconfig(char const *file){ Update = config_getint(Configtable,GLOBAL,"update",Update); IP_tos = config_getint(Configtable,GLOBAL,"tos",IP_tos); Mcast_ttl = config_getint(Configtable,GLOBAL,"ttl",Mcast_ttl); - Output_fd = socket(AF_INET,SOCK_DGRAM,0); // Eventually intended for all output with sendto() + Output_fd = socket(AF_INET,SOCK_DGRAM,0); if(Output_fd < 0){ fprintf(stdout,"can't create output socket: %s\n",strerror(errno)); exit(EX_NOHOST); // let systemd restart us } fcntl(Output_fd,F_SETFL,O_NONBLOCK); // Just drop instead of blocking real time + if(Output_fd < 0){ + fprintf(stdout,"can't create output socket: %s\n",strerror(errno)); + exit(EX_NOHOST); // let systemd restart us + } + + Output_fd_lo = socket(AF_INET,SOCK_DGRAM,0); + if(Output_fd_lo < 0){ + fprintf(stdout,"can't create output socket: %s\n",strerror(errno)); + exit(EX_NOHOST); // let systemd restart us + } + + setup_loopback(Output_fd_lo); + // Set up default output stream file descriptor and socket // There can be multiple senders to an output stream, so let avahi suppress the duplicate addresses @@ -960,6 +974,8 @@ static void *rtcp_send(void *arg){ if(sendto(Output_fd,buffer,dp-buffer,0,(struct sockaddr *)&chan->rtcp.dest_socket,sizeof(chan->rtcp.dest_socket)) < 0) chan->output.errors++; + if(sendto(Output_fd_lo,buffer,dp-buffer,0,(struct sockaddr *)&chan->rtcp.dest_socket,sizeof(chan->rtcp.dest_socket)) < 0) + chan->output.errors++; done:; sleep(1); } diff --git a/multicast.c b/multicast.c index 91a5165..f0ffa84 100644 --- a/multicast.c +++ b/multicast.c @@ -779,31 +779,39 @@ static void set_ipv4_options(int const fd,int const mcast_ttl,int const tos){ if(setsockopt(fd,IPPROTO_IP,IP_MULTICAST_TTL,&mcast_ttl,sizeof(mcast_ttl)) != 0) perror("so_ttl failed"); } +#if 0 // Send explicit copies to loopback interface instead, and have everyone listen on both uint8_t const loop = true; if(setsockopt(fd,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop)) != 0) perror("so_loop failed"); +#endif if(tos >= 0){ // Only needed on output if(setsockopt(fd,IPPROTO_IP,IP_TOS,&tos,sizeof(tos)) != 0) perror("so_tos failed"); } - if(mcast_ttl == 0){ - // TTL is zero, use loopback interface - struct ifreq ifr; - memset(&ifr,0,sizeof(ifr)); - // Get loopback interface address. Probably 127.0.0.1 - strncpy(ifr.ifr_name,"lo",sizeof(ifr.ifr_name)); - if(ioctl(fd, SIOCGIFADDR, &ifr) < 0) - perror("set loopback interface failed"); - else { - // Set our interface to it - struct in_addr *interface_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, interface_addr, sizeof(*interface_addr)) < 0) - perror("setsockopt IP_MULTICAST_IF failed"); +} +// Configure separate socket for transmission (only) through loopback interface +// Most of the options aren't relevant here, just select the loopback interface +int setup_loopback(int fd){ + fcntl(fd,F_SETFL,O_NONBLOCK); // Just drop instead of blocking real time + struct ifreq ifr; + memset(&ifr,0,sizeof(ifr)); + + // Instead of hardwiring the loopback name (which can vary) find it in the system's list + struct ifaddrs *ifap = NULL; + getifaddrs(&ifap); + for(struct ifaddrs const *i = ifap; i != NULL; i = i->ifa_next){ + if(i->ifa_addr->sa_family == AF_INET && (i->ifa_flags & IFF_LOOPBACK)){ + struct sockaddr_in const *sin = (struct sockaddr_in *)i->ifa_addr; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sin->sin_addr, sizeof sin->sin_addr) < 0) + perror("setsockopt IP_MULTICAST_IF failed"); + break; } } + return 0; } + // Set options on IPv6 multicast socket static void set_ipv6_options(int const fd,int const mcast_ttl,int const tos){ // Failures here are not fatal diff --git a/multicast.h b/multicast.h index acbedb4..561c2c4 100644 --- a/multicast.h +++ b/multicast.h @@ -139,6 +139,7 @@ int setup_mcast(char const *target,struct sockaddr *,int output,int ttl,int tos, static inline int setup_mcast_in(char const *target,struct sockaddr *sock,int offset,int tries){ return setup_mcast(target,sock,0,0,0,offset,tries); } +int setup_loopback(int); int join_group(int fd,struct sockaddr const * const sock, char const * const iface,int const ttl,int const tos); int connect_mcast(void const *sock,char const *iface,int const ttl,int const tos); int listen_mcast(void const *sock,char const *iface); diff --git a/radio.c b/radio.c index ed3df39..47173e6 100644 --- a/radio.c +++ b/radio.c @@ -474,8 +474,11 @@ void *sap_send(void *p){ wp += len; space -= len; - if(sendto(Output_fd,message,wp - message,0,(struct sockaddr *)&chan->sap.dest_socket,sizeof(chan->sap.dest_socket)) < 0) + if(sendto(Output_fd_lo,message,wp - message,0,(struct sockaddr *)&chan->sap.dest_socket,sizeof(chan->sap.dest_socket)) < 0) chan->output.errors++; + if(Mcast_ttl > 0) + if(sendto(Output_fd,message,wp - message,0,(struct sockaddr *)&chan->sap.dest_socket,sizeof(chan->sap.dest_socket)) < 0) + chan->output.errors++; sleep(5); } } diff --git a/radio.h b/radio.h index 560fbbc..c4f64d9 100644 --- a/radio.h +++ b/radio.h @@ -292,6 +292,7 @@ extern pthread_mutex_t Channel_list_mutex; extern int Channel_idle_timeout; extern int Ctl_fd; // File descriptor for receiving user commands extern int Output_fd; +extern int Output_fd_lo; extern struct sockaddr_storage Metadata_dest_socket; // Socket for main metadata extern int Verbose; extern float Blocktime; // Common to all receiver slices. NB! Milliseconds, not seconds diff --git a/radio_status.c b/radio_status.c index 4c4586d..8d7cc9c 100644 --- a/radio_status.c +++ b/radio_status.c @@ -109,8 +109,11 @@ int send_radio_status(struct sockaddr const *sock,struct frontend const *fronten uint8_t packet[PKTSIZE]; chan->status.packets_out++; int const len = encode_radio_status(frontend,chan,packet,sizeof(packet)); - if(sendto(Output_fd,packet,len,0,sock,sizeof(struct sockaddr)) < 0) + if(sendto(Output_fd_lo,packet,len,0,sock,sizeof(struct sockaddr)) < 0) chan->output.errors++; + if(Mcast_ttl > 0) + if(sendto(Output_fd,packet,len,0,sock,sizeof(struct sockaddr)) < 0) + chan->output.errors++; return 0; } int reset_radio_status(struct channel *chan){