From 92d8381dd544a8237b3fd68c4e7fce9bd0903fb2 Mon Sep 17 00:00:00 2001 From: tanimura Date: Mon, 20 May 2002 05:41:09 +0000 Subject: Lock down a socket, milestone 1. o Add a mutex (sb_mtx) to struct sockbuf. This protects the data in a socket buffer. The mutex in the receive buffer also protects the data in struct socket. o Determine the lock strategy for each members in struct socket. o Lock down the following members: - so_count - so_options - so_linger - so_state o Remove *_locked() socket APIs. Make the following socket APIs touching the members above now require a locked socket: - sodisconnect() - soisconnected() - soisconnecting() - soisdisconnected() - soisdisconnecting() - sofree() - soref() - sorele() - sorwakeup() - sotryfree() - sowakeup() - sowwakeup() Reviewed by: alfred --- sys/netinet6/icmp6.c | 10 +++++-- sys/netinet6/in6_pcb.c | 68 ++++++++++++++++++++++++++++++++-------------- sys/netinet6/in6_src.c | 2 ++ sys/netinet6/ip6_input.c | 5 +++- sys/netinet6/ip6_mroute.c | 2 ++ sys/netinet6/raw_ip6.c | 45 +++++++++++++++++++++++++----- sys/netinet6/udp6_usrreq.c | 57 ++++++++++++++++++++++++++++++++------ 7 files changed, 151 insertions(+), 38 deletions(-) (limited to 'sys/netinet6') diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 38332b2..f9994c6 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1941,8 +1941,11 @@ icmp6_rip6_input(mp, off) if (opts) { m_freem(opts); } - } else + } else { + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); + } opts = NULL; } } @@ -1958,8 +1961,11 @@ icmp6_rip6_input(mp, off) m_freem(m); if (opts) m_freem(opts); - } else + } else { + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); + } } else { m_freem(m); ip6stat.ip6s_delivered--; diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 09a5c29..ab37249 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -124,14 +124,19 @@ in6_pcbbind(inp, nam, td) struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; - int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); + int wild = 0, reuseport; + SOCK_LOCK(so); + reuseport = (so->so_options & SO_REUSEPORT); + SOCK_UNLOCK(so); if (!in6_ifaddr) /* XXX broken! */ return (EADDRNOTAVAIL); if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) return(EINVAL); + SOCK_LOCK(so); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; + SOCK_UNLOCK(so); if (nam) { sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof(*sin6)) @@ -157,8 +162,10 @@ in6_pcbbind(inp, nam, td) * and a multicast address is bound on both * new and duplicated sockets. */ + SOCK_LOCK(so); if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; + SOCK_UNLOCK(so); } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; @@ -190,14 +197,19 @@ in6_pcbbind(inp, nam, td) t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, INPLOOKUP_WILDCARD); - if (t && - (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || - !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || - (t->inp_socket->so_options & - SO_REUSEPORT) == 0) && - (so->so_cred->cr_uid != - t->inp_socket->so_cred->cr_uid)) - return (EADDRINUSE); + if (t != NULL) { + SOCK_LOCK(t->inp_socket); + if ((!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || + !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || + (t->inp_socket->so_options & + SO_REUSEPORT) == 0) && + (so->so_cred->cr_uid != + t->inp_socket->so_cred->cr_uid)) { + SOCK_UNLOCK(t->inp_socket); + return (EADDRINUSE); + } + SOCK_UNLOCK(t->inp_socket); + } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -218,8 +230,14 @@ in6_pcbbind(inp, nam, td) } t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, wild); - if (t && (reuseport & t->inp_socket->so_options) == 0) - return(EADDRINUSE); + if (t != NULL) { + SOCK_LOCK(t->inp_socket); + if ((reuseport & t->inp_socket->so_options) == 0) { + SOCK_UNLOCK(t->inp_socket); + return(EADDRINUSE); + } + SOCK_UNLOCK(t->inp_socket); + } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -227,14 +245,19 @@ in6_pcbbind(inp, nam, td) in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local(pcbinfo, sin.sin_addr, lport, wild); - if (t && - (reuseport & t->inp_socket->so_options) - == 0 && - (ntohl(t->inp_laddr.s_addr) - != INADDR_ANY || - INP_SOCKAF(so) == - INP_SOCKAF(t->inp_socket))) - return (EADDRINUSE); + if (t != NULL) { + SOCK_LOCK(t->inp_socket); + if ((reuseport & t->inp_socket->so_options) + == 0 && + (ntohl(t->inp_laddr.s_addr) + != INADDR_ANY || + INP_SOCKAF(so) == + INP_SOCKAF(t->inp_socket))) { + SOCK_UNLOCK(t->inp_socket); + return (EADDRINUSE); + } + SOCK_UNLOCK(t->inp_socket); + } } } inp->in6p_laddr = sin6->sin6_addr; @@ -589,8 +612,12 @@ in6_pcbdisconnect(inp) /* clear flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; in_pcbrehash(inp); - if (inp->inp_socket->so_state & SS_NOFDREF) + SOCK_LOCK(inp->inp_socket); + if (inp->inp_socket->so_state & SS_NOFDREF) { + SOCK_UNLOCK(inp->inp_socket); in6_pcbdetach(inp); + } else + SOCK_UNLOCK(inp->inp_socket); } void @@ -607,6 +634,7 @@ in6_pcbdetach(inp) inp->inp_gencnt = ++ipi->ipi_gencnt; in_pcbremlists(inp); sotoinpcb(so) = 0; + SOCK_LOCK(so); sotryfree(so); if (inp->in6p_options) diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 3dd2212..910e66d 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -338,9 +338,11 @@ in6_pcbsetport(laddr, inp, td) int count, error = 0, wild = 0; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + SOCK_LOCK(so); /* XXX: this is redundant when called from in6_pcbbind */ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = INPLOOKUP_WILDCARD; + SOCK_UNLOCK(so); inp->inp_flags |= INP_ANONPORT; diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 16df5bb..f237cdf 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1147,16 +1147,19 @@ ip6_savecontrol(in6p, mp, ip6, m) privileged++; #ifdef SO_TIMESTAMP + SOCK_LOCK(in6p->in6p_socket); if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) { struct timeval tv; + SOCK_UNLOCK(in6p->in6p_socket); microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), SCM_TIMESTAMP, SOL_SOCKET); if (*mp) { mp = &(*mp)->m_next; } - } + } else + SOCK_UNLOCK(in6p->in6p_socket); #endif /* RFC 2292 sec. 5 */ diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index 9af7e81..513e307 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -892,7 +892,9 @@ socket_send(s, mm, src) if (sbappendaddr(&s->so_rcv, (struct sockaddr *)src, mm, (struct mbuf *)0) != 0) { + SOCK_LOCK(s); sorwakeup(s); + SOCK_UNLOCK(s); return 0; } } diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 2dcec0c..5fbd7c9 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -182,9 +182,16 @@ rip6_input(mp, offp, proto) } else #endif /*IPSEC*/ if (n) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) + if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, n); + else { + SOCK_LOCK(last->in6p_socket); + if (last->in6p_socket->so_options & SO_TIMESTAMP) { + SOCK_UNLOCK(last->in6p_socket); + ip6_savecontrol(last, &opts, ip6, n); + } else + SOCK_UNLOCK(last->in6p_socket); + } /* strip intermediate headers */ m_adj(n, *offp); if (sbappendaddr(&last->in6p_socket->so_rcv, @@ -194,8 +201,11 @@ rip6_input(mp, offp, proto) if (opts) m_freem(opts); rip6stat.rip6s_fullsock++; - } else + } else { + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); + } opts = NULL; } } @@ -213,9 +223,16 @@ rip6_input(mp, offp, proto) } else #endif /*IPSEC*/ if (last) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) + if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, m); + else { + SOCK_LOCK(last->in6p_socket); + if (last->in6p_socket->so_options & SO_TIMESTAMP) { + SOCK_UNLOCK(last->in6p_socket); + ip6_savecontrol(last, &opts, ip6, m); + } else + SOCK_UNLOCK(last->in6p_socket); + } /* strip intermediate headers */ m_adj(m, *offp); if (sbappendaddr(&last->in6p_socket->so_rcv, @@ -224,8 +241,11 @@ rip6_input(mp, offp, proto) if (opts) m_freem(opts); rip6stat.rip6s_fullsock++; - } else + } else { + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); + } } else { rip6stat.rip6s_nosock++; if (m->m_flags & M_MCAST) @@ -591,7 +611,9 @@ rip6_detach(struct socket *so) static int rip6_abort(struct socket *so) { + SOCK_LOCK(so); soisdisconnected(so); + SOCK_UNLOCK(so); return rip6_detach(so); } @@ -600,8 +622,12 @@ rip6_disconnect(struct socket *so) { struct inpcb *inp = sotoinpcb(so); - if ((so->so_state & SS_ISCONNECTED) == 0) + SOCK_LOCK(so); + if ((so->so_state & SS_ISCONNECTED) == 0) { + SOCK_UNLOCK(so); return ENOTCONN; + } + SOCK_UNLOCK(so); inp->in6p_faddr = in6addr_any; return rip6_abort(so); } @@ -669,7 +695,9 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) return (error ? error : EADDRNOTAVAIL); inp->in6p_laddr = *in6a; inp->in6p_faddr = addr->sin6_addr; + SOCK_LOCK(so); soisconnected(so); + SOCK_UNLOCK(so); return 0; } @@ -689,7 +717,9 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct sockaddr_in6 *dst; /* always copy sockaddr to avoid overwrites */ + SOCK_LOCK(so); if (so->so_state & SS_ISCONNECTED) { + SOCK_UNLOCK(so); if (nam) { m_freem(m); return EISCONN; @@ -702,6 +732,7 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, sizeof(struct in6_addr)); dst = &tmp; } else { + SOCK_UNLOCK(so); if (nam == NULL) { m_freem(m); return ENOTCONN; diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 20913e6..0e58be5 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -269,10 +269,18 @@ udp6_input(mp, offp, proto) * and m_copy() will copy M_PKTHDR * only if offset is 0. */ - if (last->in6p_flags & IN6P_CONTROLOPTS - || last->in6p_socket->so_options & SO_TIMESTAMP) + if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, n); + else { + SOCK_LOCK(last->in6p_socket); + if (last->in6p_socket->so_options & SO_TIMESTAMP) { + SOCK_UNLOCK(last->in6p_socket); + ip6_savecontrol(last, &opts, + ip6, n); + } else + SOCK_UNLOCK(last->in6p_socket); + } m_adj(n, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, @@ -282,8 +290,11 @@ udp6_input(mp, offp, proto) if (opts) m_freem(opts); udpstat.udps_fullsock++; - } else + } else { + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); + } opts = NULL; } } @@ -296,9 +307,13 @@ udp6_input(mp, offp, proto) * port. It assumes that an application will never * clear these options after setting them. */ + SOCK_LOCK(last->in6p_socket); if ((last->in6p_socket->so_options & - (SO_REUSEPORT|SO_REUSEADDR)) == 0) + (SO_REUSEPORT|SO_REUSEADDR)) == 0) { + SOCK_UNLOCK(last->in6p_socket); break; + } else + SOCK_UNLOCK(last->in6p_socket); } if (last == NULL) { @@ -320,9 +335,16 @@ udp6_input(mp, offp, proto) goto bad; } #endif /* IPSEC */ - if (last->in6p_flags & IN6P_CONTROLOPTS - || last->in6p_socket->so_options & SO_TIMESTAMP) + if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, m); + else { + SOCK_LOCK(last->in6p_socket); + if (last->in6p_socket->so_options & SO_TIMESTAMP) { + SOCK_UNLOCK(last->in6p_socket); + ip6_savecontrol(last, &opts, ip6, m); + } else + SOCK_UNLOCK(last->in6p_socket); + } m_adj(m, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, @@ -331,7 +353,9 @@ udp6_input(mp, offp, proto) udpstat.udps_fullsock++; goto bad; } + SOCK_LOCK(last->in6p_socket); sorwakeup(last->in6p_socket); + SOCK_UNLOCK(last->in6p_socket); return IPPROTO_DONE; } /* @@ -375,9 +399,16 @@ udp6_input(mp, offp, proto) */ init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; - if (in6p->in6p_flags & IN6P_CONTROLOPTS - || in6p->in6p_socket->so_options & SO_TIMESTAMP) + if (in6p->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(in6p, &opts, ip6, m); + else { + SOCK_LOCK(in6p->in6p_socket); + if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { + SOCK_UNLOCK(in6p->in6p_socket); + ip6_savecontrol(in6p, &opts, ip6, m); + } else + SOCK_UNLOCK(in6p->in6p_socket); + } m_adj(m, off + sizeof(struct udphdr)); if (sbappendaddr(&in6p->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, @@ -385,7 +416,9 @@ udp6_input(mp, offp, proto) udpstat.udps_fullsock++; goto bad; } + SOCK_LOCK(in6p->in6p_socket); sorwakeup(in6p->in6p_socket); + SOCK_UNLOCK(in6p->in6p_socket); return IPPROTO_DONE; bad: if (m) @@ -509,7 +542,9 @@ udp6_abort(struct socket *so) inp = sotoinpcb(so); if (inp == 0) return EINVAL; /* ??? possible? panic instead? */ + SOCK_LOCK(so); soisdisconnected(so); + SOCK_UNLOCK(so); s = splnet(); in6_pcbdetach(inp); splx(s); @@ -614,7 +649,9 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) if (error == 0) { inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; + SOCK_LOCK(so); soisconnected(so); + SOCK_UNLOCK(so); } return error; } @@ -629,7 +666,9 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; } + SOCK_LOCK(so); soisconnected(so); + SOCK_UNLOCK(so); } return error; } @@ -673,7 +712,9 @@ udp6_disconnect(struct socket *so) in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; splx(s); + SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ + SOCK_UNLOCK(so); return 0; } -- cgit v1.1