changeset 12823:dec8caad2567

6926514 Multiple multicasts mapping to same ethernet addr cannot be used on gld
author Ravi Chandra Nallan <Ravichandra.Nallan@Sun.COM>
date Tue, 13 Jul 2010 18:17:30 +0530
parents 7a89035df76c
children 3e6822aec2be
files usr/src/uts/common/inet/ip.h usr/src/uts/common/inet/ip/ip_if.c usr/src/uts/common/inet/ip/ip_multi.c usr/src/uts/common/inet/ip_if.h
diffstat 4 files changed, 126 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/inet/ip.h	Tue Jul 13 11:05:20 2010 +0800
+++ b/usr/src/uts/common/inet/ip.h	Tue Jul 13 18:17:30 2010 +0530
@@ -93,6 +93,7 @@
 #define	IP_ABITS		32
 #define	IPV4_ABITS		IP_ABITS
 #define	IPV6_ABITS		128
+#define	IP_MAX_HW_LEN	40
 
 #define	IP_HOST_MASK		(ipaddr_t)0xffffffffU
 
@@ -1272,6 +1273,18 @@
 	ip_stack_t	*irb_ipst;	/* Does not have a netstack_hold */
 } irb_t;
 
+/*
+ * This is the structure used to store the multicast physical addresses
+ * that an interface has joined.
+ * The refcnt keeps track of the number of multicast IP addresses mapping
+ * to a physical multicast address.
+ */
+typedef struct multiphysaddr_s {
+	struct	multiphysaddr_s  *mpa_next;
+	char	mpa_addr[IP_MAX_HW_LEN];
+	int	mpa_refcnt;
+} multiphysaddr_t;
+
 #define	IRB2RT(irb)	(rt_t *)((caddr_t)(irb) - offsetof(rt_t, rt_irb))
 
 /* Forward declarations */
@@ -1803,6 +1816,9 @@
 	uint32_t	ill_mrouter_cnt; /* mrouter allmulti joins */
 	uint32_t	ill_allowed_ips_cnt;
 	in6_addr_t	*ill_allowed_ips;
+
+	/* list of multicast physical addresses joined on this ill */
+	multiphysaddr_t *ill_mphysaddr_list;
 } ill_t;
 
 /*
@@ -1943,6 +1959,7 @@
  * ill_grp (for underlying ill)	ipsq + ill_g_lock	ipsq OR ill_g_lock
  * ill_grp_pending		ill_mcast_serializer	ill_mcast_serializer
  * ill_mrouter_cnt		atomics			atomics
+ * ill_mphysaddr_list	ill_lock		ill_lock
  *
  * NOTE: It's OK to make heuristic decisions on an underlying interface
  *	 by using IS_UNDER_IPMP() or comparing ill_grp's raw pointer value.
--- a/usr/src/uts/common/inet/ip/ip_if.c	Tue Jul 13 11:05:20 2010 +0800
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Tue Jul 13 18:17:30 2010 +0530
@@ -580,6 +580,17 @@
 		ill->ill_grp = NULL;
 	}
 
+	if (ill->ill_mphysaddr_list != NULL) {
+		multiphysaddr_t *mpa, *tmpa;
+
+		mpa = ill->ill_mphysaddr_list;
+		ill->ill_mphysaddr_list = NULL;
+		while (mpa) {
+			tmpa = mpa->mpa_next;
+			kmem_free(mpa, sizeof (*mpa));
+			mpa = tmpa;
+		}
+	}
 	/*
 	 * Take us out of the list of ILLs. ill_glist_delete -> phyint_free
 	 * could free the phyint. No more reference to the phyint after this
--- a/usr/src/uts/common/inet/ip/ip_multi.c	Tue Jul 13 11:05:20 2010 +0800
+++ b/usr/src/uts/common/inet/ip/ip_multi.c	Tue Jul 13 18:17:30 2010 +0530
@@ -19,10 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1990 Mentat Inc.
  */
-/* Copyright (c) 1990 Mentat Inc. */
 
 #include <sys/types.h>
 #include <sys/stream.h>
