Index: sys/netinet6/ip6_output.c =================================================================== --- sys/netinet6/ip6_output.c (revision 239383) +++ sys/netinet6/ip6_output.c (working copy) @@ -130,10 +130,10 @@ struct ip6_exthdrs { struct mbuf *ip6e_dest2; }; -static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **, - struct ucred *, int)); -static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, - struct socket *, struct sockopt *)); +static int ip6_pcbopt __P((struct inpcb *, int, u_char *, int, struct ucred *, + int)); +static int ip6_pcbopts __P((struct inpcb *, struct mbuf *, struct socket *, + struct sockopt *)); static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); static int ip6_setpktopt __P((int, u_char *, int, struct ip6_pktopts *, struct ucred *, int, int, int)); @@ -1544,8 +1544,9 @@ ip6_ctloutput(struct socket *so, struct sockopt *s error = soopt_mcopyin(sopt, m); /* XXX */ if (error != 0) break; - error = ip6_pcbopts(&in6p->in6p_outputopts, - m, so, sopt); + INP_WLOCK(in6p); + error = ip6_pcbopts(in6p, m, so, sopt); + INP_WUNLOCK(in6p); m_freem(m); /* XXX */ break; } @@ -1645,18 +1646,17 @@ do { \ case IPV6_HOPLIMIT: { - struct ip6_pktopts **optp; - /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } - optp = &in6p->in6p_outputopts; - error = ip6_pcbopt(IPV6_HOPLIMIT, + INP_WLOCK(in6p); + error = ip6_pcbopt(in6p, IPV6_HOPLIMIT, (u_char *)&optval, sizeof(optval), - optp, (td != NULL) ? td->td_ucred : - NULL, uproto); + td != NULL ? td->td_ucred : NULL, + uproto); + INP_WUNLOCK(in6p); break; } @@ -1768,12 +1768,12 @@ do { \ if (error) break; { - struct ip6_pktopts **optp; - optp = &in6p->in6p_outputopts; - error = ip6_pcbopt(optname, + INP_WLOCK(in6p); + error = ip6_pcbopt(in6p, optname, (u_char *)&optval, sizeof(optval), - optp, (td != NULL) ? td->td_ucred : - NULL, uproto); + td != NULL ? td->td_ucred : NULL, + uproto); + INP_WUNLOCK(in6p); break; } @@ -1855,10 +1855,12 @@ do { \ break; optlen = sopt->sopt_valsize; optbuf = optbuf_storage; + INP_WLOCK(in6p); optp = &in6p->in6p_outputopts; - error = ip6_pcbopt(optname, optbuf, optlen, - optp, (td != NULL) ? td->td_ucred : NULL, + error = ip6_pcbopt(in6p, optname, optbuf, + optlen, td != NULL ? td->td_ucred : NULL, uproto); + INP_WUNLOCK(in6p); break; } #undef OPTSET @@ -2108,8 +2110,10 @@ do { \ case IPV6_DONTFRAG: case IPV6_USE_MIN_MTU: case IPV6_PREFER_TEMPADDR: + INP_RLOCK(in6p); error = ip6_getpcbopt(in6p->in6p_outputopts, optname, sopt); + INP_RUNLOCK(in6p); break; case IPV6_MULTICAST_IF: @@ -2236,15 +2240,18 @@ ip6_raw_ctloutput(struct socket *so, struct sockop * specifying behavior of outgoing packets. */ static int -ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, - struct socket *so, struct sockopt *sopt) +ip6_pcbopts(struct inpcb *in6p, struct mbuf *m, struct socket *so, + struct sockopt *sopt) { - struct ip6_pktopts *opt = *pktopt; - int error = 0; - struct thread *td = sopt->sopt_td; + struct ip6_pktopts *opt; + struct thread *td; + int error; - /* turn off any old options. */ + INP_WLOCK_ASSERT(in6p); +clear: + opt = in6p->in6p_outputopts; if (opt) { + /* Turn off any old options. */ #ifdef DIAGNOSTIC if (opt->ip6po_pktinfo || opt->ip6po_nexthop || opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || @@ -2252,10 +2259,22 @@ static int printf("ip6_pcbopts: all specified options are cleared.\n"); #endif ip6_clearpktopts(opt, -1); - } else + } else { + INP_WUNLOCK(in6p); opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); - *pktopt = NULL; + INP_WLOCK(in6p); + if (in6p->in6p_outputopts != NULL) { + /* + * The options were initilized by another process while + * we weren't holding the lock. + */ + free(opt, M_IP6OPT); + goto clear; + } + } + in6p->in6p_outputopts = NULL; + if (!m || m->m_len == 0) { /* * Only turning off any previous options, regardless of @@ -2266,13 +2285,14 @@ static int } /* set options specified by user. */ + td = sopt->sopt_td; if ((error = ip6_setpktopts(m, opt, NULL, (td != NULL) ? td->td_ucred : NULL, so->so_proto->pr_protocol)) != 0) { ip6_clearpktopts(opt, -1); /* XXX: discard all options */ free(opt, M_IP6OPT); return (error); } - *pktopt = opt; + in6p->in6p_outputopts = opt; return (0); } @@ -2292,17 +2312,28 @@ ip6_initpktopts(struct ip6_pktopts *opt) } static int -ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, +ip6_pcbopt(struct inpcb *in6p, int optname, u_char *buf, int len, struct ucred *cred, int uproto) { struct ip6_pktopts *opt; - if (*pktopt == NULL) { - *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT, - M_WAITOK); - ip6_initpktopts(*pktopt); + INP_WLOCK_ASSERT(in6p); + opt = in6p->in6p_outputopts; + if (opt == NULL) { + INP_WUNLOCK(in6p); + opt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT, M_WAITOK); + INP_WLOCK(in6p); + if (in6p->in6p_outputopts != NULL) { + /* + * The options were initilized by another process while + * we weren't holding the lock. + */ + free(opt, M_IP6OPT); + } else { + ip6_initpktopts(opt); + in6p->in6p_outputopts = opt; + } } - opt = *pktopt; return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto)); }