Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 27 Mar 2007 17:19:33 +0100
From:      "Bruce M. Simpson" <bms@FreeBSD.org>
To:        FreeBSD Current <freebsd-current@freebsd.org>
Subject:   [Fwd: Panic: if_freemulti: protospec not NULL]
Message-ID:  <46094415.3050704@FreeBSD.org>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------080000050300080700070504
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Patch for this condition is attached.

This particular bug is irritating: the IPv4 stack joins 224.0.0.1 once 
for every IPv4 unicast address configured in the stack. This is 
incorrect behaviour. The implementation of refcounting has exposed this 
bug. The fix is not particularly elegant.

It is most likely a left-over from the FreeBSD 2.x/3.x era when IPv4 
'aliases' were first introduced. At that point in time the IGMP code in 
FreeBSD would allow groups to be joined on a per-IPv4-address basis, 
which is inconsistent with the IGMPv2/v3 specified behaviour (and indeed 
that addressed in future multicast RFCs).

It seems that there are other situations where the stack is not 
adequately equipped to deal with interfaces going away unexpectedly. We 
can't afford to be complacent about multicast code on the basis of 'it's 
not the critical path', because it is an integral component of IPv6, and 
many ideas which people are trying to implement in IPv4 also require 
that the multicast code is fit for purpose.

We would do well to have more people available to help on reviewing and 
possibly rewriting parts of the network stack from a perspective of 
correctness, not just performance. If this interests you please consider 
signing up to the Wiki and updating the page at 
http://wiki.freebsd.org/NetworkRFCCompliance.

Regards,
BMS

--------------080000050300080700070504
Content-Type: text/x-patch;
 name="ifa_detach.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="ifa_detach.diff"

==== //depot/user/bms/netdev/sys/netinet/in.c#11 - /home/bms/p4/netdev/sys/netinet/in.c ====
--- /tmp/tmp.1127.0	Tue Mar 27 16:43:56 2007
+++ /home/bms/p4/netdev/sys/netinet/in.c	Tue Mar 27 16:40:04 2007
@@ -212,6 +212,11 @@
 /*
  * Generic internet control operations (ioctl's).
  * Ifp is 0 if not an interface-specific ioctl.
+ *
+ * XXX: This code has some nice big juicy bugs related to interface
+ * attach and detach. Specifically, the 224.0.0.1 group is now only
+ * joined on the first IPv4 address configured on an interface, and
+ * only left when all IPv4 state is torn down for an interface.
  */
 /* ARGSUSED */
 int
@@ -230,7 +235,9 @@
 	struct in_aliasreq *ifra = (struct in_aliasreq *)data;
 	struct sockaddr_in oldaddr;
 	int error, hostIsNew, iaIsNew, maskIsNew, s;
+	int iaIsFirst;
 
+	iaIsFirst = 0;
 	iaIsNew = 0;
 
 	switch (cmd) {
@@ -282,6 +289,8 @@
 					break;
 				}
 			}
+		if (ia == NULL)
+			iaIsFirst = 1;
 	}
 
 	switch (cmd) {
@@ -423,8 +432,20 @@
 		    (struct sockaddr_in *) &ifr->ifr_addr, 1);
 		if (error != 0 && iaIsNew)
 			break;
-		if (error == 0)
+		if (error == 0) {
+			/*
+			 * Only join 224.0.0.1 if this is the very first
+			 * IPv4 unicast address which has been configured
+			 * on this ifnet.
+			 */
+			if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) {
+				struct in_addr addr;
+
+				addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+				in_addmulti(&addr, ifp);
+			}
 			EVENTHANDLER_INVOKE(ifaddr_event, ifp);
+		}
 		return (0);
 
 	case SIOCSIFNETMASK:
@@ -467,8 +488,20 @@
 		if ((ifp->if_flags & IFF_BROADCAST) &&
 		    (ifra->ifra_broadaddr.sin_family == AF_INET))
 			ia->ia_broadaddr = ifra->ifra_broadaddr;
