Set inp address, port and rtable together with inpcb hash.
authorbluhm <bluhm@openbsd.org>
Fri, 1 Dec 2023 15:30:46 +0000 (15:30 +0000)
committerbluhm <bluhm@openbsd.org>
Fri, 1 Dec 2023 15:30:46 +0000 (15:30 +0000)
The inpcb hash table is protected by table->inpt_mtx.  The hash is
based on addresses, ports, and routing table.  These fields were
not sychronized with the hash.  Put writes and hash update into the
same critical section.
Move the updates from ip_ctloutput(), ip6_ctloutput(), syn_cache_get(),
tcp_connect(), udp_disconnect() to dedicated inpcb set functions.
There they use the same table mutex as in_pcbrehash().
in_pcbbind(), in_pcbconnect(), and in6_pcbconnect() need more work
and are not included yet.

OK sashan@ mvs@

sys/netinet/in_pcb.c
sys/netinet/in_pcb.h
sys/netinet/ip_output.c
sys/netinet/tcp_input.c
sys/netinet/tcp_usrreq.c
sys/netinet/udp_usrreq.c
sys/netinet6/in6_pcb.c
sys/netinet6/ip6_output.c

index 41cfebc..502354e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in_pcb.c,v 1.279 2023/11/29 18:30:48 bluhm Exp $      */
+/*     $OpenBSD: in_pcb.c,v 1.280 2023/12/01 15:30:46 bluhm Exp $      */
 /*     $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $     */
 
 /*
@@ -268,6 +268,7 @@ in_pcballoc(struct socket *so, struct inpcbtable *table, int wait)
 int
 in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
 {
+       struct inpcbtable *table = inp->inp_table;
        struct socket *so = inp->inp_socket;
        u_int16_t lport = 0;
        int wild = 0;
@@ -341,7 +342,10 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p)
                }
        }
        inp->inp_lport = lport;
+       mtx_enter(&table->inpt_mtx);
        in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+
        return (0);
 }
 
@@ -480,6 +484,7 @@ in_pcbpickport(u_int16_t *lport, const void *laddr, int wild,
 int
 in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
 {
+       struct inpcbtable *table = inp->inp_table;
        struct in_addr ina;
        struct sockaddr_in *sin;
        struct inpcb *t;
@@ -526,7 +531,10 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
        }
        inp->inp_faddr = sin->sin_addr;
        inp->inp_fport = sin->sin_port;
+       mtx_enter(&table->inpt_mtx);
        in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+
 #if NSTOEPLITZ > 0
        inp->inp_flowid = stoeplitz_ip4port(inp->inp_faddr.s_addr,
            inp->inp_laddr.s_addr, inp->inp_fport, inp->inp_lport);
@@ -544,20 +552,7 @@ in_pcbdisconnect(struct inpcb *inp)
                pf_inp_unlink(inp);
        }
 #endif
-       switch (sotopf(inp->inp_socket)) {
-#ifdef INET6
-       case PF_INET6:
-               inp->inp_faddr6 = in6addr_any;
-               break;
-#endif
-       case PF_INET:
-               inp->inp_faddr.s_addr = INADDR_ANY;
-               break;
-       }
-
-       inp->inp_fport = 0;
        inp->inp_flowid = 0;
-       in_pcbrehash(inp);
        if (inp->inp_socket->so_state & SS_NOFDREF)
                in_pcbdetach(inp);
 }
@@ -1044,11 +1039,11 @@ in_pcbrehash(struct inpcb *inp)
 {
        struct inpcbtable *table = inp->inp_table;
 
-       mtx_enter(&table->inpt_mtx);
+       MUTEX_ASSERT_LOCKED(&table->inpt_mtx);
+
        LIST_REMOVE(inp, inp_lhash);
        LIST_REMOVE(inp, inp_hash);
        in_pcbhash_insert(inp);
-       mtx_leave(&table->inpt_mtx);
 }
 
 void
@@ -1266,3 +1261,87 @@ in_pcblookup_listen(struct inpcbtable *table, struct in_addr laddr,
 #endif
        return (inp);
 }
+
+int
+in_pcbset_rtableid(struct inpcb *inp, u_int rtableid)
+{
+       struct inpcbtable *table = inp->inp_table;
+
+       mtx_enter(&table->inpt_mtx);
+       if (inp->inp_lport) {
+               mtx_leave(&table->inpt_mtx);
+               return (EBUSY);
+       }
+       inp->inp_rtableid = rtableid;
+       in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+
+       return (0);
+}
+
+void
+in_pcbset_laddr(struct inpcb *inp, const struct sockaddr *sa, u_int rtableid)
+{
+       struct inpcbtable *table = inp->inp_table;
+
+       mtx_enter(&table->inpt_mtx);
+       inp->inp_rtableid = rtableid;
+#ifdef INET6
+       if (ISSET(inp->inp_flags, INP_IPV6)) {
+               const struct sockaddr_in6 *sin6;
+
+               KASSERT(sa->sa_family == AF_INET6);
+               sin6 = satosin6_const(sa);
+               inp->inp_lport = sin6->sin6_port;
+               inp->inp_laddr6 = sin6->sin6_addr;
+       } else
+#endif
+       {
+               const struct sockaddr_in *sin;
+
+               KASSERT(sa->sa_family == AF_INET);
+               sin = satosin_const(sa);
+               inp->inp_lport = sin->sin_port;
+               inp->inp_laddr = sin->sin_addr;
+       }
+       in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+}
+
+void
+in_pcbunset_faddr(struct inpcb *inp)
+{
+       struct inpcbtable *table = inp->inp_table;
+
+       mtx_enter(&table->inpt_mtx);
+#ifdef INET6
+       if (ISSET(inp->inp_flags, INP_IPV6))
+               inp->inp_faddr6 = in6addr_any;
+       else
+#endif
+               inp->inp_faddr.s_addr = INADDR_ANY;
+       inp->inp_fport = 0;
+       in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+}
+
+void
+in_pcbunset_laddr(struct inpcb *inp)
+{
+       struct inpcbtable *table = inp->inp_table;
+
+       mtx_enter(&table->inpt_mtx);
+#ifdef INET6
+       if (ISSET(inp->inp_flags, INP_IPV6)) {
+               inp->inp_faddr6 = in6addr_any;
+               inp->inp_laddr6 = in6addr_any;
+       } else
+#endif
+       {
+               inp->inp_faddr.s_addr = INADDR_ANY;
+               inp->inp_laddr.s_addr = INADDR_ANY;
+       }
+       inp->inp_fport = 0;
+       in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+}
index 0bb4bac..0a3bb35 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in_pcb.h,v 1.140 2023/11/29 18:30:48 bluhm Exp $      */
+/*     $OpenBSD: in_pcb.h,v 1.141 2023/12/01 15:30:46 bluhm Exp $      */
 /*     $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $     */
 
 /*
@@ -335,5 +335,10 @@ void       in6_pcbnotify(struct inpcbtable *, struct sockaddr_in6 *,
 int    in6_selecthlim(struct inpcb *);
 int    in_pcbpickport(u_int16_t *, const void *, int, const struct inpcb *,
            struct proc *);
+int    in_pcbset_rtableid(struct inpcb *, u_int);
+void   in_pcbset_laddr(struct inpcb *, const struct sockaddr *, u_int);
+void   in_pcbunset_faddr(struct inpcb *);
+void   in_pcbunset_laddr(struct inpcb *);
+
 #endif /* _KERNEL */
 #endif /* _NETINET_IN_PCB_H_ */
