changeset 3799:271cf3faf99c

6459412 ip_strict_dst_multihoming does not handle multiple i/f with the same ip address
author ja97890
date Mon, 12 Mar 2007 04:15:10 -0700
parents 36499f71540b
children 3d29f956e474
files usr/src/uts/common/inet/ip.h usr/src/uts/common/inet/ip/ip.c usr/src/uts/common/inet/ip/ip6.c
diffstat 3 files changed, 133 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/inet/ip.h	Sat Mar 10 17:17:25 2007 -0800
+++ b/usr/src/uts/common/inet/ip.h	Mon Mar 12 04:15:10 2007 -0700
@@ -3191,6 +3191,7 @@
 extern void	ip_udp_input(queue_t *, mblk_t *, ipha_t *, ire_t *, ill_t *);
 extern void	ip_proto_input(queue_t *, mblk_t *, ipha_t *, ire_t *, ill_t *);
 extern void	ip_rput_other(ipsq_t *, queue_t *, mblk_t *, void *);
+extern ire_t	*ip_check_multihome(void *, ire_t *, ill_t *);
 extern void	ip_setqinfo(queue_t *, minor_t, boolean_t, ip_stack_t *);
 extern void	ip_trash_ire_reclaim(void *);
 extern void	ip_trash_timer_expire(void *);
--- a/usr/src/uts/common/inet/ip/ip.c	Sat Mar 10 17:17:25 2007 -0800
+++ b/usr/src/uts/common/inet/ip/ip.c	Mon Mar 12 04:15:10 2007 -0700
@@ -13988,78 +13988,107 @@
 	return (B_TRUE);
 }
 