-		if (error == 0)
+		if (error == 0) {
+			/*
+			 * Only join 224.0.0.1 if this is the very first
+			 * IPv4 unicast address which has been configured
+			 * on this ifnet.
+			 */
+			if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST) != 0) {
+				struct in_addr addr;
+
+				addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+				in_addmulti(&addr, ifp);
+			}
 			EVENTHANDLER_INVOKE(ifaddr_event, ifp);
+		}
 		return (error);
 
 	case SIOCDIFADDR:
@@ -503,8 +536,33 @@
 	s = splnet();
 	TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
 	TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link);
-	if (ia->ia_addr.sin_family == AF_INET)
+	if (ia->ia_addr.sin_family == AF_INET) {
+		struct in_ifaddr *pia;
+
 		LIST_REMOVE(ia, ia_hash);
+		/*
+		 * Only purge the 224.0.0.1 membership if the last IPv4
+		 * unicast address configured on this ifnet is removed.
+		 *
+		 * XXX: This currently involves trawling through the
+		 * in_ifaddrhead list looking for a match, which is
+		 * inefficient, though this is only done when an interface
+		 * address goes away. This could be done much more elegantly.
+		 */
+		pia = NULL;
+		IFP_TO_IA(ifp, pia);
+		if (pia == NULL) {
+			struct in_multi *inm;
+			struct in_addr addr;
+
+			addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+			IN_MULTI_LOCK();
+			IN_LOOKUP_MULTI(addr, ifp, inm);
+			if (inm != NULL)
+				in_delmulti(inm);
+			IN_MULTI_UNLOCK();
+		}
+	}
 	IFAFREE(&ia->ia_ifa);
 	splx(s);
 
@@ -793,16 +851,6 @@
 	if ((error = in_addprefix(ia, flags)) != 0)
 		return (error);
 
-	/*
-	 * If the interface supports multicast, join the "all hosts"
-	 * multicast group on that interface.
-	 */
-	if (ifp->if_flags & IFF_MULTICAST) {
-		struct in_addr addr;
-
-		addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
-		in_addmulti(&addr, ifp);
-	}
 	return (error);
 }
 
@@ -1114,6 +1162,9 @@
 		igmp_leavegroup(inm);
 
 		ifma = inm->inm_ifma;
+#ifdef DIAGNOSTIC
+		printf("%s: purging ifma %p\n", __func__, ifma);
+#endif
 		KASSERT(ifma->ifma_protospec == inm,
 		    ("%s: ifma_protospec != inm", __func__));
 		ifma->ifma_protospec = NULL;
@@ -1135,6 +1186,9 @@
 	struct in_multi *inm;
 	struct in_multi *oinm;
 
+#ifdef DIAGNOSTIC
+	printf("%s: purging ifp %p\n", __func__, ifp);
+#endif
 	IFF_LOCKGIANT(ifp);
 	IN_MULTI_LOCK();
 	LIST_FOREACH_SAFE(inm, &in_multihead, inm_link, oinm) {

--------------080000050300080700070504
Content-Type: message/rfc822;
 name="Panic: if_freemulti: protospec not NULL"
Content-Transfer-Encoding: 8bit
Content-Disposition: inline; filename="Panic: if_freemulti: protospec not NULL"

Received: from compute1.internal (compute1.internal [10.202.2.41])
	by store23m.internal (Cyrus v2.3.8-fmsvn11108) with LMTPA;
	Tue, 27 Mar 2007 07:43:01 -0400
X-Sieve: CMU Sieve 2.3
X-Spam-score: 0.0
X-Spam-source: IP='69.147.83.53', Country='US', FromHeader='org',
	MailFrom='org'
X-Delivered-to: bms@fastmail.net
Received: from mx2.freebsd.org (mx2.freebsd.org [69.147.83.53])
	by mx2.messagingengine.com (Postfix) with ESMTP id 404E91E04EE
	for <bms@fastmail.net>; Tue, 27 Mar 2007 07:42:58 -0400 (EDT)
Received: from hub.freebsd.org (hub.freebsd.org [69.147.83.54])
	by mx2.freebsd.org (Postfix) with ESMTP id 1855019D60
	for <bms@fastmail.net>; Tue, 27 Mar 2007 11:40:16 +0000 (UTC)
	(envelope-from andre@freebsd.org)
Received: by hub.freebsd.org (Postfix)
	id 1469A16A404; Tue, 27 Mar 2007 11:40:16 +0000 (UTC)
Delivered-To: bms@freebsd.org
Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52])
	by hub.freebsd.org (Postfix) with ESMTP id EEBA816A403
	for <bms@freebsd.org>; Tue, 27 Mar 2007 11:40:15 +0000 (UTC)
	(envelope-from andre@freebsd.org)