index fb9cc84..87cf83c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_output.c,v 1.391 2023/11/26 22:08:10 bluhm Exp $   */
+/*     $OpenBSD: ip_output.c,v 1.392 2023/12/01 15:30:47 bluhm Exp $   */
 /*     $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $  */
 
 /*
@@ -1082,12 +1082,7 @@ ip_ctloutput(int op, struct socket *so, int level, int optname,
                                error = EINVAL;
                                break;
                        }
-                       if (inp->inp_lport) {
-                               error = EBUSY;
-                               break;
-                       }
-                       inp->inp_rtableid = rtid;
-                       in_pcbrehash(inp);
+                       error = in_pcbset_rtableid(inp, rtid);
                        break;
                case IP_PIPEX:
                        if (m != NULL && m->m_len == sizeof(int))
index 349cadf..ef28525 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tcp_input.c,v 1.396 2023/11/30 10:21:56 bluhm Exp $   */
+/*     $OpenBSD: tcp_input.c,v 1.397 2023/12/01 15:30:47 bluhm Exp $   */
 /*     $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $  */
 
 /*
@@ -3489,6 +3489,7 @@ syn_cache_get(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
        struct tcpcb *tp = NULL;
        struct mbuf *am;
        struct socket *oso;
+       u_int rtableid;
 
        NET_ASSERT_LOCKED();
 
@@ -3553,37 +3554,25 @@ syn_cache_get(struct sockaddr *src, struct sockaddr *dst, struct tcphdr *th,
 #endif /* INET6 */
        {
                inp->inp_ip.ip_ttl = oldinp->inp_ip.ip_ttl;
+               inp->inp_options = ip_srcroute(m);
+               if (inp->inp_options == NULL) {
+                       inp->inp_options = sc->sc_ipopts;
+                       sc->sc_ipopts = NULL;
+               }
        }
 