@@ -729,6 +728,85 @@
 }
 
 /*
+ * Looks up the list of multicast physical addresses this interface
+ * listens to. Add to the list if not present already.
+ */
+boolean_t
+ip_mphysaddr_add(ill_t *ill, uchar_t *hw_addr)
+{
+	multiphysaddr_t *mpa = NULL;
+	int	hw_addr_length = ill->ill_phys_addr_length;
+
+	mutex_enter(&ill->ill_lock);
+	for (mpa = ill->ill_mphysaddr_list; mpa != NULL; mpa = mpa->mpa_next) {
+		if (bcmp(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length) == 0) {
+			mpa->mpa_refcnt++;
+			mutex_exit(&ill->ill_lock);
+			return (B_FALSE);
+		}
+	}
+
+	mpa = kmem_zalloc(sizeof (multiphysaddr_t), KM_NOSLEEP);
+	if (mpa == NULL) {
+		/*
+		 * We risk not having the multiphysadd structure. At this
+		 * point we can't fail. We can't afford to not send a
+		 * DL_ENABMULTI_REQ also. It is better than pre-allocating
+		 * the structure and having the code to track it also.
+		 */
+		ip0dbg(("ip_mphysaddr_add: ENOMEM. Some multicast apps"
+		    " may have issues. hw_addr: %p ill_name: %s\n",
+		    (void *)hw_addr, ill->ill_name));
+		mutex_exit(&ill->ill_lock);
+		return (B_TRUE);
+	}
+	bcopy(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length);
+	mpa->mpa_refcnt = 1;
+	mpa->mpa_next = ill->ill_mphysaddr_list;
+	ill->ill_mphysaddr_list = mpa;
+	mutex_exit(&ill->ill_lock);
+	return (B_TRUE);
+}
+
+/*
+ * Look up hw_addr from the list of physical multicast addresses this interface
+ * listens to.
+ * Remove the entry if the refcnt is 0
+ */
+boolean_t
+ip_mphysaddr_del(ill_t *ill, uchar_t *hw_addr)
+{
+	multiphysaddr_t *mpap = NULL, **mpapp = NULL;
+	int hw_addr_length = ill->ill_phys_addr_length;
+	boolean_t ret = B_FALSE;
+
+	mutex_enter(&ill->ill_lock);
+	for (mpapp = &ill->ill_mphysaddr_list; (mpap = *mpapp) != NULL;
+	    mpapp = &(mpap->mpa_next)) {
+		if (bcmp(hw_addr, &(mpap->mpa_addr[0]), hw_addr_length) == 0)
+			break;
+	}
+	if (mpap == NULL) {
+		/*
+		 * Should be coming here only when there was a memory
+		 * exhaustion and we were not able to allocate
+		 * a multiphysaddr_t. We still send a DL_DISABMULTI_REQ down.
+		 */
+
+		ip0dbg(("ip_mphysaddr_del: No entry for this addr. Some "
+		    "multicast apps might have had issues. hw_addr: %p "
+		    " ill_name: %s\n", (void *)hw_addr, ill->ill_name));
+		ret = B_TRUE;
+	} else if (--mpap->mpa_refcnt == 0) {
+		*mpapp = mpap->mpa_next;
+		kmem_free(mpap, sizeof (multiphysaddr_t));
+		ret = B_TRUE;
+	}
+	mutex_exit(&ill->ill_lock);
+	return (ret);
+}
+
+/*
  * Send a multicast request to the driver for enabling or disabling
  * multicast reception for v6groupp address. The caller has already
  * checked whether it is appropriate to send one or not.
@@ -742,6 +820,7 @@
 	mblk_t	*mp;
 	uint32_t addrlen, addroff;
 	ill_t *release_ill = NULL;
+	uchar_t *cp;
 	int err = 0;
 
 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
@@ -774,15 +853,29 @@
 		err = ENOMEM;
 		goto done;
 	}
-
-	switch (((union DL_primitives *)mp->b_rptr)->dl_primitive) {
+	cp = mp->b_rptr;
+
+	switch (((union DL_primitives *)cp)->dl_primitive) {
 	case DL_ENABMULTI_REQ:
+		cp += ((dl_enabmulti_req_t *)cp)->dl_addr_offset;
+		if (!ip_mphysaddr_add(ill, cp)) {
+			freemsg(mp);
+			err = 0;
+			goto done;
+		}
 		mutex_enter(&ill->ill_lock);
 		/* Track the state if this is the first enabmulti */
 		if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN)
 			ill->ill_dlpi_multicast_state = IDS_INPROGRESS;
 		mutex_exit(&ill->ill_lock);
 		break;
+	case DL_DISABMULTI_REQ:
+		cp += ((dl_disabmulti_req_t *)cp)->dl_addr_offset;
+		if (!ip_mphysaddr_del(ill, cp)) {
+			freemsg(mp);
+			err = 0;
+			goto done;
+		}
 	}
 	ill_dlpi_queue(ill, mp);
 done:
--- a/usr/src/uts/common/inet/ip_if.h	Tue Jul 13 11:05:20 2010 +0800
+++ b/usr/src/uts/common/inet/ip_if.h	Tue Jul 13 18:17:30 2010 +0530
@@ -33,7 +33,6 @@
 #endif
 
 #define	PREFIX_INFINITY	0xffffffffUL
-#define	IP_MAX_HW_LEN	40
 
 #define	IP_LOOPBACK_MTU	(8*1024)