On Mon, Jun 08, 2020 at 12:49:05PM +0300, Vitaliy Makkoveev wrote:
> 
> 
> > On 8 Jun 2020, at 11:34, Martin Pieuchot <m...@openbsd.org> wrote:
> > 
> > On 29/05/20(Fri) 13:22, Vitaliy Makkoveev wrote:
> >> This time pppx_add_session() has mixed initialisation order. It starts
> >> to initialise pipex(4) session, then initialises `ifnet', then links
> >> pipex(4) session, then continue to initialize `ifnet'.
> >> pppx_add_session() can sleep and pppx_if_start() can start to work with
> >> unlinked pipex(4) session.
> >> 
> >> Also pppx_if_destroy() can sleep and pppx_if_start() can access to this
> >> `pxi' with unlinked session. pppx_if_start() has checking of
> >> `IFF_RUNNING' flag but it's usesless.
> >> 
> >> Diff below does pppx_add_session() reordering. At first we initilize
> >> and attach `ifnet', at second we initialize and link pipex(4) session
> >> and at last we set `IFF_RUNNING' flag on `ifnet.
> > 
> > The fact that you have to call if_detach() if a field isn't validated
> > makes me wonder if this refactoring is better than the existing logic.
> 
> Except "req->pr_timeout_sec !=0” checking all userland input checks
> were copypasted prom pipex_add_session(). But pppx_add_session()
> inserts pppx(4) related code between checks and session initialization.
> So I decided to keep all pipex(4) related code together for further
> deduplication. Also we want to initialise `ifnet’ before session
> linking.
> 
> There is another way to rewrite pppx_add_session() and
> pipex_add_session(). We can split pipex_add_session() to two
> functions: pipex_init_session() and pipex_setup_session(). The first
> will do checks and insert new session with PIPEX_STATE_INITIAL state.
> The second will do the rest. So we can do checks before `ifnet’
> dances in ppppx_add_session(). This time PIPEX_STATE_INITIAL declared
> but not used, at least pipex_lookup_by_session_id() should be
> refactored before.
> 
> Is this direction more acceptable?

Diff below illustrates this.

1. New function pipex_init_session() validates request and if it's
accepted creates new pipex session but keeps it unlinked. This session
is only inserted to `session_list'. Unlinked session can't be accessed
by stack.
2. New function pipex_link_session() connects session to stack.
3. New function pipex_unlink_session() disconnects session from the
stack.

Duplicated pipex(4) code from pppx_add_session was replaced by
pipex_init_session() and pipex_link_session(). pppx(4) related code is
not reordered.

Duplicated pipex(4) code from pppx_if_destroy() was replaced by
pipex_unlink_session().

Index: sys/net/if_pppx.c
===================================================================
RCS file: /cvs/src/sys/net/if_pppx.c,v
retrieving revision 1.87
diff -u -p -r1.87 if_pppx.c
--- sys/net/if_pppx.c   29 May 2020 06:51:52 -0000      1.87
+++ sys/net/if_pppx.c   8 Jun 2020 12:07:09 -0000
@@ -659,14 +659,10 @@ pppx_add_session(struct pppx_dev *pxd, s
 {
        struct pppx_if *pxi;
        struct pipex_session *session;
-       struct pipex_hash_head *chain;
        struct ifnet *ifp;
        int unit, error = 0;
        struct in_ifaddr *ia;
        struct sockaddr_in ifaddr;
-#ifdef PIPEX_PPPOE
-       struct ifnet *over_ifp = NULL;
-#endif
 
        /*
         * XXX: As long as `session' is allocated as part of a `pxi'