+       /* inherit rtable from listening socket */
+       rtableid = sc->sc_rtableid;
 #if NPF > 0
        if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
                struct pf_divert *divert;
 
                divert = pf_find_divert(m);
                KASSERT(divert != NULL);
-               inp->inp_rtableid = divert->rdomain;
-       } else
-#endif
-       /* inherit rtable from listening socket */
-       inp->inp_rtableid = sc->sc_rtableid;
-
-       inp->inp_lport = th->th_dport;
-       switch (src->sa_family) {
-#ifdef INET6
-       case AF_INET6:
-               inp->inp_laddr6 = satosin6(dst)->sin6_addr;
-               break;
-#endif /* INET6 */
-       case AF_INET:
-               inp->inp_laddr = satosin(dst)->sin_addr;
-               inp->inp_options = ip_srcroute(m);
-               if (inp->inp_options == NULL) {
-                       inp->inp_options = sc->sc_ipopts;
-                       sc->sc_ipopts = NULL;
-               }
-               break;
+               rtableid = divert->rdomain;
        }
-       in_pcbrehash(inp);
+#endif
+       in_pcbset_laddr(inp, dst, rtableid);
 
        /*
         * Give the new socket our cached route reference.
index e7f9767..5f06150 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tcp_usrreq.c,v 1.225 2023/12/01 14:08:03 bluhm Exp $  */
+/*     $OpenBSD: tcp_usrreq.c,v 1.226 2023/12/01 15:30:47 bluhm Exp $  */
 /*     $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */
 
 /*
@@ -656,6 +656,7 @@ tcp_connect(struct socket *so, struct mbuf *nam)
 
        tp->t_template = tcp_template(tp);
        if (tp->t_template == 0) {
+               in_pcbunset_faddr(inp);
                in_pcbdisconnect(inp);
                error = ENOBUFS;
                goto out;
index 8369391..7ad8524 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: udp_usrreq.c,v 1.311 2023/12/01 14:08:03 bluhm Exp $  */
+/*     $OpenBSD: udp_usrreq.c,v 1.312 2023/12/01 15:30:47 bluhm Exp $  */
 /*     $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
 
 /*
@@ -1184,14 +1184,7 @@ udp_disconnect(struct socket *so)
                if (inp->inp_faddr.s_addr == INADDR_ANY)
                        return (ENOTCONN);
        }
-
-#ifdef INET6
-       if (inp->inp_flags & INP_IPV6)
-               inp->inp_laddr6 = in6addr_any;
-       else
-#endif /* INET6 */
-               inp->inp_laddr.s_addr = INADDR_ANY;
-
+       in_pcbunset_laddr(inp);
        in_pcbdisconnect(inp);
        so->so_state &= ~SS_ISCONNECTED;                /* XXX */
 
index 6529063..83b6caa 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6_pcb.c,v 1.127 2023/12/01 14:08:04 bluhm Exp $     */
+/*     $OpenBSD: in6_pcb.c,v 1.128 2023/12/01 15:30:47 bluhm Exp $     */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -245,6 +245,7 @@ in6_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in6 *sin6, int wild,
 int
 in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
 {
+       struct inpcbtable *table = inp->inp_table;
        const struct in6_addr *in6a;
        struct sockaddr_in6 *sin6;
        struct inpcb *t;
@@ -312,6 +313,10 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
        }
        inp->inp_faddr6 = sin6->sin6_addr;
        inp->inp_fport = sin6->sin6_port;
+       mtx_enter(&table->inpt_mtx);
+       in_pcbrehash(inp);
+       mtx_leave(&table->inpt_mtx);
+
        inp->inp_flowinfo &= ~IPV6_FLOWLABEL_MASK;
        if (ip6_auto_flowlabel)
                inp->inp_flowinfo |=
@@ -320,7 +325,6 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
        inp->inp_flowid = stoeplitz_ip6port(&inp->inp_faddr6,
            &inp->inp_laddr6, inp->inp_fport, inp->inp_lport);
 #endif
-       in_pcbrehash(inp);
        return (0);
 }
 
index 96e0bea..8366d83 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_output.c,v 1.281 2023/11/28 13:23:20 bluhm Exp $  */
+/*     $OpenBSD: ip6_output.c,v 1.282 2023/12/01 15:30:47 bluhm Exp $  */
 /*     $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $    */
 
 /*
@@ -1381,12 +1381,7 @@ do { \
                                error = EINVAL;
                                break;
                        }
-                       if (inp->inp_lport) {
-                               error = EBUSY;
-                               break;
-                       }
-                       inp->inp_rtableid = rtid;
-                       in_pcbrehash(inp);
+                       error = in_pcbset_rtableid(inp, rtid);
                        break;
                case IPV6_PIPEX:
                        if (m != NULL && m->m_len == sizeof(int))