Received: from c00l3r.networx.ch (c00l3r.networx.ch [62.48.2.2])
	by mx1.freebsd.org (Postfix) with ESMTP id 2057413C457
	for <bms@freebsd.org>; Tue, 27 Mar 2007 11:40:14 +0000 (UTC)
	(envelope-from andre@freebsd.org)
Received: (qmail 7080 invoked from network); 27 Mar 2007 11:08:10 -0000
Received: from dotat.atdotat.at (HELO [62.48.0.47]) ([62.48.0.47])
	(envelope-sender <andre@freebsd.org>)
	by c00l3r.networx.ch (qmail-ldap-1.03) with SMTP
	for <bms@freebsd.org>; 27 Mar 2007 11:08:10 -0000
Message-ID: <4609029E.8070604@freebsd.org>
Date: Tue, 27 Mar 2007 13:40:14 +0200
From: Andre Oppermann <andre@freebsd.org>
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US;
	rv:1.8b) Gecko/20050217
MIME-Version: 1.0
To: bms@freebsd.org
Subject: Panic: if_freemulti: protospec not NULL
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 8bit


fxp0: 62.48.0.50/24

sysctl net.link.

ifconfig lo1 create
ifconfig lo1 inet 62.48.0.39/32
ifconfig lo1 inet 62.48.0.38/32 alias
# ping tests etc.
ifconfig lo1 delete 62.48.0.39
ifconfig lo1 delete 62.48.0.38
ifconfig lo1 destroy
# panic

Unread portion of the kernel message buffer:
panic: if_freemulti: protospec not NULL
cpuid = 1
KDB: enter: panic
Physical memory: 2035 MB
Dumping 103 MB: 88 72 56 40 24 8

#0  doadump () at pcpu.h:172
172     pcpu.h: No such file or directory.
         in pcpu.h
(kgdb) bt
#0  doadump () at pcpu.h:172
#1  0xc0477a3f in db_fncall (dummy1=-406202100, dummy2=0, dummy3=-1062950496, 
dummy4=0xe7c9d8e8 "\200DŽĀ")
     at ../../../ddb/db_command.c:486
