On 07/12/15(Mon) 12:29, Martin Pieuchot wrote:
> On 06/12/15(Sun) 13:52, [email protected] wrote:
> > >Synopsis: Unplugging my urtwn device causes an assertion failure in the
> > >driver code (see trace)
> > >Category: system kernel amd64
> > >Environment:
> > System : OpenBSD 5.8
> > Details : OpenBSD 5.8-current (GENERIC.MP) #1706: Fri Dec 4
> > 01:49:55 MST 2015
> >
> > [email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
> >
> > Architecture: OpenBSD.amd64
> > Machine : amd64
> > >Description:
> >
> > In the past, my urtwn(4) device (RTL8192CU chipset, I think) would stop
> > working after a while, and would start working again after unplugging and
> > plugging back in. I suspect it stopped working due to overheating, because
> > it's
> > really hot when I pull it out. Occasionally, this would crash the system.
> >
> > I upgraded from the Nov 25 snapshot to yesterday's snapshot, and now it
> > seems to fail every time with an assertion that drops me to the ddb prompt.
> > Here's the traceback I typed from a picture of the screen:
> >
> > panic: kernel diagnostic assertion "ifp != NULL" failed: file
> > "../../../../net/route.c", line 863
> > Stopped at Debugger+0x9: leave
> > TID PID UID PRFLAGS PFLAGS CPU COMMAND
> > 18228 18228 77 0x10 0x80 3 dhclient
> > *26266 26266 0 0x14000 0x200 6 usbtask
> > Debugger() at Debugger+0x9
> > panic() at panic+0xfe
> > __assert() at __assert+0x25
> > rtrequest_delete() at rtrequest_delete+0x20a
> > rtdeletemsg() at rtdeletemsg+0xae
> > rtflushclone1() at rtflushclone1+0x98
> > rn_walktree() at rn_walktree+0x83
> > rtrequest_delete() at rtrequest_delete+0x18a
> > rtdeletemsg() at rtdeletemsg+0xae
> > rt_if_remove_rtdelete() at rt_if_remove_rtdelete+0x2b
> > rn_walktree() at rn_walktree+0x83
> > rt_if_remove() at rt_if_remove+0x58
> > if_detach() at if_detach+0x9e
> > urtwn_detach() at urtwn_detach+0x81
> > end trace frame 0xffff800032cdddc0, count: 0
>
> Could you show me the output of "route -n show" before unplugging your
> device? Are you using dhclient(8) on multiple interfaces?
Ok I found the bug, can you tell me if the diff below helps?
Index: net/route.c
===================================================================
RCS file: /cvs/src/sys/net/route.c,v
retrieving revision 1.289
diff -u -p -r1.289 route.c
--- net/route.c 5 Dec 2015 10:07:55 -0000 1.289
+++ net/route.c 7 Dec 2015 15:10:15 -0000
@@ -159,8 +159,7 @@ struct sockaddr *rt_plentosa(sa_family_t
struct ifaddr *ifa_ifwithroute(int, struct sockaddr *, struct sockaddr *,
u_int);
-int rtrequest_delete(struct rt_addrinfo *, u_int8_t, struct ifnet *,
- struct rtentry **, u_int);
+int rt_delete(struct rtentry *, struct ifnet *, unsigned int);
#ifdef DDB
void db_print_sa(struct sockaddr *);
@@ -613,38 +612,29 @@ out:
}
/*
- * Delete a route and generate a message
+ * Delete a route and generate a message. The caller must hold a reference
+ * on ``rt''.
*/
int
-rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, u_int tableid)
+rtdeletemsg(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
{
int error;
- struct rt_addrinfo info;
- unsigned int ifidx;
- struct sockaddr_in6 sa_mask;
- /*
- * Request the new route so that the entry is not actually
- * deleted. That will allow the information being reported to
- * be accurate (and consistent with route_output()).
- */
- bzero((caddr_t)&info, sizeof(info));
- info.rti_info[RTAX_DST] = rt_key(rt);
- if (!ISSET(rt->rt_flags, RTF_HOST))
- info.rti_info[RTAX_NETMASK] = rt_plen2mask(rt, &sa_mask);
- info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
- info.rti_flags = rt->rt_flags;
- ifidx = rt->rt_ifidx;
- error = rtrequest_delete(&info, rt->rt_priority, ifp, &rt, tableid);
- rt_missmsg(RTM_DELETE, &info, info.rti_flags, ifidx, error, tableid);
+ KASSERT(rt->rt_ifidx == ifp->if_index);
+
+ error = rt_delete(rt, ifp, rtableid);
if (error == 0)
- rtfree(rt);
+ rt_sendmsg(rt, RTM_DELETE, rtableid);
+
return (error);
}
static inline int
rtequal(struct rtentry *a, struct rtentry *b)
{
+ if (a == b)
+ return 1;
+
if (memcmp(rt_key(a), rt_key(b), rt_key(a)->sa_len) == 0 &&
rt_plen(a) == rt_plen(b))
return 1;
@@ -656,10 +646,25 @@ int
rtflushclone1(struct rtentry *rt, void *arg, u_int id)
{
struct rtentry *parent = arg;
+ struct ifnet *ifp;
+
+ ifp = if_get(rt->rt_ifidx);
+
+ /*
+ * This happens when an interface with a RTF_CLONING route is
+ * being detached. In this case it's safe to bail because all
+ * the routes are being purged by rt_if_remove().
+ */
+ if (ifp == NULL)
+ return 0;
- if ((rt->rt_flags & RTF_CLONED) != 0 && (rt->rt_parent == parent ||
- rtequal(rt->rt_parent, parent)))
- rtdeletemsg(rt, NULL, id);
+ if (ISSET(rt->rt_flags, RTF_CLONED) && rtequal(rt->rt_parent, parent)) {
+ rtref(rt);
+ rtdeletemsg(rt, ifp, id);
+ rtfree(rt);
+ }
+
+ if_put(ifp);
return 0;
}
@@ -801,53 +806,20 @@ rt_getifa(struct rt_addrinfo *info, u_in
}
int
-rtrequest_delete(struct rt_addrinfo *info, u_int8_t prio, struct ifnet *ifp,
- struct rtentry **ret_nrt, u_int tableid)
+rt_delete(struct rtentry *rt, struct ifnet *ifp, unsigned int rtableid)
{
- struct rtentry *rt;
- int error;
+ struct sockaddr_in6 sa_mask;
- splsoftassert(IPL_SOFTNET);
+ KASSERT(ifp->if_index == rt->rt_ifidx);
- if (!rtable_exists(tableid))
- return (EAFNOSUPPORT);
- rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
- info->rti_info[RTAX_NETMASK], info->rti_info[RTAX_GATEWAY], prio);
- if (rt == NULL)
- return (ESRCH);
-#ifndef SMALL_KERNEL
- /*
- * If we got multipath routes, we require users to specify
- * a matching gateway.
- */
- if ((rt->rt_flags & RTF_MPATH) &&
- info->rti_info[RTAX_GATEWAY] == NULL) {
- rtfree(rt);
- return (ESRCH);
- }
-#endif
-
- /*
- * Since RTP_LOCAL cannot be set by userland, make
- * sure that local routes are only modified by the
- * kernel.
- */
- if ((rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST)) &&
- prio != RTP_LOCAL) {
- rtfree(rt);
- return (EINVAL);
- }
+ splsoftassert(IPL_SOFTNET);
- error = rtable_delete(tableid, info->rti_info[RTAX_DST],
- info->rti_info[RTAX_NETMASK], rt);
- if (error != 0) {
- rtfree(rt);
+ if (rtable_delete(rtableid, rt_key(rt), rt_plen2mask(rt, &sa_mask), rt))
return (ESRCH);
- }
/* clean up any cloned children */
if ((rt->rt_flags & RTF_CLONING) != 0)
- rtflushclone(tableid, rt);
+ rtflushclone(rtableid, rt);
rtfree(rt->rt_gwroute);
rt->rt_gwroute = NULL;
@@ -857,23 +829,10 @@ rtrequest_delete(struct rt_addrinfo *inf
rt->rt_flags &= ~RTF_UP;
- if (ifp == NULL) {
- ifp = if_get(rt->rt_ifidx);
- KASSERT(ifp != NULL);
- ifp->if_rtrequest(ifp, RTM_DELETE, rt);
- if_put(ifp);
- } else {
- KASSERT(ifp->if_index == rt->rt_ifidx);
- ifp->if_rtrequest(ifp, RTM_DELETE, rt);
- }
+ ifp->if_rtrequest(ifp, RTM_DELETE, rt);
atomic_inc_int(&rttrash);
- if (ret_nrt != NULL)
- *ret_nrt = rt;
- else
- rtfree(rt);
-
return (0);
}
@@ -900,9 +859,50 @@ rtrequest(int req, struct rt_addrinfo *i
info->rti_info[RTAX_NETMASK] = NULL;
switch (req) {
case RTM_DELETE:
- error = rtrequest_delete(info, prio, NULL, ret_nrt, tableid);
- if (error)
+ rt = rtable_lookup(tableid, info->rti_info[RTAX_DST],
+ info->rti_info[RTAX_NETMASK],
+ info->rti_info[RTAX_GATEWAY], prio);
+ if (rt == NULL)
+ return (ESRCH);
+
+#ifndef SMALL_KERNEL
+ /*
+ * If we got multipath routes, we require users to specify
+ * a matching gateway.
+ */
+ if (ISSET(rt->rt_flags, RTF_MPATH) &&
+ info->rti_info[RTAX_GATEWAY] == NULL) {
+ rtfree(rt);
+ return (ESRCH);
+ }
+#endif
+
+ /*
+ * Since RTP_LOCAL cannot be set by userland, make
+ * sure that local routes are only modified by the
+ * kernel.
+ */
+ if (ISSET(rt->rt_flags, RTF_LOCAL|RTF_BROADCAST) &&
+ prio != RTP_LOCAL) {
+ rtfree(rt);
+ return (EINVAL);
+ }
+
+ ifp = if_get(rt->rt_ifidx);
+ KASSERT(ifp != NULL);
+ error = rt_delete(rt, ifp, tableid);
+ if_put(ifp);
+
+ if (error) {
+ rtfree(rt);
return (error);
+ }
+
+ if (ret_nrt != NULL)
+ *ret_nrt = rt;
+ else
+ rtfree(rt);
+
break;
case RTM_RESOLVE:
@@ -1067,7 +1067,7 @@ rtrequest(int req, struct rt_addrinfo *i
if (error != 0 && (crt = rtalloc(ndst, 0, tableid)) != NULL) {
/* overwrite cloned route */
if ((crt->rt_flags & RTF_CLONED) != 0) {
- rtdeletemsg(crt, NULL, tableid);
+ rtdeletemsg(crt, ifp, tableid);
error = rtable_insert(tableid, ndst,
info->rti_info[RTAX_NETMASK],
info->rti_info[RTAX_GATEWAY],
@@ -1235,8 +1235,8 @@ rt_ifa_del(struct ifaddr *ifa, int flags
struct rtentry *rt;
struct mbuf *m = NULL;
struct sockaddr *deldst;
- struct rt_addrinfo info;
- struct sockaddr_rtlabel sa_rl;
+ struct sockaddr *mask = NULL;
+ struct sockaddr *gateway = NULL;
unsigned int rtableid = ifp->if_rdomain;
uint8_t prio = ifp->if_priority + RTP_STATIC;
int error;
@@ -1256,16 +1256,10 @@ rt_ifa_del(struct ifaddr *ifa, int flags
dst = deldst;
}
- memset(&info, 0, sizeof(info));
- info.rti_ifa = ifa;
- info.rti_flags = flags;
- info.rti_info[RTAX_DST] = dst;
if ((flags & RTF_LLINFO) == 0)
- info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
- info.rti_info[RTAX_LABEL] = rtlabel_id2sa(ifp->if_rtlabelid, &sa_rl);
-
+ gateway = ifa->ifa_addr;
if ((flags & RTF_HOST) == 0)
- info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
+ mask = ifa->ifa_netmask;
if (flags & (RTF_LOCAL|RTF_BROADCAST))
prio = RTP_LOCAL;
@@ -1273,13 +1267,32 @@ rt_ifa_del(struct ifaddr *ifa, int flags
if (flags & RTF_CONNECTED)
prio = RTP_CONNECTED;
- error = rtrequest_delete(&info, prio, ifp, &rt, rtableid);
- if (error == 0) {
- rt_sendmsg(rt, RTM_DELETE, rtableid);
- if (flags & RTF_LOCAL)
- rt_sendaddrmsg(rt, RTM_DELADDR);
+ rt = rtable_lookup(rtableid, dst, mask, gateway, prio);
+ if (rt == NULL)
+ return (ESRCH);
+
+#ifndef SMALL_KERNEL
+ /*
+ * If we got multipath routes, we require users to specify
+ * a matching gateway.
+ */
+ if (ISSET(rt->rt_flags, RTF_MPATH) && gateway == NULL) {
rtfree(rt);
+ return (ESRCH);
}
+#endif
+
+ /* Make sure that's the route the caller want to delete. */
+ if (rt->rt_ifidx != ifp->if_index) {
+ rtfree(rt);
+ return (ESRCH);
+ }
+
+ error = rtdeletemsg(rt, ifp, rtableid);
+ if (error == 0 && (flags & RTF_LOCAL))
+ rt_sendaddrmsg(rt, RTM_DELADDR);
+ rtfree(rt);
+
if (m != NULL)
m_free(m);
@@ -1691,9 +1704,12 @@ rt_if_remove_rtdelete(struct rtentry *rt
struct ifnet *ifp = vifp;
if (rt->rt_ifidx == ifp->if_index) {
- int cloning = (rt->rt_flags & RTF_CLONING);
+ int error, cloning = ISSET(rt->rt_flags, RTF_CLONING);
- if (rtdeletemsg(rt, ifp, id) == 0 && cloning)
+ rtref(rt);
+ error = rtdeletemsg(rt, ifp, id);
+ rtfree(rt);
+ if (error == 0 && cloning)
return (EAGAIN);
}
@@ -1751,7 +1767,9 @@ rt_if_linkstate_change(struct rtentry *r
* clone a new route from a better source.
*/
if (rt->rt_flags & RTF_CLONED) {
+ rtref(rt);
rtdeletemsg(rt, ifp, id);
+ rtfree(rt);
return (0);
}
/* take route down */
Index: netinet/ip_icmp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.150
diff -u -p -r1.150 ip_icmp.c
--- netinet/ip_icmp.c 3 Dec 2015 21:11:53 -0000 1.150
+++ netinet/ip_icmp.c 7 Dec 2015 12:40:06 -0000
@@ -1042,19 +1042,21 @@ icmp_mtudisc(struct icmp *icp, u_int rta
void
icmp_mtudisc_timeout(struct rtentry *rt, struct rttimer *r)
{
- if (rt == NULL)
- panic("icmp_mtudisc_timeout: bad route to timeout");
+ struct ifnet *ifp;
+ int s;
- if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
- (RTF_DYNAMIC | RTF_HOST)) {
+ ifp = if_get(rt->rt_ifidx);
+ if (ifp == NULL)
+ return;
+
+ if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
void *(*ctlfunc)(int, struct sockaddr *, u_int, void *);
struct sockaddr_in sin;
- int s;
sin = *satosin(rt_key(rt));
s = splsoftnet();
- rtdeletemsg(rt, NULL, r->rtt_tableid);
+ rtdeletemsg(rt, ifp, r->rtt_tableid);
/* Notify TCP layer of increased Path MTU estimate */
ctlfunc = inetsw[ip_protox[IPPROTO_TCP]].pr_ctlinput;
@@ -1062,9 +1064,12 @@ icmp_mtudisc_timeout(struct rtentry *rt,
(*ctlfunc)(PRC_MTUINC, sintosa(&sin),
r->rtt_tableid, NULL);
splx(s);
- } else
+ } else {
if ((rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
rt->rt_rmx.rmx_mtu = 0;
+ }
+
+ if_put(ifp);
}
/*
@@ -1088,17 +1093,20 @@ icmp_ratelimit(const struct in_addr *dst
void
icmp_redirect_timeout(struct rtentry *rt, struct rttimer *r)
{
- if (rt == NULL)
- panic("icmp_redirect_timeout: bad route to timeout");
+ struct ifnet *ifp;
+ int s;
- if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
- (RTF_DYNAMIC | RTF_HOST)) {
- int s;
+ ifp = if_get(rt->rt_ifidx);
+ if (ifp == NULL)
+ return;
+ if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
s = splsoftnet();
- rtdeletemsg(rt, NULL, r->rtt_tableid);
+ rtdeletemsg(rt, ifp, r->rtt_tableid);
splx(s);
}
+
+ if_put(ifp);
}
int
Index: netinet6/icmp6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.182
diff -u -p -r1.182 icmp6.c
--- netinet6/icmp6.c 3 Dec 2015 21:11:53 -0000 1.182
+++ netinet6/icmp6.c 7 Dec 2015 12:39:28 -0000
@@ -1952,34 +1952,42 @@ icmp6_mtudisc_clone(struct sockaddr *dst
void
icmp6_mtudisc_timeout(struct rtentry *rt, struct rttimer *r)
{
- if (rt == NULL)
- panic("icmp6_mtudisc_timeout: bad route to timeout");
- if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
- (RTF_DYNAMIC | RTF_HOST)) {
- int s;
+ struct ifnet *ifp;
+ int s;
+ ifp = if_get(rt->rt_ifidx);
+ if (ifp == NULL)
+ return;
+
+ if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
s = splsoftnet();
- rtdeletemsg(rt, NULL, r->rtt_tableid);
+ rtdeletemsg(rt, ifp, r->rtt_tableid);
splx(s);
} else {
if (!(rt->rt_rmx.rmx_locks & RTV_MTU))
rt->rt_rmx.rmx_mtu = 0;
}
+
+ if_put(ifp);
}
void
icmp6_redirect_timeout(struct rtentry *rt, struct rttimer *r)
{
- if (rt == NULL)
- panic("icmp6_redirect_timeout: bad route to timeout");
- if ((rt->rt_flags & (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) ==
- (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) {
- int s;
+ struct ifnet *ifp;
+ int s;
+ ifp = if_get(rt->rt_ifidx);
+ if (ifp == NULL)
+ return;
+
+ if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) {
s = splsoftnet();
- rtdeletemsg(rt, NULL, r->rtt_tableid);
+ rtdeletemsg(rt, ifp, r->rtt_tableid);
splx(s);
}
+
+ if_put(ifp);
}
int *icmpv6ctl_vars[ICMPV6CTL_MAXID] = ICMPV6CTL_VARS;