@@ -676,151 +672,20 @@ pppx_add_session(struct pppx_dev *pxd, s
        if (req->pr_timeout_sec != 0)
                return (EINVAL);
 
-       switch (req->pr_protocol) {
-#ifdef PIPEX_PPPOE
-       case PIPEX_PROTO_PPPOE:
-               over_ifp = ifunit(req->pr_proto.pppoe.over_ifname);
-               if (over_ifp == NULL)
-                       return (EINVAL);
-               if (req->pr_peer_address.ss_family != AF_UNSPEC)
-                       return (EINVAL);
-               break;
-#endif
-#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
-       case PIPEX_PROTO_PPTP:
-       case PIPEX_PROTO_L2TP:
-               switch (req->pr_peer_address.ss_family) {
-               case AF_INET:
-                       if (req->pr_peer_address.ss_len != sizeof(struct 
sockaddr_in))
-                               return (EINVAL);
-                       break;
-#ifdef INET6
-               case AF_INET6:
-                       if (req->pr_peer_address.ss_len != sizeof(struct 
sockaddr_in6))
-                               return (EINVAL);
-                       break;
-#endif
-               default:
-                       return (EPROTONOSUPPORT);
-               }
-               if (req->pr_peer_address.ss_family !=
-                   req->pr_local_address.ss_family ||
-                   req->pr_peer_address.ss_len !=
-                   req->pr_local_address.ss_len)
-                       return (EINVAL);
-               break;
-#endif /* defined(PIPEX_PPTP) || defined(PIPEX_L2TP) */
-       default:
-               return (EPROTONOSUPPORT);
-       }
-
-       session = pipex_lookup_by_session_id(req->pr_protocol,
-           req->pr_session_id);
-       if (session)
-               return (EEXIST);
-
        pxi = pool_get(pppx_if_pl, PR_WAITOK | PR_ZERO);
 
        session = &pxi->pxi_session;
        ifp = &pxi->pxi_if;
 
        /* fake a pipex interface context */
-       session->pipex_iface = &pxi->pxi_ifcontext;
-       session->pipex_iface->ifnet_this = ifp;
-       session->pipex_iface->pipexmode = PIPEX_ENABLED;
-
-       /* setup session */
-       session->state = PIPEX_STATE_OPENED;
-       session->protocol = req->pr_protocol;
-       session->session_id = req->pr_session_id;
-       session->peer_session_id = req->pr_peer_session_id;
-       session->peer_mru = req->pr_peer_mru;
-       session->timeout_sec = req->pr_timeout_sec;
-       session->ppp_flags = req->pr_ppp_flags;
-       session->ppp_id = req->pr_ppp_id;
-
-       session->ip_forward = 1;
-
-       session->ip_address.sin_family = AF_INET;
-       session->ip_address.sin_len = sizeof(struct sockaddr_in);
-       session->ip_address.sin_addr = req->pr_ip_address;
-
-       session->ip_netmask.sin_family = AF_INET;
-       session->ip_netmask.sin_len = sizeof(struct sockaddr_in);
-       session->ip_netmask.sin_addr = req->pr_ip_netmask;
-
-       if (session->ip_netmask.sin_addr.s_addr == 0L)
-               session->ip_netmask.sin_addr.s_addr = 0xffffffffL;
-       session->ip_address.sin_addr.s_addr &=
-           session->ip_netmask.sin_addr.s_addr;
-
-       if (req->pr_peer_address.ss_len > 0)
-               memcpy(&session->peer, &req->pr_peer_address,
-                   MIN(req->pr_peer_address.ss_len, sizeof(session->peer)));
-       if (req->pr_local_address.ss_len > 0)
-               memcpy(&session->local, &req->pr_local_address,
-                   MIN(req->pr_local_address.ss_len, sizeof(session->local)));
-#ifdef PIPEX_PPPOE
-       if (req->pr_protocol == PIPEX_PROTO_PPPOE)
-               session->proto.pppoe.over_ifidx = over_ifp->if_index;
-#endif
-#ifdef PIPEX_PPTP
-       if (req->pr_protocol == PIPEX_PROTO_PPTP) {
-               struct pipex_pptp_session *sess_pptp = &session->proto.pptp;
-
-               sess_pptp->snd_gap = 0;
-               sess_pptp->rcv_gap = 0;
-               sess_pptp->snd_una = req->pr_proto.pptp.snd_una;
-               sess_pptp->snd_nxt = req->pr_proto.pptp.snd_nxt;
-               sess_pptp->rcv_nxt = req->pr_proto.pptp.rcv_nxt;
-               sess_pptp->rcv_acked = req->pr_proto.pptp.rcv_acked;
-
-               sess_pptp->winsz = req->pr_proto.pptp.winsz;
-               sess_pptp->maxwinsz = req->pr_proto.pptp.maxwinsz;
-               sess_pptp->peer_maxwinsz = req->pr_proto.pptp.peer_maxwinsz;
-               /* last ack number */
-               sess_pptp->ul_snd_una = sess_pptp->snd_una - 1;
-       }
-#endif
-#ifdef PIPEX_L2TP
-       if (req->pr_protocol == PIPEX_PROTO_L2TP) {
-               struct pipex_l2tp_session *sess_l2tp = &session->proto.l2tp;
-
-               /* session keys */
-               sess_l2tp->tunnel_id = req->pr_proto.l2tp.tunnel_id;
-               sess_l2tp->peer_tunnel_id = req->pr_proto.l2tp.peer_tunnel_id;
-
-               /* protocol options */
-               sess_l2tp->option_flags = req->pr_proto.l2tp.option_flags;
-
-               /* initial state of dynamic context */
-               sess_l2tp->ns_gap = sess_l2tp->nr_gap = 0;
-               sess_l2tp->ns_nxt = req->pr_proto.l2tp.ns_nxt;
-               sess_l2tp->nr_nxt = req->pr_proto.l2tp.nr_nxt;
-               sess_l2tp->ns_una = req->pr_proto.l2tp.ns_una;
-               sess_l2tp->nr_acked = req->pr_proto.l2tp.nr_acked;
-               /* last ack number */
-               sess_l2tp->ul_ns_una = sess_l2tp->ns_una - 1;
-       }
-#endif
-#ifdef PIPEX_MPPE
-       if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0)
-               pipex_session_init_mppe_recv(session,
-                   req->pr_mppe_recv.stateless, req->pr_mppe_recv.keylenbits,
-                   req->pr_mppe_recv.master_key);
-       if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0)
-               pipex_session_init_mppe_send(session,
-                   req->pr_mppe_send.stateless, req->pr_mppe_send.keylenbits,
-                   req->pr_mppe_send.master_key);
-
-       if (pipex_session_is_mppe_required(session)) {
-               if (!pipex_session_is_mppe_enabled(session) ||
-                   !pipex_session_is_mppe_accepted(session)) {
-                       pool_put(pppx_if_pl, pxi);
-                       return (EINVAL);
-               }
+       pxi->pxi_ifcontext.ifnet_this = ifp;
+       pxi->pxi_ifcontext.pipexmode = PIPEX_ENABLED;
+
+       error = pipex_init_session(session, req, &pxi->pxi_ifcontext);
+       if (error) {
+               pool_put(pppx_if_pl, pxi);
+               goto out;
        }
-#endif
 
        /* try to set the interface up */
        unit = pppx_if_next_unit();
@@ -837,6 +702,7 @@ pppx_add_session(struct pppx_dev *pxd, s
 
        /* this is safe without splnet since we're not modifying it */
        if (RBT_FIND(pppx_ifs, &pppx_ifs, pxi) != NULL) {
+               pipex_unlink_session(session);
                pool_put(pppx_if_pl, pxi);
                error = EADDRINUSE;
                goto out;
@@ -858,24 +724,7 @@ pppx_add_session(struct pppx_dev *pxd, s
        ifp->if_softc = pxi;
        /* ifp->if_rdomain = req->pr_rdomain; */
 
-       /* hook up pipex context */
-       chain = PIPEX_ID_HASHTABLE(session->session_id);
-       LIST_INSERT_HEAD(chain, session, id_chain);
-       LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
-#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
-       switch (req->pr_protocol) {
-       case PIPEX_PROTO_PPTP:
-       case PIPEX_PROTO_L2TP:
-               chain = PIPEX_PEER_ADDR_HASHTABLE(
-                   pipex_sockaddr_hash_key(&session->peer.sa));
-               LIST_INSERT_HEAD(chain, session, peer_addr_chain);
-               break;
-       }
-#endif
-
-       /* if first session is added, start timer */
-       if (LIST_NEXT(session, session_list) == NULL)
-               pipex_timer_start();
+       pipex_link_session(session);
 
        /* XXXSMP breaks atomicity */
        NET_UNLOCK();
@@ -1008,20 +857,7 @@ pppx_if_destroy(struct pppx_dev *pxd, st
        session = &pxi->pxi_session;
        ifp = &pxi->pxi_if;
 
-       LIST_REMOVE(session, id_chain);
-       LIST_REMOVE(session, session_list);
-#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
-       switch (session->protocol) {
-       case PIPEX_PROTO_PPTP:
-       case PIPEX_PROTO_L2TP:
-               LIST_REMOVE(session, peer_addr_chain);
-               break;
-       }
-#endif
-
-       /* if final session is destroyed, stop timer */
-       if (LIST_EMPTY(&pipex_session_list))
-               pipex_timer_stop();
+       pipex_unlink_session(session);
 
        /* XXXSMP breaks atomicity */
        NET_UNLOCK();
Index: sys/net/pipex.c
===================================================================
RCS file: /cvs/src/sys/net/pipex.c,v
retrieving revision 1.114
diff -u -p -r1.114 pipex.c
--- sys/net/pipex.c     31 May 2020 03:14:59 -0000      1.114
+++ sys/net/pipex.c     8 Jun 2020 12:07:09 -0000
@@ -258,13 +258,11 @@ pipex_ioctl(struct pipex_iface_context *
 /************************************************************************
  * Session management functions
  ************************************************************************/
-Static int
-pipex_add_session(struct pipex_session_req *req,
-    struct pipex_iface_context *iface)
+int
+pipex_init_session(struct pipex_session *session,
+    struct pipex_session_req *req, struct pipex_iface_context *iface)
 {
-       struct pipex_session *session;
-       struct pipex_hash_head *chain;
-       struct radix_node *rn;
+       struct pipex_session *session_tmp;
 #ifdef PIPEX_PPPOE
        struct ifnet *over_ifp = NULL;
 #endif
@@ -312,14 +310,14 @@ pipex_add_session(struct pipex_session_r
                return (EPROTONOSUPPORT);
        }
 
-       session = pipex_lookup_by_session_id(req->pr_protocol,
-           req->pr_session_id);
-       if (session)
-               return (EEXIST);
+       LIST_FOREACH(session_tmp, &pipex_session_list, session_list) {
+               if (session_tmp->protocol == req->pr_protocol &&
+                   session_tmp->session_id == req->pr_session_id)
+                       return (EEXIST);
+       }
 
        /* prepare a new session */
-       session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO);
-       session->state = PIPEX_STATE_OPENED;
+       session->state = PIPEX_STATE_INITIAL;
        session->protocol = req->pr_protocol;
        session->session_id = req->pr_session_id;
        session->peer_session_id = req->pr_peer_session_id;
@@ -396,19 +394,15 @@ pipex_add_session(struct pipex_session_r
 #endif
 #ifdef PIPEX_MPPE
        if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0) {
-               if (req->pr_mppe_recv.keylenbits <= 0) {
-                       pool_put(&pipex_session_pool, session);
+               if (req->pr_mppe_recv.keylenbits <= 0)
                        return (EINVAL);
-               }
                pipex_session_init_mppe_recv(session,
                    req->pr_mppe_recv.stateless, req->pr_mppe_recv.keylenbits,
                    req->pr_mppe_recv.master_key);
        }
        if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0) {
-               if (req->pr_mppe_send.keylenbits <= 0) {
-                       pool_put(&pipex_session_pool, session);
+               if (req->pr_mppe_send.keylenbits <= 0)
                        return (EINVAL);
-               }
                pipex_session_init_mppe_send(session,
                    req->pr_mppe_send.stateless, req->pr_mppe_send.keylenbits,
                    req->pr_mppe_send.master_key);
@@ -416,56 +410,112 @@ pipex_add_session(struct pipex_session_r
 
        if (pipex_session_is_mppe_required(session)) {
                if (!pipex_session_is_mppe_enabled(session) ||
-                   !pipex_session_is_mppe_accepted(session)) {
-                       pool_put(&pipex_session_pool, session);
+                   !pipex_session_is_mppe_accepted(session))
                        return (EINVAL);
-               }
        }
 #endif
 
-       NET_ASSERT_LOCKED();
+       LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
+
+       /* if first session is added, start timer */
+       if (LIST_NEXT(session, session_list) == NULL)
+               pipex_timer_start();
+
+       return 0;
+}
+
+void
+pipex_link_session(struct pipex_session *session)
+{
+       struct pipex_hash_head *chain;
+
+       chain = PIPEX_ID_HASHTABLE(session->session_id);
+       LIST_INSERT_HEAD(chain, session, id_chain);
+#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
+       switch (session->protocol) {
+       case PIPEX_PROTO_PPTP:
+       case PIPEX_PROTO_L2TP:
+               chain = PIPEX_PEER_ADDR_HASHTABLE(
+                   pipex_sockaddr_hash_key(&session->peer.sa));
+               LIST_INSERT_HEAD(chain, session, peer_addr_chain);
+       }
+#endif
+
+       session->state = PIPEX_STATE_OPENED;
+}
+
+void
+pipex_unlink_session(struct pipex_session *session)
+{
+       if (session->state != PIPEX_STATE_INITIAL) {
+               LIST_REMOVE(session, id_chain);
+#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
+               switch (session->protocol) {
+               case PIPEX_PROTO_PPTP:
+               case PIPEX_PROTO_L2TP:
+                       LIST_REMOVE(session, peer_addr_chain);
+                       break;
+               }
+#endif
+       }
+
+       LIST_REMOVE(session, session_list);
+
+       /* if final session is destroyed, stop timer */
+       if (LIST_EMPTY(&pipex_session_list))
+               pipex_timer_stop();
+
+       if (session->mppe_recv.old_session_keys)
+               pool_put(&mppe_key_pool, session->mppe_recv.old_session_keys);
+}
+
+Static int
+pipex_add_session(struct pipex_session_req *req,
+    struct pipex_iface_context *iface)
+{
+       struct pipex_session *session;
+       struct radix_node *rn;
+       int error;
+
+       session = pool_get(&pipex_session_pool, PR_WAITOK | PR_ZERO);
+       error = pipex_init_session(session, req, iface);
+       if (error)
+               goto out;
+
        /* commit the session */
        if (!in_nullhost(session->ip_address.sin_addr)) {
                if (pipex_lookup_by_ip_address(session->ip_address.sin_addr)
                    != NULL) {
-                       pool_put(&pipex_session_pool, session);
-                       return (EADDRINUSE);
+                       error = EADDRINUSE;
+                       goto unlink;
                }
 
                rn = rn_addroute(&session->ip_address, &session->ip_netmask,
                    pipex_rd_head4, session->ps4_rn, RTP_STATIC);
                if (rn == NULL) {
-                       pool_put(&pipex_session_pool, session);
-                       return (ENOMEM);
+                       error = ENOMEM;
+                       goto unlink;
                }
        }
        if (0) { /* NOT YET */
                rn = rn_addroute(&session->ip6_address, &session->ip6_prefixlen,
                    pipex_rd_head6, session->ps6_rn, RTP_STATIC);
                if (rn == NULL) {
-                       pool_put(&pipex_session_pool, session);
-                       return (ENOMEM);
+                       error = ENOMEM;
+                       goto unlink;
                }
        }
 
-       chain = PIPEX_ID_HASHTABLE(session->session_id);
-       LIST_INSERT_HEAD(chain, session, id_chain);
-       LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
-#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
-       switch (req->pr_protocol) {
-       case PIPEX_PROTO_PPTP:
-       case PIPEX_PROTO_L2TP:
-               chain = PIPEX_PEER_ADDR_HASHTABLE(
-                   pipex_sockaddr_hash_key(&session->peer.sa));
-               LIST_INSERT_HEAD(chain, session, peer_addr_chain);
-       }
-#endif
+       pipex_link_session(session);
+       pipex_session_log(session, LOG_INFO, "PIPEX is ready.");
 
-       /* if first session is added, start timer */
-       if (LIST_NEXT(session, session_list) == NULL)
-               pipex_timer_start();
+       return 0;
 
-       pipex_session_log(session, LOG_INFO, "PIPEX is ready.");
+unlink:
+       pipex_unlink_session(session);
+out:
+       pool_put(&pipex_session_pool, session);
+       return error;
 
        return (0);
 }
@@ -593,23 +643,7 @@ pipex_destroy_session(struct pipex_sessi
                KASSERT(rn != NULL);
        }
 
-       LIST_REMOVE(session, id_chain);
-       LIST_REMOVE(session, session_list);
-#if defined(PIPEX_PPTP) || defined(PIPEX_L2TP)
-       switch (session->protocol) {
-       case PIPEX_PROTO_PPTP:
-       case PIPEX_PROTO_L2TP:
-               LIST_REMOVE(session, peer_addr_chain);
-               break;
-       }
-#endif
-
-       /* if final session is destroyed, stop timer */
-       if (LIST_EMPTY(&pipex_session_list))
-               pipex_timer_stop();
-
-       if (session->mppe_recv.old_session_keys)
-               pool_put(&mppe_key_pool, session->mppe_recv.old_session_keys);
+       pipex_unlink_session(session);
        pool_put(&pipex_session_pool, session);
 
        return (0);
Index: sys/net/pipex_local.h
===================================================================
RCS file: /cvs/src/sys/net/pipex_local.h,v
retrieving revision 1.34
diff -u -p -r1.34 pipex_local.h
--- sys/net/pipex_local.h       26 May 2020 07:06:37 -0000      1.34
+++ sys/net/pipex_local.h       8 Jun 2020 12:07:09 -0000
@@ -376,6 +376,11 @@ extern struct pipex_hash_head      pipex_id_h
 
 void                  pipex_iface_start (struct pipex_iface_context *);
 void                  pipex_iface_stop (struct pipex_iface_context *);
+int                   pipex_init_session(struct pipex_session *,
+                                             struct pipex_session_req *,
+                                             struct pipex_iface_context *);
+void                  pipex_link_session(struct pipex_session *);
+void                  pipex_unlink_session(struct pipex_session *);
 int                   pipex_add_session (struct pipex_session_req *, struct 
pipex_iface_context *);
 int                   pipex_close_session (struct pipex_session_close_req *,
                           struct pipex_iface_context *);

Reply via email to