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--;