-static boolean_t
-ip_rput_notforus(queue_t **qp, mblk_t *mp, ire_t *ire, ill_t *ill)
-{
-	ill_group_t	*ill_group;
-	ill_group_t	*ire_group;
-	queue_t 	*q;
+ire_t *
+ip_check_multihome(void *addr, ire_t *ire, ill_t *ill)
+{
+	ire_t		*new_ire;
 	ill_t		*ire_ill;
-	uint_t		ill_ifindex;
-	ip_stack_t *ipst = ill->ill_ipst;
-
-	q = *qp;
-	/*
-	 * We need to check to make sure the packet came in
-	 * on the queue associated with the destination IRE.
-	 * Note that for multicast packets and broadcast packets sent to
-	 * a broadcast address which is shared between multiple interfaces
-	 * we should not do this since we just got a random broadcast ire.
-	 */
-	if (ire->ire_rfq && ire->ire_type != IRE_BROADCAST) {
-		boolean_t check_multi = B_TRUE;
-
-		/*
-		 * This packet came in on an interface other than the
-		 * one associated with the destination address.
-		 * "Gateway" it to the appropriate interface here.
-		 * As long as the ills belong to the same group,
-		 * we don't consider them to arriving on the wrong
-		 * interface. Thus, when the switch is doing inbound
-		 * load spreading, we won't drop packets when we
-		 * are doing strict multihoming checks. Note, the
-		 * same holds true for 'usesrc groups' where the
-		 * destination address may belong to another interface
-		 * to allow multipathing to happen
-		 */
-		ill_group = ill->ill_group;
-		ire_ill = (ill_t *)(ire->ire_rfq)->q_ptr;
-		ill_ifindex = ill->ill_usesrc_ifindex;
-		ire_group = ire_ill->ill_group;
-
-		/*
-		 * If it's part of the same IPMP group, or if it's a legal
-		 * address on the 'usesrc' interface, then bypass strict
-		 * checks.
-		 */
-		if (ill_group != NULL && ill_group == ire_group) {
-			check_multi = B_FALSE;
-		} else if (ill_ifindex != 0 &&
-		    ill_ifindex == ire_ill->ill_phyint->phyint_ifindex) {
-			check_multi = B_FALSE;
-		}
-
-		if (check_multi &&
-		    ipst->ips_ip_strict_dst_multihoming &&
-		    ((ill->ill_flags &
-		    ire->ire_ipif->ipif_ill->ill_flags &
-		    ILLF_ROUTER) == 0)) {
-			/* Drop packet */
-			BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits);
-			freemsg(mp);
-			return (B_TRUE);
-		}
-
-		/*
-		 * Change the queue (for non-virtual destination network
-		 * interfaces) and ip_rput_local will be called with the right
-		 * queue
-		 */
-		q = ire->ire_rfq;
-	}
-	/* Must be broadcast.  We'll take it. */
-	*qp = q;
-	return (B_FALSE);
+	uint_t		ifindex;
+	ip_stack_t	*ipst = ill->ill_ipst;
+	boolean_t	strict_check = B_FALSE;
+
+	/*
+	 * This packet came in on an interface other than the one associated
+	 * with the first ire we found for the destination address. We do
+	 * another ire lookup here, using the ingress ill, to see if the
+	 * interface is in an interface group.
+	 * As long as the ills belong to the same group, we don't consider
+	 * them to be arriving on the wrong interface. Thus, if the switch
+	 * is doing inbound load spreading, we won't drop packets when the
+	 * ip*_strict_dst_multihoming switch is on. Note, the same holds true
+	 * for 'usesrc groups' where the destination address may belong to
+	 * another interface to allow multipathing to happen.
+	 * We also need to check for IPIF_UNNUMBERED point2point interfaces
+	 * where the local address may not be unique. In this case we were
+	 * at the mercy of the initial ire cache lookup and the IRE_LOCAL it
+	 * actually returned. The new lookup, which is more specific, should
+	 * only find the IRE_LOCAL associated with the ingress ill if one
+	 * exists.
+	 */
+
+	if (ire->ire_ipversion == IPV4_VERSION) {
+		if (ipst->ips_ip_strict_dst_multihoming)
+			strict_check = B_TRUE;
+		new_ire = ire_ctable_lookup(*((ipaddr_t *)addr), 0, IRE_LOCAL,
+		    ill->ill_ipif, ALL_ZONES, NULL,
+		    (MATCH_IRE_TYPE|MATCH_IRE_ILL_GROUP), ipst);
+	} else {
+		ASSERT(!IN6_IS_ADDR_MULTICAST((in6_addr_t *)addr));
+		if (ipst->ips_ipv6_strict_dst_multihoming)
+			strict_check = B_TRUE;
+		new_ire = ire_ctable_lookup_v6((in6_addr_t *)addr, NULL,
+		    IRE_LOCAL, ill->ill_ipif, ALL_ZONES, NULL,
+		    (MATCH_IRE_TYPE|MATCH_IRE_ILL_GROUP), ipst);
+	}
+	/*
+	 * If the same ire that was returned in ip_input() is found then this
+	 * is an indication that interface groups are in use. The packet
+	 * arrived on a different ill in the group than the one associated with
+	 * the destination address.  If a different ire was found then the same
+	 * IP address must be hosted on multiple ills. This is possible with
+	 * unnumbered point2point interfaces. We switch to use this new ire in
+	 * order to have accurate interface statistics.
+	 */
+	if (new_ire != NULL) {
+		if ((new_ire != ire) && (new_ire->ire_rfq != NULL)) {
+			ire_refrele(ire);
+			ire = new_ire;
+		} else {
+			ire_refrele(new_ire);
+		}
+		return (ire);
+	} else if ((ire->ire_rfq == NULL) &&
+		    (ire->ire_ipversion == IPV4_VERSION)) {
+		/*
+		 * The best match could have been the original ire which
+		 * was created against an IRE_LOCAL on lo0. In the IPv4 case
+		 * the strict multihoming checks are irrelevant as we consider
+		 * local addresses hosted on lo0 to be interface agnostic. We
+		 * only expect a null ire_rfq on IREs which are associated with
+		 * lo0 hence we can return now.
+		 */
+		return (ire);
+	}
+
+	/*
+	 * Chase pointers once and store locally.
+	 */
+	ire_ill = (ire->ire_rfq == NULL) ? NULL :
+	    (ill_t *)(ire->ire_rfq->q_ptr);
+	ifindex = ill->ill_usesrc_ifindex;
+
+	/*
+	 * Check if it's a legal address on the 'usesrc' interface.
+	 */
+	if ((ifindex != 0) && (ire_ill != NULL) &&
+	    (ifindex == ire_ill->ill_phyint->phyint_ifindex)) {
+		return (ire);
+	}
+
+	/*
+	 * If the ip*_strict_dst_multihoming switch is on then we can
+	 * only accept this packet if the interface is marked as routing.
+	 */
+	if (!(strict_check))
+		return (ire);
+
+	if ((ill->ill_flags & ire->ire_ipif->ipif_ill->ill_flags &
+	    ILLF_ROUTER) != 0) {
+		return (ire);
+	}
+
+	ire_refrele(ire);
+	return (NULL);
 }
 
 ire_t *
@@ -15439,10 +15468,25 @@
 		}
 
 local:
