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.