Date: Thu, 12 Apr 2007 12:33:07 +0100 From: "Bruce M. Simpson" <bms@FreeBSD.org> To: Andrew Thompson <thompsa@freebsd.org> Cc: freebsd-net@freebsd.org Subject: Re: ipv6 multicast refcnt panic Message-ID: <461E18F3.6000905@FreeBSD.org> In-Reply-To: <20070412010707.GC9390@heff.fud.org.nz> References: <20070412010707.GC9390@heff.fud.org.nz>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --------------070108010701070003040309 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Andrew Thompson wrote: > I have come across this panic which appears to be from incorrect > refcounting on the inet6 multicast code. > I can reproduce this panic, however I don't entirely understand what's going on. When the same IPv6 unicast address is configured twice on the edsc0 interface, the ifmcstat(8) utility reports that the refcnt for two IPv6 multicast addresses changed. I do not understand why the duplicate unicast address isn't rejected, or why groups are being joined twice for the same address. I strongly suspect this is a bug in KAME the kind of which existed in netinet (whereby the 224.0.0.1 address was being joined more than once per ifnet) which the refcounting change has exposed as a panic, a very brief look at the ifaddr code in netinet6 suggests this is the case. Before second address assignment: edsc0: inet6 f00f::1 group ff01::1%edsc0 refcnt 1 mcast-macaddr 33:33:00:00:00:01 refcnt 1 group ff02::2:f23c:3567%edsc0 refcnt 1 mcast-macaddr 33:33:f2:3c:35:67 refcnt 1 group ff02::1%edsc0 refcnt 1 mcast-macaddr 33:33:00:00:00:01 refcnt 1 group ff02::1:ff00:1%edsc0 refcnt 1 mcast-macaddr 33:33:ff:00:00:01 refcnt 1 After second address assignment: edsc0: inet6 f00f::1 group ff02::1:ff00:1%edsc0 refcnt 1 mcast-macaddr 33:33:ff:00:00:01 refcnt 1 group ff01::1%edsc0 refcnt 2 mcast-macaddr 33:33:00:00:00:01 refcnt 1 group ff02::2:f23c:3567%edsc0 refcnt 2 mcast-macaddr 33:33:f2:3c:35:67 refcnt 1 group ff02::1%edsc0 refcnt 2 mcast-macaddr 33:33:00:00:00:01 refcnt 1 The order of the addresses in the list has flipped around, which makes visual comparison that much more difficult. Flipping those around to the same order as the first sample yields: edsc0: inet6 f00f::1 group ff01::1%edsc0 refcnt 2 mcast-macaddr 33:33:00:00:00:01 refcnt 1 group ff02::2:f23c:3567%edsc0 refcnt 2 mcast-macaddr 33:33:f2:3c:35:67 refcnt 1 group ff02::1%edsc0 refcnt 2 mcast-macaddr 33:33:00:00:00:01 refcnt 1 group ff02::1:ff00:1%edsc0 refcnt 1 mcast-macaddr 33:33:ff:00:00:01 refcnt 1 So we can be sure the addresses themselves haven't changed, but the refcount on the IPv6 multicast entries has gone up by 1. The refcount is no longer proxied to the ifnet-level ifma object since the code was changed. I don't entirely understand the relationship between the protocol-level multicast addresses and the unicast address in netinet6, or why attempting to configure the same unicast address on the same interface more than once wasn't rejected with an error. As far as I can tell the code is correct for the single address case. I've attached a patch which makes the netinet6 detach path more like the netinet one, though this isn't going to make a great deal of difference apart from code style; the net code already calls in6_ifdetach() in the right order. We can weaken the error checking in if_delmulti() to get an operational kernel, but this kind of defeats the point of doing the error checking (which is there to expose such problems). When reporting problems with the networking code it is helpful to use ifmcstat, INVARIANTS, and the DIAGNOSTIC kernel option as I tend to add code to catch cases like this. Regards, BMS --------------070108010701070003040309 Content-Type: text/x-patch; name="in6detach.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="in6detach.diff" ==== //depot/user/bms/netdev/sys/netinet6/in6_ifattach.c#1 - /home/bms/p4/netdev/sys/netinet6/in6_ifattach.c ==== --- /tmp/tmp.3746.0 Thu Apr 12 12:32:23 2007 +++ /home/bms/p4/netdev/sys/netinet6/in6_ifattach.c Thu Apr 12 12:25:55 2007 @@ -76,6 +76,7 @@ static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); static int in6_ifattach_loopback __P((struct ifnet *)); +static void in6_purgemaddrs __P((struct ifnet *)); #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 @@ -731,8 +732,6 @@ struct rtentry *rt; short rtflags; struct sockaddr_in6 sin6; - struct in6_multi *in6m; - struct in6_multi *in6m_next; /* remove neighbor management table */ nd6_purge(ifp); @@ -790,18 +789,10 @@ IFAFREE(&oia->ia_ifa); } - /* leave from all multicast groups joined */ - in6_pcbpurgeif0(&udbinfo, ifp); in6_pcbpurgeif0(&ripcbinfo, ifp); - - for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) { - in6m_next = LIST_NEXT(in6m, in6m_entry); - if (in6m->in6m_ifp != ifp) - continue; - in6_delmulti(in6m); - in6m = NULL; - } + /* leave from all multicast groups joined */ + in6_purgemaddrs(ifp); /* * remove neighbor management table. we call it twice just to make @@ -889,4 +880,23 @@ } splx(s); +} + +static void +in6_purgemaddrs(ifp) + struct ifnet *ifp; +{ + struct in6_multi *in6m; + struct in6_multi *oin6m; + +#ifdef DIAGNOSTIC + printf("%s: purging ifp %p\n", __func__, ifp); +#endif + + IFF_LOCKGIANT(ifp); + LIST_FOREACH_SAFE(in6m, &in6_multihead, in6m_entry, oin6m) { + if (in6m->in6m_ifp == ifp) + in6_delmulti(in6m); + } + IFF_UNLOCKGIANT(ifp); } --------------070108010701070003040309--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?461E18F3.6000905>