-		/* packet not for us */
-		if (ire->ire_rfq != q) {
-			if (ip_rput_notforus(&q, mp, ire, ill))
+		/*
+		 * If the queue in the ire is different to the ingress queue
+		 * then we need to check to see if we can accept the packet.
+		 * Note that for multicast packets and broadcast packets sent
+		 * to a broadcast address which is shared between multiple
+		 * interfaces we should not do this since we just got a random
+		 * broadcast ire.
+		 */
+		if ((ire->ire_rfq != q) && (ire->ire_type != IRE_BROADCAST)) {
+			if ((ire = ip_check_multihome(&ipha->ipha_dst, ire,
+			    ill)) == NULL) {
+				/* Drop packet */
+				BUMP_MIB(ill->ill_ip_mib,
+				    ipIfStatsForwProhibits);
+				freemsg(mp);
 				continue;
+			}
+			if (ire->ire_rfq != NULL)
+				q = ire->ire_rfq;
 		}
 
 		switch (ipha->ipha_protocol) {
--- a/usr/src/uts/common/inet/ip/ip6.c	Sat Mar 10 17:17:25 2007 -0800
+++ b/usr/src/uts/common/inet/ip/ip6.c	Mon Mar 12 04:15:10 2007 -0700
@@ -7245,7 +7245,6 @@
     uint_t flags, mblk_t *hada_mp, mblk_t *dl_mp)
 {
 	ire_t		*ire = NULL;
-	queue_t		*rq;
 	ill_t		*ill = inill;
 	ill_t		*outill;
 	ipif_t		*ipif;
@@ -7418,7 +7417,6 @@
 			pr_addr_dbg("ip_rput_data_v6: multicast for us: %s\n",
 			    AF_INET6, &ip6h->ip6_dst);
 		}
-		rq = ill->ill_rq;
 		zoneid = GLOBAL_ZONEID;
 		goto ipv6forus;
 	}
@@ -7687,77 +7685,29 @@
 		IRE_REFRELE(ire);
 		return;
 	}
-	rq = ire->ire_rfq;
 
 	/*
 	 * Need to put on correct queue for reassembly to find it.
 	 * No need to use put() since reassembly has its own locks.
 	 * Note: multicast packets and packets destined to addresses
 	 * assigned to loopback (ire_rfq is NULL) will be reassembled on
-	 * the arriving ill.
-	 */
-	if (rq != q) {
-		boolean_t check_multi = B_TRUE;
-		ill_group_t *ill_group = NULL;
-		ill_group_t *ire_group = NULL;
-		ill_t	*ire_ill = NULL;
-		uint_t	ill_ifindex = ill->ill_usesrc_ifindex;
-
-		/*
-		 * To be quicker, we may wish not to chase pointers
-		 * (ire->ire_ipif->ipif_ill...) and instead store the
-		 * forwarding policy in the ire.  An unfortunate side-
-		 * effect of this would be requiring an ire flush whenever
-		 * the ILLF_ROUTER flag changes.  For now, chase pointers
-		 * once and store in the boolean no_forward.
-		 */
-		no_forward = ((ill->ill_flags &
-		    ire->ire_ipif->ipif_ill->ill_flags & ILLF_ROUTER) == 0);
-
-		ill_group = ill->ill_group;
-		if (rq != NULL) {
-			ire_ill = (ill_t *)(rq->q_ptr);
-			ire_group = ire_ill->ill_group;
-		}
-
-		/*
-		 * If it's part of the same IPMP group, or if it's a legal
-		 * address on the 'usesrc' interface, then bypass strict
-		 * checks.
-		 */
-		if (ill_group != NULL && ill_group == ire_group) {
-			check_multi = B_FALSE;
-		} else if (ill_ifindex != 0 && ire_ill != NULL &&
-		    ill_ifindex == ire_ill->ill_phyint->phyint_ifindex) {
-			check_multi = B_FALSE;
-		}
-
-		ASSERT(!IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst));
-		if (check_multi && ipst->ips_ipv6_strict_dst_multihoming &&
-		    no_forward) {
-			/*
-			 * This packet came in on an interface other than the
-			 * one associated with the destination address
-			 * and we are strict about matches.
-			 *
-			 * As long as the ills belong to the same group,
-			 * we don't consider them to arriving on the wrong
-			 * interface. Thus, when the switch is doing inbound
-			 * load spreading, we won't drop packets when we
-			 * are doing strict multihoming checks.
-			 */
+	 * the arriving ill. Unlike the IPv4 case, enabling strict
+	 * destination multihoming will prevent accepting packets
+	 * addressed to an IRE_LOCAL on lo0.
+	 */
+	if (ire->ire_rfq != q) {
+		if ((ire = ip_check_multihome(&ip6h->ip6_dst, ire, ill))
+		    == NULL) {
 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits);
 			freemsg(hada_mp);
 			freemsg(first_mp);
-			ire_refrele(ire);
-			return;
-		}
-
-		if (rq != NULL)
-			q = rq;
-
-		ill = (ill_t *)q->q_ptr;
-		ASSERT(ill);
+			return;
+		}
+		if (ire->ire_rfq != NULL) {
+			q = ire->ire_rfq;
+			ill = (ill_t *)q->q_ptr;
+			ASSERT(ill != NULL);
+		}
 	}
 
 	zoneid = ire->ire_zoneid;