Subject: IPv4 multicast set/getsockopt, specifying interface index
To: None <tech-net@netbsd.org>
From: None <itojun@iijlab.net>
List: tech-net
Date: 01/13/2001 12:53:17
	IPv4 multicast set/getsockopt uses interface address to identify
	the interface.  this causes problem when you have the same IPv4
	address on multiple interfaces (example: "unnumbered" p2p interface).
	we need a way to specify interface by interface index instead.
	also, if interface address changes after setsockopt(a, IP_MULTICAST_IF),
	getsockopt(IP_MULTICAST_IF) will return the new address and it will
	be harder for user program to guess which interface it is.

	the following patch uses 0.0.0.0/8 as 24bit interface index value.
	on getsockopt(IP_MULTICAST_IF) it will return the value the user has
	set.  does it look right, or messy?
	(Dave Thaler suggested this based on RFC1724 section 3.3 - RIPv2
	MIB, and he said there's other system doing/will be doing this)

itojun


Index: ip_output.c
===================================================================
RCS file: /cvsroot/kame/kame/netbsd/sys/netinet/ip_output.c,v
retrieving revision 1.17
diff -u -r1.17 ip_output.c
--- ip_output.c	2000/12/02 07:30:45	1.17
+++ ip_output.c	2001/01/13 03:19:12
@@ -145,6 +145,7 @@
 #endif /*IPSEC*/
 
 static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));
+static struct ifnet *ip_multicast_if __P((struct in_addr *));
 static void ip_mloopback
 	__P((struct ifnet *, struct mbuf *, struct sockaddr_in *));
 
@@ -1194,6 +1195,26 @@
 }
 
 /*
+ * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
+ */
+static struct ifnet *
+ip_multicast_if(a)
+	struct in_addr *a;
+{
+	int ifindex;
+	struct ifnet *ifp;
+
+	if (ntohl(a->s_addr) >> 24 == 0) {
+		ifindex = ntohl(a->s_addr) & 0xffffff;
+		if (ifindex < 0 || if_index < ifindex)
+			return NULL;
+		ifp = ifindex2ifnet[ifindex];
+	} else
+		INADDR_TO_IFP(*a, ifp);
+	return ifp;
+}
+
+/*
  * Set the IP multicast options in response to user setsockopt().
  */
 int
@@ -1224,6 +1245,7 @@
 			return (ENOBUFS);
 		*imop = imo;
 		imo->imo_multicast_ifp = NULL;
+		imo->imo_multicast_addr.s_addr = INADDR_ANY;
 		imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
 		imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
 		imo->imo_num_memberships = 0;
@@ -1254,12 +1276,13 @@
 		 * IP address.  Find the interface and confirm that
 		 * it supports multicasting.
 		 */
-		INADDR_TO_IFP(addr, ifp);
+		ifp = ip_multicast_if(&addr);
 		if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
 			error = EADDRNOTAVAIL;
 			break;
 		}
 		imo->imo_multicast_ifp = ifp;
+		imo->imo_multicast_addr = addr;
 		break;
 
 	case IP_MULTICAST_TTL:
@@ -1319,7 +1342,7 @@
 			ifp = ro.ro_rt->rt_ifp;
 			rtfree(ro.ro_rt);
 		} else {
-			INADDR_TO_IFP(mreq->imr_interface, ifp);
+			ifp = ip_multicast_if(&mreq->imr_interface);
 		}
 		/*
 		 * See if we found an interface, and confirm that it
@@ -1380,7 +1403,7 @@
 		if (in_nullhost(mreq->imr_interface))
 			ifp = NULL;
 		else {
-			INADDR_TO_IFP(mreq->imr_interface, ifp);
+			ifp = ip_multicast_if(&mreq->imr_interface);
 			if (ifp == NULL) {
 				error = EADDRNOTAVAIL;
 				break;
@@ -1444,7 +1467,6 @@
 	u_char *ttl;
 	u_char *loop;
 	struct in_addr *addr;
-	struct in_ifaddr *ia;
 
 	*mp = m_get(M_WAIT, MT_SOOPTS);
 
@@ -1456,8 +1478,8 @@
 		if (imo == NULL || imo->imo_multicast_ifp == NULL)
 			*addr = zeroin_addr;
 		else {
-			IFP_TO_IA(imo->imo_multicast_ifp, ia);
-			*addr = ia ? ia->ia_addr.sin_addr : zeroin_addr;
+			/* return the value user has set */
+			*addr = imo->imo_multicast_addr;
 		}
 		return (0);