#2  0xc047784b in db_command (last_cmdp=0xc0a23244, cmd_table=0x0) at 
../../../ddb/db_command.c:401
#3  0xc0477906 in db_command_loop () at ../../../ddb/db_command.c:453
#4  0xc0479551 in db_trap (type=3, code=0) at ../../../ddb/db_main.c:222
#5  0xc06e1f0c in kdb_trap (type=3, code=0, tf=0xe7c9da80) at ../../../kern/subr_kdb.c:502
#6  0xc08c0529 in trap (frame=0xe7c9da80) at ../../../i386/i386/trap.c:621
#7  0xc08aa47b in calltrap () at ../../../i386/i386/exception.s:139
#8  0xc06e1c33 in kdb_enter (msg=0x12 <Address 0x12 out of bounds>) at cpufunc.h:60
#9  0xc06c2fc0 in panic (fmt=0xc0956e98 "if_freemulti: protospec not NULL") at 
../../../kern/kern_shutdown.c:547
#10 0xc073aa18 in if_freemulti (ifma=0xc548f360) at ../../../net/if.c:2251
#11 0xc073b036 in if_delmulti_locked (ifp=0xc50fd000, ifma=0xc548f360, detaching=1) at 
../../../net/if.c:2552
#12 0xc0737dd7 in if_purgemaddrs (ifp=0xc50fd000) at ../../../net/if.c:636
#13 0xc0737f0b in if_detach (ifp=0xc50fd000) at ../../../net/if.c:701
#14 0xc073e939 in lo_clone_destroy (ifp=0xc50fd000) at ../../../net/if_loop.c:131
#15 0xc073c1aa in ifc_simple_destroy (ifc=0xc0a015e0, ifp=0xc1433000) at 
../../../net/if_clone.c:560
#16 0xc073b753 in if_clone_destroyif (ifc=0xc0a015e0, ifp=0xc50fd000) at 
../../../net/if_clone.c:218
#17 0xc073b685 in if_clone_destroy (name=0xc548f9e0 "lo1") at ../../../net/if_clone.c:196
#18 0xc073a370 in ifioctl (so=0xc558ec3c, cmd=2149607801, data=0xc548f9e0 "lo1", 
td=0xc5529a20)
     at ../../../net/if.c:1849
#19 0xc06f4b2f in soo_ioctl (fp=0x12, cmd=2149607801, data=0xc548f9e0, 
active_cred=0xc5899880, td=0xc5529a20)
     at ../../../kern/sys_socket.c:202
