From 65af96e0907ba9367aab9c1534b11c7f674c1e6a Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Tue, 19 Apr 2022 13:29:07 +0300
Subject: [PATCH 2/6] mreceive: join IGMP group by interface

mreceive uses the old-style struct ip_mreq for IP_ADD_MEMBERSHIP, which
takes the source address of the interface wishing to join.

Since the IPV6_ADD_MEMBERSHIP variant only takes a struct ipv6_mreq
which contains the ifindex and not the source address, we need to add
support for that.

In preparation for IPv6 support, add logic to join an IGMP group either
by source address or by interface name, whichever is specified.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 mreceive.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 46 insertions(+), 5 deletions(-)

--- a/mreceive.c
+++ b/mreceive.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <net/if.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/time.h>
@@ -61,7 +62,7 @@ Usage: mreceive [-g GROUP] [-p PORT] [-i
   -h           Print the command usage.\n\n", VERSION);
 }
 
-static void igmp_join(int s, in_addr_t multiaddr, in_addr_t interface)
+static void igmp_join_by_saddr(int s, in_addr_t multiaddr, in_addr_t interface)
 {
 	struct ip_mreq mreq;
 	int ret;
@@ -77,10 +78,34 @@ static void igmp_join(int s, in_addr_t m
 	}
 }
 
+static void igmp_join_by_if_name(int s, in_addr_t multicast,
+				 const char *if_name)
+{
+	struct ip_mreqn mreq = {};
+	int if_index;
+	int ret;
+
+	if_index = if_nametoindex(if_name);
+	if (!if_index) {
+		perror("if_nametoindex");
+		exit(1);
+	}
+
+	mreq.imr_multiaddr.s_addr = multicast;
+	mreq.imr_ifindex = if_index;
+
+	ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+	if (ret) {
+		perror("setsockopt() IP_ADD_MEMBERSHIP");
+		exit(1);
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	struct sockaddr_in stLocal, stFrom;
 	unsigned char achIn[BUFSIZE];
+	const char *if_name;
 	int s, i;
 	int iTmp, iRet;
 	int ipnum = 0;
@@ -131,6 +156,17 @@ int main(int argc, char *argv[])
 				ii++;
 				ipnum++;
 			}
+		} else if (strcmp(argv[ii], "-I") == 0) {
+			ii++;
+			if (ii < argc) {
+				if (if_name) {
+					printf("Single interface expected\n");
+					exit(1);
+				}
+
+				if_name = argv[ii];
+				ii++;
+			}
 		} else if (strcmp(argv[ii], "-n") == 0) {
 			ii++;
 			NUM = 1;
@@ -167,11 +203,16 @@ int main(int argc, char *argv[])
 	}
 
 	/* join the multicast group. */
-	if (!ipnum) {		/* single interface */
-		igmp_join(s, inet_addr(TEST_ADDR), INADDR_ANY);
+	if (if_name) {
+		igmp_join_by_if_name(s, inet_addr(TEST_ADDR), if_name);
 	} else {
-		for (i = 0; i < ipnum; i++) {
-			igmp_join(s, inet_addr(TEST_ADDR), IP[i]);
+		if (!ipnum) {		/* single interface */
+			igmp_join_by_saddr(s, inet_addr(TEST_ADDR), INADDR_ANY);
+		} else {
+			for (i = 0; i < ipnum; i++) {
+				igmp_join_by_saddr(s, inet_addr(TEST_ADDR),
+						   IP[i]);
+			}
 		}
 	}
 
