Skip to content

Commit

Permalink
attempt to keep local multicast robust against network adapter going …
Browse files Browse the repository at this point in the history
…downg
  • Loading branch information
ka9q committed Jan 21, 2025
1 parent 3d34c36 commit 196fbb8
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Makefile.linux
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
Expand Down
23 changes: 20 additions & 3 deletions control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
20 changes: 18 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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);
}
Expand Down
34 changes: 21 additions & 13 deletions multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions multicast.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion radio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
1 change: 1 addition & 0 deletions radio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion radio_status.c
Original file line number Diff line number Diff line change
Expand Up @@ -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){
Expand Down

0 comments on commit 196fbb8

Please sign in to comment.