Hi,

Radek reported a problem to misc@ that multiple Windows clients behind a NAT cannot use a L2TP/IPsec server simultaneously.

https://marc.info/?t=160996816100001&r=1&w=2

There is two problems. First is pipex(4) doesn't pass the proper ipsecflowinfo to ip_output(). Second is the IPsec policy check which is done by ipsp_spd_lookup() returns -1 (EINVAL) if the given tdb is not cached. This happens when its flow is shared by another tdb (for another client of the same NAT).

The following 2 diffs fix these problem.

comment?
ok?

diff #1

Fix IPsec NAT-T work with pipex.

Index: sys/net/pipex.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/net/pipex.c,v
retrieving revision 1.132
diff -u -p -r1.132 pipex.c
--- sys/net/pipex.c     10 Mar 2021 10:21:48 -0000      1.132
+++ sys/net/pipex.c     12 May 2021 09:38:32 -0000
@@ -1628,6 +1628,7 @@ pipex_l2tp_output(struct mbuf *m0, struc
 #ifdef INET6
        struct ip6_hdr *ip6;
 #endif
+       struct m_tag *mtag;

        hlen = sizeof(struct pipex_l2tp_header) +
            ((pipex_session_is_l2tp_data_sequencing_on(session))
@@ -1703,6 +1704,15 @@ pipex_l2tp_output(struct mbuf *m0, struc
                ip->ip_ttl = MAXTTL;
                ip->ip_tos = 0;
                ip->ip_off = 0;
+
+               if (session->proto.l2tp.ipsecflowinfo > 0) {
+ if ((mtag = m_tag_get(PACKET_TAG_IPSEC_FLOWINFO,
+                           sizeof(u_int32_t), M_NOWAIT)) == NULL)
+                               goto drop;
+                       *(u_int32_t *)(mtag + 1) =
+                           session->proto.l2tp.ipsecflowinfo;
+                       m_tag_prepend(m0, mtag);
+               }

                ip_send(m0);
                break;
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_input.c,v
retrieving revision 1.359
diff -u -p -r1.359 ip_input.c
--- sys/netinet/ip_input.c      30 Apr 2021 13:52:48 -0000      1.359
+++ sys/netinet/ip_input.c      12 May 2021 09:38:32 -0000
@@ -1790,6 +1790,8 @@ ip_send_do_dispatch(void *xmq, int flags
        struct mbuf_queue *mq = xmq;
        struct mbuf *m;
        struct mbuf_list ml;
+       struct m_tag *mtag;
+       u_int32_t ipsecflowinfo = 0;

        mq_delist(mq, &ml);
        if (ml_empty(&ml))
@@ -1797,7 +1799,12 @@ ip_send_do_dispatch(void *xmq, int flags

        NET_LOCK();
        while ((m = ml_dequeue(&ml)) != NULL) {
-               ip_output(m, NULL, NULL, flags, NULL, NULL, 0);
+ if ((mtag = m_tag_find(m, PACKET_TAG_IPSEC_FLOWINFO, NULL))
+                   != NULL) {
+                       ipsecflowinfo = *(u_int32_t *)(mtag + 1);
+                       m_tag_delete(m, mtag);
+               }
+ ip_output(m, NULL, NULL, flags, NULL, NULL, ipsecflowinfo);
        }
        NET_UNLOCK();
 }
Index: sys/sys/mbuf.h
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/sys/mbuf.h,v
retrieving revision 1.252
diff -u -p -r1.252 mbuf.h
--- sys/sys/mbuf.h      25 Feb 2021 02:43:31 -0000      1.252
+++ sys/sys/mbuf.h      12 May 2021 09:38:32 -0000
@@ -469,6 +469,7 @@ struct m_tag *m_tag_next(struct mbuf *,
 /* Packet tag types */
#define PACKET_TAG_IPSEC_IN_DONE 0x0001 /* IPsec applied, in */ #define PACKET_TAG_IPSEC_OUT_DONE 0x0002 /* IPsec applied, out */
+#define PACKET_TAG_IPSEC_FLOWINFO      0x0004  /* IPsec flowinfo */
 #define PACKET_TAG_WIREGUARD           0x0040  /* WireGuard data */
#define PACKET_TAG_GRE 0x0080 /* GRE processing done */ #define PACKET_TAG_DLT 0x0100 /* data link layer type */
@@ -479,7 +480,7 @@ struct m_tag *m_tag_next(struct mbuf *,
#define PACKET_TAG_CARP_BAL_IP 0x4000 /* carp(4) ip balanced marker */

 #define MTAG_BITS \
-    ("\20\1IPSEC_IN_DONE\2IPSEC_OUT_DONE\3IPSEC_IN_CRYPTO_DONE" \
+    ("\20\1IPSEC_IN_DONE\2IPSEC_OUT_DONE\3IPSEC_FLOWINFO" \
"\4IPSEC_OUT_CRYPTO_NEEDED\5IPSEC_PENDING_TDB\6BRIDGE\7WG\10GRE\11DLT" \ "\12PF_DIVERT\14PF_REASSEMBLED\15SRCROUTE\16TUNNEL\17CARP_BAL_IP")


****

diff #2

Make the IPsec flow can have multiple `ipsec_ids' so that
ipsp_spd_lookup() can check whether the `ipsec_ids` of the given tdb is
belonged with a flow shared by mutlple clients behind a NAT.

Index: sys/net/pfkeyv2.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/net/pfkeyv2.c,v
retrieving revision 1.211
diff -u -p -r1.211 pfkeyv2.c
--- sys/net/pfkeyv2.c   4 May 2021 09:28:04 -0000       1.211
+++ sys/net/pfkeyv2.c   12 May 2021 10:07:11 -0000
@@ -1106,6 +1106,7 @@ pfkeyv2_send(struct socket *so, void *me
        int i, j, rval = 0, mode = PFKEYV2_SENDMESSAGE_BROADCAST;
        int delflag = 0;
        struct sockaddr_encap encapdst, encapnetmask;
+       struct ipsec_ids *ids, *ids0;
        struct ipsec_policy *ipo;
        struct ipsec_acquire *ipa;
        struct radix_node_head *rnh;
@@ -1951,15 +1952,10 @@ pfkeyv2_send(struct socket *so, void *me

ipo->ipo_sproto = SADB_X_GETSPROTO(smsg->sadb_msg_satype);

-               if (ipo->ipo_ids) {
-                       ipsp_ids_free(ipo->ipo_ids);
-                       ipo->ipo_ids = NULL;
-               }
-
                if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL &&
                    (did = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
-                       import_identities(&ipo->ipo_ids, 0, sid, did);
-                       if (ipo->ipo_ids == NULL) {
+                       import_identities(&ids0, 0, sid, did);
+                       if (ids0 == NULL) {
                                if (exists)
                                        ipsec_delete_policy(ipo);
                                else
@@ -1967,6 +1963,22 @@ pfkeyv2_send(struct socket *so, void *me
                                rval = ENOBUFS;
                                NET_UNLOCK();
                                goto ret;
+                       }
+                       for (ids = ipo->ipo_ids; ids != NULL;
+                           ids = ids->id_pnext)
+                               if (ipsp_ids_match(ids, ids0))
+                                       break;
+                       if (ids != NULL)
+                               /* already have this */
+                               ipsp_ids_free(ids0);
+                       else {
+                               ids0->id_pnext = ipo->ipo_ids;
+                               ipo->ipo_ids = ids0;
+                       }
+               } else {
+                       if (ipo->ipo_ids) {
+                               ipsp_ids_free(ipo->ipo_ids);
+                               ipo->ipo_ids = NULL;
                        }
                }

Index: sys/netinet/ip_ipsp.h
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_ipsp.h,v
retrieving revision 1.197
diff -u -p -r1.197 ip_ipsp.h
--- sys/netinet/ip_ipsp.h       4 May 2021 09:28:04 -0000       1.197
+++ sys/netinet/ip_ipsp.h       12 May 2021 10:07:11 -0000
@@ -237,6 +237,7 @@ struct ipsec_ids {
        u_int32_t               id_flow;
        int                     id_refcount;
        struct timeout          id_timeout;
+ struct ipsec_ids *id_pnext; /* list for ipsec_policy */
 };
 RBT_HEAD(ipsec_ids_flows, ipsec_ids);
 RBT_HEAD(ipsec_ids_tree, ipsec_ids);
Index: sys/netinet/ip_spd.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_spd.c,v
retrieving revision 1.103
diff -u -p -r1.103 ip_spd.c
--- sys/netinet/ip_spd.c        4 May 2021 09:28:04 -0000       1.103
+++ sys/netinet/ip_spd.c        12 May 2021 10:07:11 -0000
@@ -515,10 +515,16 @@ ipsp_spd_lookup(struct mbuf *m, int af,
                                goto nomatchin;

                        /* Match source/dest IDs. */
-                       if (ipo->ipo_ids)
-                               if (tdbp->tdb_ids == NULL ||
- !ipsp_ids_match(ipo->ipo_ids, tdbp->tdb_ids))
+                       if (ipo->ipo_ids) {
+                               if (tdbp->tdb_ids == NULL)
                                        goto nomatchin;
+                               for (ids = ipo->ipo_ids; ids != NULL;
+                                   ids = ids->id_pnext)
+ if (ipsp_ids_match(ids, tdbp->tdb_ids))
+                                               break;
+                               if (ids == NULL)
+                                       goto nomatchin;
+                       }

                        /* Add it to the cache. */
                        if (ipo->ipo_tdb)
@@ -624,6 +630,7 @@ int
 ipsec_delete_policy(struct ipsec_policy *ipo)
 {
        struct ipsec_acquire *ipa;
+       struct ipsec_ids *ids;
        struct radix_node_head *rnh;
        struct radix_node *rn = (struct radix_node *)ipo;
        int err = 0;
@@ -647,8 +654,11 @@ ipsec_delete_policy(struct ipsec_policy

        TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);

-       if (ipo->ipo_ids)
-               ipsp_ids_free(ipo->ipo_ids);
+       while (ipo->ipo_ids != NULL) {
+               ids = ipo->ipo_ids;
+               ipo->ipo_ids = ipo->ipo_ids->id_pnext;
+               ipsp_ids_free(ids);
+       }

        ipsec_in_use--;

Reply via email to