How to enable ss(another utility to investigate sockets) to see the joined multicast group IP address on a Linux host?
I have access to an internal tool which can be used to join a multicast IP:PORT and capture packets as a pcap file but the tool doesn't come with implementation.
When I run the tool, both netstat
and ss
can be used to see the joined multicast IP address.
For example:
$ netstat -gn|grep 224.0.115.66
sfc2 1 224.0.115.66
$ ss -uan|grep 224.0.115.66
UNCONN 0 0 224.0.115.66:6000 0.0.0.0:*
I have written a simple c code that can be used to join a multicast group. The issue I have is that when I run my application, only netstat can find the joined multicast group but ss.
$ netstat -gn|grep 224.0.115.66
sfc2 1 224.0.115.66
$ ss -uan|grep 224.0.115.66
Question> I assume there must be some extra configurations I have to do in order to enable ss
to see the joined multicast group IP address. Can someone tell me whether there is any special handling I have to incorporate into the code so that ss
can detect the joined multicast group IP:PORT similar as what netstat
can do?
Thank you
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
int main() {
struct sockaddr_in multicast_addr;
struct ip_mreq mreq;
int sockfd;
// Create a UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
// Set up the multicast address structure
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(INADDR_ANY);
multicast_addr.sin_port = htons(6000);
// Bind the socket to the multicast address
if (bind(sockfd, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)) < 0) {
perror("bind");
exit(1);
}
// Join the multicast group
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.0.115.66");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt");
exit(1);
}
printf("Joined multicast group successfully\n");
// You can now receive data from the multicast group using this socket
// do sth thing here so that the application doesn't immediately exit!!!
// Close the socket when done
close(sockfd);
return 0;
}
Answers
The code you've written looks correct for joining a multicast group on a specific interface. However, there might be some issues related to the socket options or the way the socket is bound.
When joining a multicast group, it's crucial to specify the interface through which you want to receive multicast traffic. In your code, you set mreq.imr_interface.s_addr
to htonl(INADDR_ANY)
, which means the kernel should choose the interface. This might work, but it's better to explicitly specify the interface you want to use, especially if you have multiple interfaces.
Additionally, you're binding the socket to INADDR_ANY
, which means it's bound to all network interfaces. This is usually fine for multicast, but if you're troubleshooting, it might be worth explicitly binding the socket to a specific interface.
Here's an updated version of your code with these considerations:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
int main() {
struct sockaddr_in multicast_addr;
struct ip_mreq mreq;
int sockfd;
// Create a UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
// Set up the multicast address structure
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = htonl(INADDR_ANY);
multicast_addr.sin_port = htons(6000);
// Bind the socket to a specific interface
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(1);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET) {
if (strcmp(ifa->ifa_name, "your_interface_name") == 0) {
multicast_addr.sin_addr.s_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
break;
}
}
}
freeifaddrs(ifaddr);
if (bind(sockfd, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)) < 0) {
perror("bind");
exit(1);
}
// Join the multicast group
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("224.0.115.66");
// Specify the interface to use for receiving multicast traffic
mreq.imr_interface.s_addr = multicast_addr.sin_addr.s_addr;
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt");
exit(1);
}
printf("Joined multicast group successfully\n");
// Keep the application running to continue receiving multicast traffic
while (1) {
// Do something to prevent the application from exiting immediately
}
// Close the socket when done (will never be reached in this example)
close(sockfd);
return 0;
}
Replace "your_interface_name"
with the name of the interface you want to use for receiving multicast traffic. Additionally, the while(1)
loop at the end will keep the application running indefinitely to continue receiving multicast traffic. You may replace it with your actual application logic.