Hi,

On Tue, 14 Dec 2021 01:20:49 +0100
Alexander Bluhm <alexander.bl...@gmx.net> wrote:
> I don't know much about l2tp, pipex or npppd.  So I cannot say if
> the new logic is correct.  But I guess you have tested that.

Yes, I've tested some L2TP/IPsec cases already.

> The tdb mutex and ref counting looks correct.
> 
>> +    struct tdb *tdb, *tdblocal = NULL;
> 
> The variable names tdb and tdbp are used very inconsistently within
> IPsec.  Don't use both.  I think tdpb and a tdbflow are sufficient.

Ok,

> 
>> +    if (ipsecflowinfo != 0)
>> +            ids = ipsp_ids_lookup(ipsecflowinfo);
> 
> Can you move that to the place where it is needed?

Yes,

> Perhaps it is easier to understand this way:
> 
>       if (ipsecflowinfo != 0) {

Sure.  Let me update the diff.

> It is hard to say whether the new
> rn_lookup(tdbp->tdb_filter/tdbp->tdb_filtermask) changes existing
> IPsec behavior for setups without l2tp.

I suppose it has no regression on other setups.
But I'll look it more carefully and test the other setups.

> Do we need it there?

Yes, if there is another better idea, it will be welcome.
For this moment, the diff is the best idea for me.

> I never ran into problems patching the correct policy.

Index: sys/netinet/ip_ipsp.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_ipsp.c,v
retrieving revision 1.264
diff -u -p -r1.264 ip_ipsp.c
--- sys/netinet/ip_ipsp.c       11 Dec 2021 16:33:47 -0000      1.264
+++ sys/netinet/ip_ipsp.c       14 Dec 2021 06:32:07 -0000
@@ -91,6 +91,8 @@ void          tdb_soft_timeout(void *);
 void           tdb_soft_firstuse(void *);
 int            tdb_hash(u_int32_t, union sockaddr_union *, u_int8_t);
 void           tdb_dodelete(struct tdb *, int locked);
+int            sockaddr_encap_match(struct sockaddr_encap *,
+                   struct sockaddr_encap *, struct sockaddr_encap *);
 
 int ipsec_in_use = 0;
 u_int64_t ipsec_last_added = 0;
@@ -510,6 +512,78 @@ gettdbbysrc(u_int rdomain, union sockadd
        tdb_ref(tdbp);
        mtx_leave(&tdb_sadb_mtx);
        return tdbp;
+}
+
+/*
+ * Get an SA given the flow, the direction, the security protocol type, and
+ * the desired IDs.
+ */
+struct tdb *
+gettdbbyflow(u_int rdomain, int direction, struct sockaddr_encap *senflow,
+    u_int8_t sproto, struct ipsec_ids *ids)
+{
+       u_int32_t hashval;
+       struct tdb *tdbp;
+       union sockaddr_union srcdst;
+
+       if (ids == NULL)        /* ids is mandatory */
+               return NULL;
+
+       memset(&srcdst, 0, sizeof(srcdst));
+       switch (senflow->sen_type) {
+       case SENT_IP4:
+               srcdst.sin.sin_len = sizeof(srcdst.sin);
+               srcdst.sin.sin_family = AF_INET;
+               if (direction == IPSP_DIRECTION_OUT)
+                       srcdst.sin.sin_addr = senflow->Sen.Sip4.Dst;
+               else
+                       srcdst.sin.sin_addr = senflow->Sen.Sip4.Src;
+               break;
+       case SENT_IP6:
+               srcdst.sin6.sin6_len = sizeof(srcdst.sin6);
+               srcdst.sin6.sin6_family = AF_INET6;
+               if (direction == IPSP_DIRECTION_OUT)
+                       srcdst.sin6.sin6_addr = senflow->Sen.Sip6.Dst;
+               else
+                       srcdst.sin6.sin6_addr = senflow->Sen.Sip6.Src;
+               break;
+       }
+
+       mtx_enter(&tdb_sadb_mtx);
+       hashval = tdb_hash(0, &srcdst, sproto);
+
+       for (tdbp = tdbdst[hashval]; tdbp != NULL; tdbp = tdbp->tdb_dnext)
+               if (tdbp->tdb_sproto == sproto &&
+                   tdbp->tdb_rdomain == rdomain &&
+                   (tdbp->tdb_flags & TDBF_INVALID) == 0 &&
+                   ipsp_ids_match(ids, tdbp->tdb_ids) &&
+                   ((direction == IPSP_DIRECTION_OUT &&
+                   !memcmp(&tdbp->tdb_dst, &srcdst, srcdst.sa.sa_len)) ||
+                   (direction == IPSP_DIRECTION_IN &&
+                   !memcmp(&tdbp->tdb_src, &srcdst, srcdst.sa.sa_len)))) {
+                       if (sockaddr_encap_match(&tdbp->tdb_filter,
+                           &tdbp->tdb_filtermask, senflow))
+                               break;
+               }
+
+       tdb_ref(tdbp);
+       mtx_leave(&tdb_sadb_mtx);
+       return tdbp;
+}
+
+int
+sockaddr_encap_match(struct sockaddr_encap *addr, struct sockaddr_encap *mask,
+    struct sockaddr_encap *dest)
+{
+       size_t  off;
+
+       for (off = offsetof(struct sockaddr_encap, sen_type);
+           off < dest->sen_len; off++) {
+               if ((*((u_char *)addr + off) & *((u_char *)mask + off)) !=
+                   (*((u_char *)dest + off) & *((u_char *)mask + off)))
+                       break;
+       }
+       return (off < dest->sen_len)? 0 : 1;
 }
 
 #ifdef DDB
Index: sys/netinet/ip_ipsp.h
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_ipsp.h,v
retrieving revision 1.230
diff -u -p -r1.230 ip_ipsp.h
--- sys/netinet/ip_ipsp.h       11 Dec 2021 16:33:47 -0000      1.230
+++ sys/netinet/ip_ipsp.h       14 Dec 2021 06:32:07 -0000
@@ -574,6 +574,8 @@ struct      tdb *gettdbbysrcdst_dir(u_int, u_
                union sockaddr_union *, u_int8_t, int);
 #define gettdbbysrcdst(a,b,c,d,e) gettdbbysrcdst_dir((a),(b),(c),(d),(e),0)
 #define gettdbbysrcdst_rev(a,b,c,d,e) gettdbbysrcdst_dir((a),(b),(c),(d),(e),1)
+struct tdb *gettdbbyflow(u_int, int, struct sockaddr_encap *, u_int8_t,
+               struct ipsec_ids *);
 void   puttdb(struct tdb *);
 void   puttdb_locked(struct tdb *);
 void   tdb_delete(struct tdb *);
Index: sys/netinet/ip_spd.c
===================================================================
RCS file: /disk/cvs/openbsd/src/sys/netinet/ip_spd.c,v
retrieving revision 1.108
diff -u -p -r1.108 ip_spd.c
--- sys/netinet/ip_spd.c        3 Dec 2021 17:18:34 -0000       1.108
+++ sys/netinet/ip_spd.c        14 Dec 2021 06:32:07 -0000
@@ -150,13 +150,14 @@ ipsp_spd_lookup(struct mbuf *m, int af, 
     u_int32_t ipsecflowinfo)
 {
        struct radix_node_head *rnh;
-       struct radix_node *rn;
+       struct radix_node *rn = NULL;
        union sockaddr_union sdst, ssrc;
        struct sockaddr_encap *ddst, dst;
        struct ipsec_policy *ipo;
        struct ipsec_ids *ids = NULL;
        int error, signore = 0, dignore = 0;
        u_int rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
+       struct tdb *tdbflow = NULL;
 
        NET_ASSERT_LOCKED();
 
@@ -297,9 +298,33 @@ ipsp_spd_lookup(struct mbuf *m, int af, 
                return EAFNOSUPPORT;
        }
 
+       /*
+        * Prepare tdb for searching the correct SPD by rn_lookup().
+        * "tdb_filtemask" of the tdb is used to find the correct SPD when
+        * multiple policies are overlapped.
+        */
+       if (ipsecflowinfo != 0) {
+               ids = ipsp_ids_lookup(ipsecflowinfo);
+               if (ids != NULL)
+                       tdbflow = gettdbbyflow(rdomain, direction, &dst,
+                           IPPROTO_ESP, ids);
+       }
+
        /* Actual SPD lookup. */
-       if ((rnh = spd_table_get(rdomain)) == NULL ||
-           (rn = rn_match((caddr_t)&dst, rnh)) == NULL) {
+       rnh = spd_table_get(rdomain);
+       if (rnh != NULL) {
+               if (tdbflow != NULL)
+                       rn = rn_lookup((caddr_t)&tdbflow->tdb_filter,
+                           (caddr_t)&tdbflow->tdb_filtermask, rnh);
+               else if (tdbp != NULL)
+                       rn = rn_lookup((caddr_t)&tdbp->tdb_filter,
+                           (caddr_t)&tdbp->tdb_filtermask, rnh);
+               else
+                       rn = rn_match((caddr_t)&dst, rnh);
+       }
+       tdb_unref(tdbflow);
+
+       if (rn == NULL) {
                /*
                 * Return whatever the socket requirements are, there are no
                 * system-wide policies.
@@ -389,9 +414,6 @@ ipsp_spd_lookup(struct mbuf *m, int af, 
                        }
                }
 
-               if (ipsecflowinfo)
-                       ids = ipsp_ids_lookup(ipsecflowinfo);
-
                /* Check that the cached TDB (if present), is appropriate. */
                if (ipo->ipo_tdb != NULL) {
                        if ((ipo->ipo_last_searched <= ipsec_last_added) ||
@@ -499,10 +521,19 @@ 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 != NULL) {
+                               if ((tdbp->tdb_flags & TDBF_TUNNELING) == 0 &&
+                                   (tdbp->tdb_flags & TDBF_UDPENCAP) != 0) {
+                                       /*
+                                        * Skip IDs check for transport mode
+                                        * with NAT-T.  Multiple clients (IDs)
+                                        * can use a same policy.
+                                        */
+                               } else if (tdbp->tdb_ids == NULL &&
+                                   !ipsp_ids_match(ipo->ipo_ids,
+                                   tdbp->tdb_ids))
                                        goto nomatchin;
+                       }
 
                        /* Add it to the cache. */
                        if (ipo->ipo_tdb != NULL) {


Thanks,

Reply via email to