Hello,

This patch allows handling multiple IKE1/L2TP tunnels that come from the
same IP address.

It happens when clients are behind the same NAT gateway, thus seen using
the same IP address. Currently, the issue is that it works for only one
client at a time.

The idea is to rely on identifiers to distinguish clients. (This is why
they are also sent when doing a SADB_X_DELFLOW.)

Regarding storage of IPSEC policies, those that match the same route are
stored contiguously in ipo_list, glued by ipo_next_ipo_uses_same_route.
rn_match() points to the first item of a block, then it's just a matter
of finding the item containing the right identifiers.

Regarding performances of ipsp_spd_lookup(), there is always a check on
ipo->ipo_next_ipo_uses_same_route so the minimum cost is negligible.
Then, when tunnels are sharing a route, the search is a basic iteration
so this part may not scale very well.

The patch that follows is made against current sources. Some parts have
been written to keep the diff the shortest possible (first chunk of
isakmpd/pf_key_v2.c is definitely made with this in mind and should be
rewritten).

Affected files:
- sbin/isakmpd/ipsec.c
- sbin/isakmpd/pf_key_v2.c
- sys/net/pfkeyv2.c
- sys/net/pfkeyv2_parsemessage.c
- sys/netinet/ip_ipsp.c
- sys/netinet/ip_ipsp.h
- sys/netinet/ip_spd.c

Regards,

Mathieu J. PAPINEAU



diff -r 1f9cc3fc2a87 -r ec38c9134666 sbin/isakmpd/ipsec.c
--- sbin/isakmpd/ipsec.c        Thu Oct 05 10:26:02 2023 +0200
+++ sbin/isakmpd/ipsec.c        Mon Sep 18 11:54:00 2023 +0200
@@ -289,6 +289,20 @@
            isa->dst_mask == NULL || isa2->dst_mask == NULL)
                return 0;
 