#20 0xc06ef872 in kern_ioctl (td=0xc5529a20, fd=3, com=2149607801, data=0xc548f9e0 "lo1") 
at file.h:266
#21 0xc06ef595 in ioctl (td=0xc5529a20, uap=0xe7c9dd00) at ../../../kern/sys_generic.c:544
#22 0xc08c0d72 in syscall (frame=0xe7c9dd38) at ../../../i386/i386/trap.c:1010
#23 0xc08aa4e0 in Xint0x80_syscall () at ../../../i386/i386/exception.s:196
#24 0x00000033 in ?? ()
Previous frame inner to this frame (corrupt stack?)
(kgdb) frame 10
#10 0xc073aa18 in if_freemulti (ifma=0xc548f360) at ../../../net/if.c:2251
2251            KASSERT(ifma->ifma_protospec == NULL,
(kgdb) list
2246    if_freemulti(struct ifmultiaddr *ifma)
2247    {
2248
2249            KASSERT(ifma->ifma_refcount == 0, ("if_freemulti: refcount %d",
2250                ifma->ifma_refcount));
2251            KASSERT(ifma->ifma_protospec == NULL,
2252                ("if_freemulti: protospec not NULL"));
2253
2254            if (ifma->ifma_lladdr != NULL)
2255                    FREE(ifma->ifma_lladdr, M_IFMADDR);
(kgdb) inspect *ifma
$2 = {ifma_link = {tqe_next = 0x0, tqe_prev = 0xc50fd0bc}, ifma_addr = 0xc53210d0, 
ifma_lladdr = 0x0, ifma_ifp = 0x0,
   ifma_refcount = 0, ifma_protospec = 0xc5716180, ifma_llifma = 0x0}
(kgdb) up
#11 0xc073b036 in if_delmulti_locked (ifp=0xc50fd000, ifma=0xc548f360, detaching=1) at 
../../../net/if.c:2552
2552            if_freemulti(ifma);
(kgdb) inspect *ifp
$3 = {if_softc = 0xc548c990, if_l2com = 0x0, if_link = {tqe_next = 0x0, tqe_prev = 
0xc530b808},
   if_xname = "lo1", '\0' <repeats 12 times>, if_dname = 0xc0909303 "lo", if_dunit = 1, 
if_addrhead = {
     tqh_first = 0xc59b7600, tqh_last = 0xc59b7660}, if_klist = {kl_list = {slh_first = 0x0},
     kl_lock = 0xc06a8d78 <knlist_mtx_lock>, kl_unlock = 0xc06a8d94 <knlist_mtx_unlock>,
     kl_locked = 0xc06a8db0 <knlist_mtx_locked>, kl_lockarg = 0xc0a4d7f8}, if_pcount = 0, 
if_carp = 0x0,
   if_bpf = 0xc5716b80, if_index = 8, if_timer = 0, if_vlantrunk = 0x0, if_flags = 32776, 
if_capabilities = 0,
   if_capenable = 0, if_linkmib = 0x0, if_linkmiblen = 0, if_data = {ifi_type = 24 '\030', 
ifi_physical = 0 '\0',
     ifi_addrlen = 0 '\0', ifi_hdrlen = 0 '\0', ifi_link_state = 0 '\0', ifi_recvquota = 0 
'\0',
     ifi_xmitquota = 0 '\0', ifi_datalen = 80 'P', ifi_mtu = 16384, ifi_metric = 0, 
ifi_baudrate = 0,
     ifi_ipackets = 0, ifi_ierrors = 0, ifi_opackets = 0, ifi_oerrors = 0, ifi_collisions 
= 0, ifi_ibytes = 0,
     ifi_obytes = 0, ifi_imcasts = 0, ifi_omcasts = 0, ifi_iqdrops = 0, ifi_noproto = 0, 
ifi_hwassist = 0,
     ifi_epoch = 7771, ifi_lastchange = {tv_sec = 1174954038, tv_usec = 187825}}, 
if_multiaddrs = {tqh_first = 0x0,
     tqh_last = 0xc50fd0bc}, if_amcount = 0, if_output = 0xc073eadc <looutput>, if_input = 
0, if_start = 0,
   if_ioctl = 0xc073ed14 <loioctl>, if_watchdog = 0, if_init = 0, if_resolvemulti = 0, 
if_addr = 0xc59b7600,
   if_spare2 = 0x0, if_spare3 = 0x0, if_drv_flags = 64, if_spare_flags2 = 0, if_snd = 
{ifq_head = 0x0, ifq_tail = 0x0,
     ifq_len = 0, ifq_maxlen = 50, ifq_drops = 0, ifq_mtx = {lock_object = {lo_name = 
0xc50fd010 "lo1",
         lo_type = 0xc0956c03 "if send queue", lo_flags = 16973824, lo_witness_data = 
{lod_list = {
             stqe_next = 0xc0a5de98}, lod_witness = 0xc0a5de98}}, mtx_lock = 4, 
mtx_recurse = 0}, ifq_drv_head = 0x0,
     ifq_drv_tail = 0x0, ifq_drv_len = 0, ifq_drv_maxlen = 0, altq_type = 0, altq_flags = 
0, altq_disc = 0x0,
     altq_ifp = 0xc50fd000, altq_enqueue = 0, altq_dequeue = 0, altq_request = 0, 
altq_clfier = 0x0,
     altq_classify = 0, altq_tbr = 0x0, altq_cdnr = 0x0}, if_broadcastaddr = 0x0, 
if_bridge = 0x0, lltables = 0x0,
   if_label = 0x0, if_prefixhead = {tqh_first = 0x0, tqh_last = 0xc50fd170}, if_afdata = 
{0x0 <repeats 28 times>,
     0xc548c720, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, if_afdata_initialized = 2, 
if_afdata_mtx = {
     lock_object = {lo_name = 0xc0956bf9 "if_afdata", lo_type = 0xc0956bf9 "if_afdata", 
lo_flags = 16973824,
       lo_witness_data = {lod_list = {stqe_next = 0xc0a5dec0}, lod_witness = 0xc0a5dec0}}, 
mtx_lock = 4,
     mtx_recurse = 0}, if_starttask = {ta_link = {stqe_next = 0x0}, ta_pending = 0, 
ta_priority = 0,
     ta_func = 0xc073b2b0 <if_start_deferred>, ta_context = 0xc50fd000}, if_linktask = 
{ta_link = {stqe_next = 0x0},
     ta_pending = 0, ta_priority = 0, ta_func = 0xc07393f8 <do_link_state_change>, 
ta_context = 0xc50fd000},
   if_addr_mtx = {lock_object = {lo_name = 0xc094f3db "if_addr_mtx", lo_type = 0xc094f3db 
"if_addr_mtx",
       lo_flags = 16973824, lo_witness_data = {lod_list = {stqe_next = 0xc0a5f388}, 
lod_witness = 0xc0a5f388}},
     mtx_lock = 3310524960, mtx_recurse = 0}, if_clones = {le_next = 0xc530b800, le_prev = 
0xc0a01628}, if_groups = {
     tqh_first = 0xc548c6f0, tqh_last = 0xc548c6f4}, if_pf_kif = 0x0}
kgdb) inspect *ifma
$4 = {ifma_link = {tqe_next = 0x0, tqe_prev = 0xc50fd0bc}, ifma_addr = 0xc53210d0, 
ifma_lladdr = 0x0, ifma_ifp = 0x0,
   ifma_refcount = 0, ifma_protospec = 0xc5716180, ifma_llifma = 0x0}
(kgdb) up
#12 0xc0737dd7 in if_purgemaddrs (ifp=0xc50fd000) at ../../../net/if.c:636
636                     if_delmulti_locked(ifp, ifma, 1);
(kgdb) up
#13 0xc0737f0b in if_detach (ifp=0xc50fd000) at ../../../net/if.c:701
701             if_purgemaddrs(ifp);
(kgdb) up
#14 0xc073e939 in lo_clone_destroy (ifp=0xc50fd000) at ../../../net/if_loop.c:131
131             if_detach(ifp);
(kgdb) up
#15 0xc073c1aa in ifc_simple_destroy (ifc=0xc0a015e0, ifp=0xc1433000) at 
../../../net/if_clone.c:560
560             ifcs->ifcs_destroy(ifp);
(kgdb) inspect *ifc
$5 = {ifc_list = {le_next = 0xc0a01400, le_prev = 0xc0a9d034}, ifc_name = 0xc0909303 "lo", 
ifc_maxunit = 32767,
   ifc_units = 0xc5316000 "\003", ifc_bmlen = 4096, ifc_data = 0xc0a015c4,
   ifc_attach = 0xc073bfe4 <ifc_simple_attach>, ifc_match = 0xc073c070 <ifc_simple_match>,
   ifc_create = 0xc073c0d4 <ifc_simple_create>, ifc_destroy = 0xc073c18c 
<ifc_simple_destroy>, ifc_refcnt = 3,
   ifc_mtx = {lock_object = {lo_name = 0xc0957019 "if_clone lock", lo_type = 0xc0957019 
"if_clone lock",
       lo_flags = 16973824, lo_witness_data = {lod_list = {stqe_next = 0xc0a5dbc8}, 
lod_witness = 0xc0a5dbc8}},
     mtx_lock = 4, mtx_recurse = 0}, ifc_iflist = {lh_first = 0xc530b800}}
(kgdb) up
#16 0xc073b753 in if_clone_destroyif (ifc=0xc0a015e0, ifp=0xc50fd000) at 
../../../net/if_clone.c:218
218             err =  (*ifc->ifc_destroy)(ifc, ifp);
(kgdb) up
#17 0xc073b685 in if_clone_destroy (name=0xc548f9e0 "lo1") at ../../../net/if_clone.c:196
196             return (if_clone_destroyif(ifc, ifp));
(kgdb) up
#18 0xc073a370 in ifioctl (so=0xc558ec3c, cmd=2149607801, data=0xc548f9e0 "lo1", 
td=0xc5529a20)
     at ../../../net/if.c:1849
1849                    return if_clone_destroy(ifr->ifr_name);


--------------080000050300080700070504--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?46094415.3050704>