+       /* Having different ports means different peers. */
+       struct sockaddr *dst1, *dst2;
+       if (sa->initiator)
+               sa->transport->vtbl->get_src(sa->transport, &dst1);
+       else
+               sa->transport->vtbl->get_dst(sa->transport, &dst1);
+       if (sa2->initiator)
+               sa2->transport->vtbl->get_src(sa2->transport, &dst2);
+       else
+               sa2->transport->vtbl->get_dst(sa2->transport, &dst2);
+       if (dst1 && dst2)
+               if (sockaddr_port(dst1) != sockaddr_port(dst2))
+                       return 0;
+
        return isa->src_net->sa_family == isa2->src_net->sa_family &&
            memcmp(sockaddr_addrdata(isa->src_net),
            sockaddr_addrdata(isa2->src_net),
diff -r 1f9cc3fc2a87 -r ec38c9134666 sbin/isakmpd/pf_key_v2.c
--- sbin/isakmpd/pf_key_v2.c    Thu Oct 05 10:26:02 2023 +0200
+++ sbin/isakmpd/pf_key_v2.c    Mon Sep 18 11:54:00 2023 +0200
@@ -1520,7 +1520,7 @@
        if (!flow)
                goto cleanup;
 
-       if (!delete) {
+       if (!delete || proto == IPSEC_PROTO_IPSEC_ESP) {
                /* Setup the source ID, if provided. */
                if (srcid) {
                        sid = calloc(
@@ -1980,20 +1980,40 @@
        struct ipsec_sa *isa = sa->data;
        struct sockaddr *dst, *src;
        struct proto   *proto = TAILQ_FIRST(&sa->protos);
+       int             sidtype = 0, didtype = 0;
+       size_t          sidlen = 0, didlen = 0;
+       u_int8_t       *sid = 0, *did = 0;
 
        sa->transport->vtbl->get_dst(sa->transport, &dst);
        sa->transport->vtbl->get_src(sa->transport, &src);
 
+       if (sa->id_i) {
+               if (sa->initiator)
+                       sid = pf_key_v2_convert_id(sa->id_i,
+                           sa->id_i_len, &sidlen, &sidtype);
+               else
+                       did = pf_key_v2_convert_id(sa->id_i,
+                           sa->id_i_len, &didlen, &didtype);
+       }
+       if (sa->id_r) {
+               if (sa->initiator)
+                       did = pf_key_v2_convert_id(sa->id_r,
+                           sa->id_r_len, &didlen, &didtype);
+               else
+                       sid = pf_key_v2_convert_id(sa->id_r,
+                           sa->id_r_len, &sidlen, &sidtype);
+       }
+
        if (!incoming)
                return pf_key_v2_flow(isa->src_net, isa->src_mask,
                    isa->dst_net, isa->dst_mask, isa->tproto, isa->sport,
-                   isa->dport, proto->spi[0], proto->proto, src, dst, 1, 0,
-                   0, 0, 0, 0, 0, 0, proto->data);
+                   isa->dport, proto->spi[0], proto->proto, dst, src, 1, 0,
+                   sidtype, sid, sidlen, didtype, did, didlen, proto->data);
        else {
                return pf_key_v2_flow(isa->dst_net, isa->dst_mask,
                    isa->src_net, isa->src_mask, isa->tproto, isa->dport,
                    isa->sport, proto->spi[1], proto->proto, src, dst, 1, 1,
-                   0, 0, 0, 0, 0, 0, proto->data);
+                   sidtype, sid, sidlen, didtype, did, didlen, proto->data);
        }
 }
 
diff -r 1f9cc3fc2a87 -r ec38c9134666 sys/net/pfkeyv2.c
--- sys/net/pfkeyv2.c   Thu Oct 05 10:26:02 2023 +0200
+++ sys/net/pfkeyv2.c   Mon Sep 18 11:54:00 2023 +0200
@@ -1127,6 +1127,7 @@
        int delflag = 0;
        struct sockaddr_encap encapdst, encapnetmask;
        struct ipsec_policy *ipo;
+       struct ipsec_ids *ipo_ids_tmp = NULL;
        struct ipsec_acquire *ipa;
        struct radix_node_head *rnh;
        struct radix_node *rn = NULL;
@@ -1884,6 +1885,27 @@
                } else
                        ipo = NULL;
 
+               struct ipsec_policy *ipo_holding_route = NULL;
+               if (exists && udpencap_enable) {
+                       ipo_holding_route = ipo;
+
+                       if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL &&
+                           (did = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
+                               import_identities(&ipo_ids_tmp, 0, sid, did);
+                               if (ipo_ids_tmp == NULL) {
+                                       rval = ENOBUFS;
+                                       NET_UNLOCK();
+                                       goto ret;
+                               }
+                       }
+
+                       if (ipo_ids_tmp)
+                               ipo = ipsp_find_ipo_by_ids(ipo, ipo_ids_tmp);
+
+                       if (!ipo)
+                               exists = 0;
+               }
+
                /*
                 * If the existing policy is static, only delete or update
                 * it if the new one is also static.
@@ -1980,7 +2002,12 @@
 
                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_ids_tmp) {
+                               ipo->ipo_ids = ipo_ids_tmp;
+                               ipo_ids_tmp = NULL; /* As they should not be 
freed. */
+                       } else
+                               import_identities(&ipo->ipo_ids, 0, sid, did);
+
                        if (ipo->ipo_ids == NULL) {
                                if (exists)
                                        ipsec_delete_policy(ipo);
@@ -2004,28 +2031,37 @@
                        ipo->ipo_rdomain = rdomain;
                        refcnt_init(&ipo->ipo_refcnt);
 
-                       /* Add SPD entry */
-                       if ((rnh = spd_table_get(rdomain)) == NULL ||
-                           (rn = rn_addroute((caddr_t)&ipo->ipo_addr,
-                               (caddr_t)&ipo->ipo_mask, rnh,
-                               ipo->ipo_nodes, 0)) == NULL) {
-                               /* Remove from linked list of policies on TDB */
-                               mtx_enter(&ipo_tdb_mtx);
-                               if (ipo->ipo_tdb != NULL) {
-                                       TAILQ_REMOVE(
-                                           &ipo->ipo_tdb->tdb_policy_head,
-                                           ipo, ipo_tdb_next);
-                                       tdb_unref(ipo->ipo_tdb);
-                                       ipo->ipo_tdb = NULL;
+                       if (ipo_holding_route && ipo != ipo_holding_route) {
+                               /* SPD entry already exists. */
+                               TAILQ_INSERT_AFTER(&ipsec_policy_head, 
ipo_holding_route, ipo,
+                                   ipo_list);
+                               ipo->ipo_next_ipo_uses_same_route =
+                                   
ipo_holding_route->ipo_next_ipo_uses_same_route;
+                               ipo_holding_route->ipo_next_ipo_uses_same_route 
= 1;
+                       } else {
+                               /* Add SPD entry */
+                               if (rnh == NULL ||
+                                       (rn = 
rn_addroute((caddr_t)&ipo->ipo_addr,
+                                       (caddr_t)&ipo->ipo_mask, rnh,
+                                       ipo->ipo_nodes, 0)) == NULL) {
+                                       /* Remove from linked list of policies 
on TDB */
+                                       mtx_enter(&ipo_tdb_mtx);
+                                       if (ipo->ipo_tdb != NULL) {
+                                               TAILQ_REMOVE(
+                                                       
&ipo->ipo_tdb->tdb_policy_head,
+                                                       ipo, ipo_tdb_next);
+                                               tdb_unref(ipo->ipo_tdb);
+                                               ipo->ipo_tdb = NULL;
+                                       }
+                                       mtx_leave(&ipo_tdb_mtx);
+                                       if (ipo->ipo_ids)
+                                               ipsp_ids_free(ipo->ipo_ids);
+                                       pool_put(&ipsec_policy_pool, ipo);
+                                       NET_UNLOCK();
+                                       goto ret;
                                }
-                               mtx_leave(&ipo_tdb_mtx);
-                               if (ipo->ipo_ids)
-                                       ipsp_ids_free(ipo->ipo_ids);
-                               pool_put(&ipsec_policy_pool, ipo);
-                               NET_UNLOCK();
-                               goto ret;
+                               TAILQ_INSERT_HEAD(&ipsec_policy_head, ipo, 
ipo_list);
                        }
-                       TAILQ_INSERT_HEAD(&ipsec_policy_head, ipo, ipo_list);
                        ipsec_in_use++;
                } else {
                        ipo->ipo_last_searched = ipo->ipo_flags = 0;
@@ -2092,6 +2128,9 @@
        }
 
 ret:
+       if (ipo_ids_tmp)
+               ipsp_ids_free(ipo_ids_tmp);
+
        if (rval) {
                if ((rval == EINVAL) || (rval == ENOMEM) || (rval == ENOBUFS))
                        goto realret;
@@ -2613,7 +2652,7 @@
 }
 
 int
-pfkeyv2_sysctl_policydumper(struct ipsec_policy *ipo, void *arg,
+pfkeyv2_sysctl_policydumper_single(struct ipsec_policy *ipo, void *arg,
     unsigned int tableid)
 {
        struct pfkeyv2_sysctl_walk *w = (struct pfkeyv2_sysctl_walk *)arg;
@@ -2673,6 +2712,26 @@
 }
 
 int
+pfkeyv2_sysctl_policydumper(struct ipsec_policy *ipo, void *arg,
+    unsigned int tableid)
+{
+       int error = 0;
+
+       while (ipo) {
+               error = pfkeyv2_sysctl_policydumper_single(ipo, arg, tableid);
+               if (error)
+                       break;
+
+               if (!ipo->ipo_next_ipo_uses_same_route)
+                       break;
+
+               ipo = TAILQ_NEXT(ipo, ipo_list);
+       }
+
+       return error;
+}
+
+int
 pfkeyv2_policy_flush(struct ipsec_policy *ipo, void *arg, unsigned int tableid)
 {
        int error;
diff -r 1f9cc3fc2a87 -r ec38c9134666 sys/net/pfkeyv2_parsemessage.c
--- sys/net/pfkeyv2_parsemessage.c      Thu Oct 05 10:26:02 2023 +0200
+++ sys/net/pfkeyv2_parsemessage.c      Mon Sep 18 11:54:00 2023 +0200
@@ -166,7 +166,7 @@
        /* X_ADDFLOW */
        BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY_SRC | 
BITMAP_IDENTITY_DST | BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
        /* X_DELFLOW */
-       BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
+       BITMAP_X_FLOW | BITMAP_X_RDOMAIN | BITMAP_IDENTITY,
        /* X_GRPSPIS */
        BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | 
BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
        /* X_ASKPOLICY */
diff -r 1f9cc3fc2a87 -r ec38c9134666 sys/netinet/ip_ipsp.c
--- sys/netinet/ip_ipsp.c       Thu Oct 05 10:26:02 2023 +0200
+++ sys/netinet/ip_ipsp.c       Mon Sep 18 11:54:00 2023 +0200
@@ -1376,3 +1376,45 @@
                return -1;
        return 0;
 }
+
+static struct ipsec_policy *
+ipsp_find_ipo_by_flow_id(struct ipsec_policy *ipo, u_int32_t id_flow)
+{
+       while (ipo != NULL) {
+               if (ipo->ipo_ids && ipo->ipo_ids->id_flow == id_flow)
+                       break;
+
+               if (!ipo->ipo_next_ipo_uses_same_route) {
+                       ipo = NULL;
+                       break;
+               }
+
+               ipo = TAILQ_NEXT(ipo, ipo_list);
+       }
+
+       return ipo;
+}
+
+struct ipsec_policy *
+ipsp_find_ipo_by_ids(struct ipsec_policy *ipo, struct ipsec_ids *ids)
+{
+       if (ids == NULL)
+               return NULL;
+
+       if (ids->id_flow)
+               return ipsp_find_ipo_by_flow_id(ipo, ids->id_flow);
+
+       while (ipo != NULL) {
+               if (ipsp_ids_cmp(ids, ipo->ipo_ids) == 0)
+                       break;
+
+               if (!ipo->ipo_next_ipo_uses_same_route) {
+                       ipo = NULL;
+                       break;
+               }
+
+               ipo = TAILQ_NEXT(ipo, ipo_list);
+       }
+
+       return ipo;
+}
diff -r 1f9cc3fc2a87 -r ec38c9134666 sys/netinet/ip_ipsp.h
--- sys/netinet/ip_ipsp.h       Thu Oct 05 10:26:02 2023 +0200
+++ sys/netinet/ip_ipsp.h       Mon Sep 18 11:54:00 2023 +0200
@@ -281,6 +281,8 @@
 
        u_int64_t       ipo_last_searched;      /* [P] Timestamp of lookup */
 
+       u_int8_t        ipo_next_ipo_uses_same_route;
+
        u_int8_t                ipo_flags;      /* See IPSP_POLICY_* 
definitions */
        u_int8_t                ipo_type;       /* USE/ACQUIRE/... */
        u_int8_t                ipo_sproto;     /* ESP/AH; if zero, use system 
dflts */
@@ -695,5 +697,8 @@
 int    ipsec_forward_check(struct mbuf *, int, int);
 int    ipsec_local_check(struct mbuf *, int, int, int);
 
+struct ipsec_policy *ipsp_find_ipo_by_ids(struct ipsec_policy *ipo,
+           struct ipsec_ids *ids);
+
 #endif /* _KERNEL */
 #endif /* _NETINET_IPSP_H_ */
diff -r 1f9cc3fc2a87 -r ec38c9134666 sys/netinet/ip_spd.c
--- sys/netinet/ip_spd.c        Thu Oct 05 10:26:02 2023 +0200
+++ sys/netinet/ip_spd.c        Mon Sep 18 11:54:00 2023 +0200
@@ -315,6 +315,18 @@
        }
        ipo = (struct ipsec_policy *)rn;
 
+       /* Get the policy that matches dst AND identifiers. */
+       if (ipo->ipo_next_ipo_uses_same_route) {
+               struct ipsec_ids *ids_filter = ipsecflowinfo_ids;
+               if (!ids_filter && tdbin)
+                       ids_filter = tdbin->tdb_ids;
+               if (ids_filter) {
+                       ipo = ipsp_find_ipo_by_ids(ipo, ids_filter);
+                       if (!ipo)
+                               return ipsp_spd_inp(m, inp, NULL, tdbout);
+               }
+       }
+
        switch (ipo->ipo_type) {
        case IPSP_PERMIT:
                return ipsp_spd_inp(m, inp, ipo, tdbout);
@@ -672,16 +684,39 @@
        struct ipsec_acquire *ipa;
        struct radix_node_head *rnh;
        struct radix_node *rn = (struct radix_node *)ipo;
+       struct ipsec_policy *ipo_prev;
 
        NET_ASSERT_LOCKED_EXCLUSIVE();
 
        if (refcnt_rele(&ipo->ipo_refcnt) == 0)
                return 0;
 
-       /* Delete from SPD. */
-       if ((rnh = spd_table_get(ipo->ipo_rdomain)) == NULL ||
-           rn_delete(&ipo->ipo_addr, &ipo->ipo_mask, rnh, rn) == NULL)
-               return (ESRCH);
+       /* Delete from SPD only when it's the first policy in the list. */
+       ipo_prev = TAILQ_PREV(ipo, ipsec_policy_head, ipo_list);
+       if (!ipo_prev || !ipo_prev->ipo_next_ipo_uses_same_route) {
+               rnh = spd_table_get(ipo->ipo_rdomain);
+               if (rnh == NULL ||
+                   rn_delete(&ipo->ipo_addr, &ipo->ipo_mask, rnh, rn) == NULL)
+                       return (ESRCH);
+
+               /* Eventually redo route with next policy. */
+               if (rnh && ipo->ipo_next_ipo_uses_same_route) {
+                       struct ipsec_policy *ipo_next = TAILQ_NEXT(ipo, 
ipo_list);
+                       if (ipo_next)
+                               rn = rn_addroute(
+                                   (caddr_t)&ipo_next->ipo_addr,
+                                   (caddr_t)&ipo_next->ipo_mask,
+                                   rnh,
+                                   ipo_next->ipo_nodes,
+                                   0
+                               );
+               }
+       }
+
+       /* When removing last policy, mark previous one as the new last. */
+       if (ipo_prev && ipo_prev->ipo_next_ipo_uses_same_route)
+               if (!ipo->ipo_next_ipo_uses_same_route)
+                       ipo_prev->ipo_next_ipo_uses_same_route = 0;
 
        mtx_enter(&ipo_tdb_mtx);
        if (ipo->ipo_tdb != NULL) {<br><div style='text-align: 
Center;font-family: Helvetica 75 Bold;color: #ED7D31;font-size: 8pt;margin: 
5pt;'>Orange Restricted</div>
____________________________________________________________________________________________________________
Ce message et ses pieces jointes peuvent contenir des informations 
confidentielles ou privilegiees et ne doivent donc
pas etre diffuses, exploites ou copies sans autorisation. Si vous avez recu ce 
message par erreur, veuillez le signaler
a l'expediteur et le detruire ainsi que les pieces jointes. Les messages 
electroniques etant susceptibles d'alteration,
Orange decline toute responsabilite si ce message a ete altere, deforme ou 
falsifie. Merci.

This message and its attachments may contain confidential or privileged 
information that may be protected by law;
they should not be distributed, used or copied without authorisation.
If you have received this email in error, please notify the sender and delete 
this message and its attachments.
As emails may be altered, Orange is not liable for messages that have been 
modified, changed or falsified.
Thank you.

Reply via email to