introduce ipsec-id bundles and use them for ipsecflowinfo,
authormarkus <markus@openbsd.org>
Sat, 23 May 2015 12:38:53 +0000 (12:38 +0000)
committermarkus <markus@openbsd.org>
Sat, 23 May 2015 12:38:53 +0000 (12:38 +0000)
fixes rekeying for l2tp/ipsec against multiple windows clients
and saves memory (for many SAs to same peers); feedback and ok mikeb@

sys/net/pfkeyv2.c
sys/net/pfkeyv2.h
sys/net/pfkeyv2_convert.c
sys/netinet/ip_ipsp.c
sys/netinet/ip_ipsp.h
sys/netinet/ip_spd.c
sys/netinet/udp_usrreq.c

index d0c95d5..54114e9 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2.c,v 1.143 2015/04/16 19:24:13 markus Exp $ */
+/* $OpenBSD: pfkeyv2.c,v 1.144 2015/05/23 12:38:53 markus Exp $ */
 
 /*
  *     @(#)COPYRIGHT   1.1 (NRL) 17 January 1995
@@ -523,11 +523,10 @@ pfkeyv2_get(struct tdb *sa, void **headers, void **buffer, int *lenp)
        i += sizeof(struct sadb_address) + PADUP(SA_LEN(&sa->tdb_src.sa));
        i += sizeof(struct sadb_address) + PADUP(SA_LEN(&sa->tdb_dst.sa));
 
-       if (sa->tdb_srcid)
-               i += sizeof(struct sadb_ident) + PADUP(sa->tdb_srcid->ref_len);
-
-       if (sa->tdb_dstid)
-               i += sizeof(struct sadb_ident) + PADUP(sa->tdb_dstid->ref_len);
+       if (sa->tdb_ids) {
+               i += sizeof(struct sadb_ident) + PADUP(sa->tdb_ids->id_local->len);
+               i += sizeof(struct sadb_ident) + PADUP(sa->tdb_ids->id_remote->len);
+       }
 
        if (sa->tdb_amxkey)
                i += sizeof(struct sadb_key) + PADUP(sa->tdb_amxkeylen);
@@ -613,17 +612,9 @@ pfkeyv2_get(struct tdb *sa, void **headers, void **buffer, int *lenp)
        headers[SADB_EXT_ADDRESS_DST] = p;
        export_address(&p, (struct sockaddr *) &sa->tdb_dst);
 
-       /* Export source identity, if present */
-       if (sa->tdb_srcid) {
-               headers[SADB_EXT_IDENTITY_SRC] = p;
-               export_identity(&p, &sa->tdb_srcid);
-       }
-
-       /* Export destination identity, if present */
-       if (sa->tdb_dstid) {
-               headers[SADB_EXT_IDENTITY_DST] = p;
-               export_identity(&p, &sa->tdb_dstid);
-       }
+       /* Export source/destination identities, if present */
+       if (sa->tdb_ids)
+               export_identities(&p, sa->tdb_ids, sa->tdb_ids_swapped, headers);
 
        /* Export authentication key, if present */
        if (sa->tdb_amxkey) {
@@ -809,7 +800,7 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
        struct sadb_spirange *sprng;
        struct sadb_sa *ssa;
        struct sadb_supported *ssup;
-       struct sadb_ident *sid;
+       struct sadb_ident *sid, *did;
 
        u_int rdomain;
 
@@ -995,9 +986,10 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
                            PFKEYV2_AUTHENTICATION_KEY);
                        import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
                            PFKEYV2_ENCRYPTION_KEY);
-                       import_identity(&newsa->tdb_srcid,
-                           headers[SADB_EXT_IDENTITY_SRC]);
-                       import_identity(&newsa->tdb_dstid,
+                       newsa->tdb_ids_swapped = 1; /* only on TDB_UPDATE */
+                       import_identities(&newsa->tdb_ids,
+                           newsa->tdb_ids_swapped,
+                           headers[SADB_EXT_IDENTITY_SRC],
                            headers[SADB_EXT_IDENTITY_DST]);
                        import_flow(&newsa->tdb_filter, &newsa->tdb_filtermask,
                            headers[SADB_X_EXT_SRC_FLOW],
@@ -1151,9 +1143,9 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
                        import_key(&ii, headers[SADB_EXT_KEY_ENCRYPT],
                            PFKEYV2_ENCRYPTION_KEY);
 
-                       import_identity(&newsa->tdb_srcid,
-                           headers[SADB_EXT_IDENTITY_SRC]);
-                       import_identity(&newsa->tdb_dstid,
+                       import_identities(&newsa->tdb_ids,
+                           newsa->tdb_ids_swapped,
+                           headers[SADB_EXT_IDENTITY_SRC],
                            headers[SADB_EXT_IDENTITY_DST]);
 
                        import_flow(&newsa->tdb_filter, &newsa->tdb_filtermask,
@@ -1607,19 +1599,15 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
 
                ipo->ipo_sproto = SADB_X_GETSPROTO(smsg->sadb_msg_satype);
 
-               if (ipo->ipo_srcid) {
-                       ipsp_reffree(ipo->ipo_srcid);
-                       ipo->ipo_srcid = NULL;
-               }
-
-               if (ipo->ipo_dstid) {
-                       ipsp_reffree(ipo->ipo_dstid);
-                       ipo->ipo_dstid = NULL;
+               if (ipo->ipo_ids) {
+                       ipsp_ids_free(ipo->ipo_ids);
+                       ipo->ipo_ids = NULL;
                }
 
-               if ((sid = headers[SADB_EXT_IDENTITY_SRC]) != NULL) {
-                       import_identity(&ipo->ipo_srcid, sid);
-                       if (ipo->ipo_srcid == 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) {
                                if (exists)
                                        ipsec_delete_policy(ipo);
                                else
@@ -1630,23 +1618,6 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
                        }
                }
 
-               if ((sid = headers[SADB_EXT_IDENTITY_DST]) != NULL) {
-                       import_identity(&ipo->ipo_dstid, sid);
-                       if (ipo->ipo_dstid == NULL) {
-                               if (exists)
-                                       ipsec_delete_policy(ipo);
-                               else {
-                                       if (ipo->ipo_dstid)
-                                               ipsp_reffree(ipo->ipo_dstid);
-                                       pool_put(&ipsec_policy_pool, ipo);
-                               }
-
-                               splx(s);
-                               rval = ENOBUFS;
-                               goto ret;
-                       }
-               }
-
                /* Flow type */
                if (!exists) {
                        /* Add SPD entry */
@@ -1666,10 +1637,8 @@ pfkeyv2_send(struct socket *socket, void *message, int len)
                                        TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head,
                                            ipo, ipo_tdb_next);
 
-                               if (ipo->ipo_srcid)
-                                       ipsp_reffree(ipo->ipo_srcid);
-                               if (ipo->ipo_dstid)
-                                       ipsp_reffree(ipo->ipo_dstid);
+                               if (ipo->ipo_ids)
+                                       ipsp_ids_free(ipo->ipo_ids);
                                pool_put(&ipsec_policy_pool, ipo);
 
                                splx(s);
@@ -1805,11 +1774,10 @@ pfkeyv2_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw,
            sizeof(struct sadb_address) + PADUP(SA_LEN(&gw->sa)) +
            sizeof(struct sadb_prop) + 1 * sizeof(struct sadb_comb);
 
-       if (ipo->ipo_srcid)
-               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_srcid->ref_len);
-
-       if (ipo->ipo_dstid)
-               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_dstid->ref_len);
+       if (ipo->ipo_ids) {
+               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
+               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
+       }
 
        /* Allocate */
        if (!(p = malloc(i, M_PFKEY, M_NOWAIT | M_ZERO))) {
@@ -1856,15 +1824,8 @@ pfkeyv2_acquire(struct ipsec_policy *ipo, union sockaddr_union *gw,
        bcopy(gw, headers[SADB_EXT_ADDRESS_DST] + sizeof(struct sadb_address),
            SA_LEN(&gw->sa));
 
-       if (ipo->ipo_srcid) {
-               headers[SADB_EXT_IDENTITY_SRC] = p;
-               export_identity(&p, &ipo->ipo_srcid);
-       }
-
-       if (ipo->ipo_dstid) {
-               headers[SADB_EXT_IDENTITY_DST] = p;
-               export_identity(&p, &ipo->ipo_dstid);
-       }
+       if (ipo->ipo_ids)
+               export_identities(&p, ipo->ipo_ids, 0, headers);
 
        headers[SADB_EXT_PROPOSAL] = p;
        p += sizeof(struct sadb_prop);
@@ -2200,10 +2161,10 @@ pfkeyv2_dump_policy(struct ipsec_policy *ipo, void **headers, void **buffer,
                return (EINVAL);
        }
 
-       if (ipo->ipo_srcid)
-               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_srcid->ref_len);
-       if (ipo->ipo_dstid)
-               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_dstid->ref_len);
+       if (ipo->ipo_ids) {
+               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_local->len);
+               i += sizeof(struct sadb_ident) + PADUP(ipo->ipo_ids->id_remote->len);
+       }
 
        if (lenp)
                *lenp = i;
@@ -2237,14 +2198,8 @@ pfkeyv2_dump_policy(struct ipsec_policy *ipo, void **headers, void **buffer,
 
        /* Add ids only when we are root. */
        perm = suser(curproc, 0);
-       if (perm == 0 && ipo->ipo_srcid) {
-               headers[SADB_EXT_IDENTITY_SRC] = p;
-               export_identity(&p, &ipo->ipo_srcid);
-       }
-       if (perm == 0 && ipo->ipo_dstid) {
-               headers[SADB_EXT_IDENTITY_DST] = p;
-               export_identity(&p, &ipo->ipo_dstid);
-       }
+       if (perm == 0 && ipo->ipo_ids)
+               export_identities(&p, ipo->ipo_ids, 0, headers);
 
        rval = 0;
 ret:
index 8e96f0c..3d08bd3 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfkeyv2.h,v 1.68 2015/04/17 10:04:37 mikeb Exp $ */
+/* $OpenBSD: pfkeyv2.h,v 1.69 2015/05/23 12:38:53 markus Exp $ */
 /*
  *     @(#)COPYRIGHT   1.1 (NRL) January 1998
  * 
@@ -424,7 +424,7 @@ int pfkeyv2_sysctl_policydumper(struct ipsec_policy *, void *);
 int pfdatatopacket(void *, int, struct mbuf **);
 
 void export_address(void **, struct sockaddr *);
-void export_identity(void **, struct ipsec_ref **);
+void export_identities(void **, struct ipsec_ids *, int, void **);
 void export_lifetime(void **, struct tdb *, int);
 void export_sa(void **, struct tdb *);
 void export_flow(void **, u_int8_t, struct sockaddr_encap *,
@@ -435,7 +435,8 @@ void export_tag(void **, struct tdb *);
 void export_tap(void **, struct tdb *);
 
 void import_address(struct sockaddr *, struct sadb_address *);
-void import_identity(struct ipsec_ref **, struct sadb_ident *);
+void import_identities(struct ipsec_ids **, int, struct sadb_ident *,
+    struct sadb_ident *);
 void import_key(struct ipsecinit *, struct sadb_key *, int);
 void import_lifetime(struct tdb *, struct sadb_lifetime *, int);
 void import_sa(struct tdb *, struct sadb_sa *, struct ipsecinit *);
index 9fa4920..946c11a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfkeyv2_convert.c,v 1.50 2015/04/17 10:04:37 mikeb Exp $      */
+/*     $OpenBSD: pfkeyv2_convert.c,v 1.51 2015/05/23 12:38:53 markus Exp $     */
 /*
  * The author of this code is Angelos D. Keromytis (angelos@keromytis.org)
  *
@@ -702,46 +702,64 @@ export_address(void **p, struct sockaddr *sa)
 /*
  * Import an identity payload into the TDB.
  */
-void
-import_identity(struct ipsec_ref **ipr, struct sadb_ident *sadb_ident)
+static void
+import_identity(struct ipsec_id **id, struct sadb_ident *sadb_ident)
 {
        if (!sadb_ident)
                return;
 
-       *ipr = malloc(EXTLEN(sadb_ident) - sizeof(struct sadb_ident) +
-           sizeof(struct ipsec_ref), M_CREDENTIALS, M_WAITOK);
-       (*ipr)->ref_len = EXTLEN(sadb_ident) - sizeof(struct sadb_ident);
+       *id = malloc(EXTLEN(sadb_ident) - sizeof(struct sadb_ident) +
+           sizeof(struct ipsec_id), M_CREDENTIALS, M_WAITOK);
+       (*id)->len = EXTLEN(sadb_ident) - sizeof(struct sadb_ident);
 
        switch (sadb_ident->sadb_ident_type) {
        case SADB_IDENTTYPE_PREFIX:
-               (*ipr)->ref_type = IPSP_IDENTITY_PREFIX;
+               (*id)->type = IPSP_IDENTITY_PREFIX;
                break;
        case SADB_IDENTTYPE_FQDN:
-               (*ipr)->ref_type = IPSP_IDENTITY_FQDN;
+               (*id)->type = IPSP_IDENTITY_FQDN;
                break;
        case SADB_IDENTTYPE_USERFQDN:
-               (*ipr)->ref_type = IPSP_IDENTITY_USERFQDN;
+               (*id)->type = IPSP_IDENTITY_USERFQDN;
                break;
        default:
-               free(*ipr, M_CREDENTIALS, 0);
-               *ipr = NULL;
+               free(*id, M_CREDENTIALS, 0);
+               *id = NULL;
                return;
        }
-       (*ipr)->ref_count = 1;
-       (*ipr)->ref_malloctype = M_CREDENTIALS;
-       bcopy((void *) sadb_ident + sizeof(struct sadb_ident), (*ipr) + 1,
-           (*ipr)->ref_len);
+       bcopy((void *) sadb_ident + sizeof(struct sadb_ident), (*id) + 1,
+           (*id)->len);
 }
 
 void
-export_identity(void **p, struct ipsec_ref **ipr)
+import_identities(struct ipsec_ids **ids, int swapped,
+    struct sadb_ident *srcid, struct sadb_ident *dstid)
+{
+       struct ipsec_ids *tmp;
+
+       *ids = NULL;
+       tmp = malloc(sizeof(struct ipsec_ids), M_CREDENTIALS, M_WAITOK);
+       import_identity(&tmp->id_local, swapped ? dstid: srcid);
+       import_identity(&tmp->id_remote, swapped ? srcid: dstid);
+       if (tmp->id_local != NULL && tmp->id_remote != NULL) {
+               *ids = ipsp_ids_insert(tmp);
+               if (*ids == tmp)
+                       return;
+       }
+       free(tmp->id_local, M_CREDENTIALS, 0);
+       free(tmp->id_remote, M_CREDENTIALS, 0);
+       free(tmp, M_CREDENTIALS, 0);
+}
+
+static void
+export_identity(void **p, struct ipsec_id *id)
 {
        struct sadb_ident *sadb_ident = (struct sadb_ident *) *p;
 
        sadb_ident->sadb_ident_len = (sizeof(struct sadb_ident) +
-           PADUP((*ipr)->ref_len)) / sizeof(uint64_t);
+           PADUP(id->len)) / sizeof(uint64_t);
 
-       switch ((*ipr)->ref_type) {
+       switch (id->type) {
        case IPSP_IDENTITY_PREFIX:
                sadb_ident->sadb_ident_type = SADB_IDENTTYPE_PREFIX;
                break;
@@ -753,8 +771,18 @@ export_identity(void **p, struct ipsec_ref **ipr)
                break;
        }
        *p += sizeof(struct sadb_ident);
-       bcopy((*ipr) + 1, *p, (*ipr)->ref_len);
-       *p += PADUP((*ipr)->ref_len);
+       bcopy(id + 1, *p, id->len);
+       *p += PADUP(id->len);
+}
+
+void
+export_identities(void **p, struct ipsec_ids *ids, int swapped,
+    void **headers)
+{
+       headers[SADB_EXT_IDENTITY_SRC] = *p;
+       export_identity(p, swapped ? ids->id_remote : ids->id_local);
+       headers[SADB_EXT_IDENTITY_DST] = *p;
+       export_identity(p, swapped ? ids->id_local : ids->id_remote);
 }
 
 /* ... */
index c39c337..499b958 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_ipsp.c,v 1.213 2015/04/17 11:04:01 mikeb Exp $     */
+/*     $OpenBSD: ip_ipsp.c,v 1.214 2015/05/23 12:38:53 markus Exp $    */
 /*
  * The authors of this code are John Ioannidis (ji@tla.org),
  * Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -92,6 +92,19 @@ struct ipsec_policy_head ipsec_policy_head =
 struct ipsec_acquire_head ipsec_acquire_head =
     TAILQ_HEAD_INITIALIZER(ipsec_acquire_head);
 
+u_int32_t ipsec_ids_next_flow = 1;     /* may not be zero */
+int ipsec_ids_idle = 100;              /* keep free ids for 100s */
+struct ipsec_ids_tree ipsec_ids_tree;
+struct ipsec_ids_flows ipsec_ids_flows;
+
+void ipsp_ids_timeout(void *);
+static int ipsp_ids_cmp(struct ipsec_ids *, struct ipsec_ids *);
+static int ipsp_ids_flow_cmp(struct ipsec_ids *, struct ipsec_ids *);
+RB_PROTOTYPE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
+RB_PROTOTYPE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
+RB_GENERATE(ipsec_ids_tree, ipsec_ids, id_node_flow, ipsp_ids_cmp);
+RB_GENERATE(ipsec_ids_flows, ipsec_ids, id_node_id, ipsp_ids_flow_cmp);
+
 /*
  * This is the proper place to define the various encapsulation transforms.
  */
@@ -331,19 +344,13 @@ gettdbbysrcdst(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
  */
 int
 ipsp_aux_match(struct tdb *tdb,
-    struct ipsec_ref *psrcid,
-    struct ipsec_ref *pdstid,
+    struct ipsec_ids *ids,
     struct sockaddr_encap *pfilter,
     struct sockaddr_encap *pfiltermask)
 {
-       if (psrcid != NULL)
-               if (tdb->tdb_srcid == NULL ||
-                   !ipsp_ref_match(tdb->tdb_srcid, psrcid))
-                       return 0;
-
-       if (pdstid != NULL)
-               if (tdb->tdb_dstid == NULL ||
-                   !ipsp_ref_match(tdb->tdb_dstid, pdstid))
+       if (ids != NULL)
+               if (tdb->tdb_ids == NULL ||
+                   !ipsp_ids_match(tdb->tdb_ids, ids))
                        return 0;
 
        /* Check for filter matches. */
@@ -372,7 +379,7 @@ ipsp_aux_match(struct tdb *tdb,
  */
 struct tdb *
 gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
-    struct ipsec_ref *srcid, struct ipsec_ref *dstid,
+    struct ipsec_ids *ids,
     struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
 {
        u_int32_t hashval;
@@ -389,8 +396,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
                    ((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
                    (!memcmp(&tdbp->tdb_dst, dst, SA_LEN(&dst->sa)))) {
                        /* Do IDs match ? */
-                       if (!ipsp_aux_match(tdbp, srcid, dstid, filter,
-                           filtermask))
+                       if (!ipsp_aux_match(tdbp, ids, filter, filtermask))
                                continue;
                        break;
                }
@@ -404,7 +410,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, u_int8_t sproto,
  */
 struct tdb *
 gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
-    struct ipsec_ref *srcid, struct ipsec_ref *dstid,
+    struct ipsec_ids *ids,
     struct sockaddr_encap *filter, struct sockaddr_encap *filtermask)
 {
        u_int32_t hashval;
@@ -421,7 +427,7 @@ gettdbbysrc(u_int rdomain, union sockaddr_union *src, u_int8_t sproto,
                    ((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
                    (!memcmp(&tdbp->tdb_src, src, SA_LEN(&src->sa)))) {
                        /* Check whether IDs match */
-                       if (!ipsp_aux_match(tdbp, dstid, srcid, filter,
+                       if (!ipsp_aux_match(tdbp, ids, filter,
                            filtermask))
                                continue;
                        break;
@@ -793,14 +799,9 @@ tdb_free(struct tdb *tdbp)
        timeout_del(&tdbp->tdb_stimer_tmo);
        timeout_del(&tdbp->tdb_sfirst_tmo);
 
-       if (tdbp->tdb_srcid) {
-               ipsp_reffree(tdbp->tdb_srcid);
-               tdbp->tdb_srcid = NULL;
-       }
-
-       if (tdbp->tdb_dstid) {
-               ipsp_reffree(tdbp->tdb_dstid);
-               tdbp->tdb_dstid = NULL;
+       if (tdbp->tdb_ids) {
+               ipsp_ids_free(tdbp->tdb_ids);
+               tdbp->tdb_ids = NULL;
        }
 
 #if NPF > 0
@@ -892,28 +893,118 @@ ipsp_is_unspecified(union sockaddr_union addr)
        }
 }
 
-/* Free reference-counted structure. */
+int
+ipsp_ids_match(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+       return a == b;
+}
+
+struct ipsec_ids *
+ipsp_ids_insert(struct ipsec_ids *ids)
+{
+       struct ipsec_ids *found;
+       u_int32_t start_flow;
+
+       found = RB_INSERT(ipsec_ids_tree, &ipsec_ids_tree, ids);
+       if (found) {
+               /* if refcount was zero, then timeout is running */
+               if (found->id_refcount++ == 0)
+                       timeout_del(&found->id_timeout);
+               DPRINTF(("%s: ids %p count %d\n", __func__,
+                   found, found->id_refcount));
+               return found;
+       }
+       ids->id_flow = start_flow = ipsec_ids_next_flow;
+       if (++ipsec_ids_next_flow == 0)
+               ipsec_ids_next_flow = 1;
+       while (RB_INSERT(ipsec_ids_flows, &ipsec_ids_flows, ids) != NULL) {
+               ids->id_flow = ipsec_ids_next_flow;
+               if (++ipsec_ids_next_flow == 0)
+                       ipsec_ids_next_flow = 1;
+               if (ipsec_ids_next_flow == start_flow) {
+                       DPRINTF(("ipsec_ids_next_flow exhausted %u\n",
+                           ipsec_ids_next_flow));
+                       return NULL;
+               }
+       }
+       ids->id_refcount = 1;
+       DPRINTF(("%s: new ids %p flow %u\n", __func__, ids, ids->id_flow));
+       timeout_set(&ids->id_timeout, ipsp_ids_timeout, ids);
+       return ids;
+}
+
+struct ipsec_ids *
+ipsp_ids_lookup(u_int32_t ipsecflowinfo)
+{
+       struct ipsec_ids        key;
+
+       key.id_flow = ipsecflowinfo;
+       return RB_FIND(ipsec_ids_flows, &ipsec_ids_flows, &key);
+}
+
+/* free ids only from delayed timeout */
 void
-ipsp_reffree(struct ipsec_ref *ipr)
+ipsp_ids_timeout(void *arg)
 {
-#ifdef DIAGNOSTIC
-       if (ipr->ref_count <= 0)
-               printf("ipsp_reffree: illegal reference count %d for "
-                   "object %p (len = %d, malloctype = %d)\n",
-                   ipr->ref_count, ipr, ipr->ref_len, ipr->ref_malloctype);
-#endif
-       if (--ipr->ref_count <= 0)
-               free(ipr, ipr->ref_malloctype, 0);
+       struct ipsec_ids *ids = arg;
+       int s;
+
+       DPRINTF(("%s: ids %p count %d\n", __func__, ids, ids->id_refcount));
+       KASSERT(ids->id_refcount == 0);
+       s = splsoftnet();
+       RB_REMOVE(ipsec_ids_tree, &ipsec_ids_tree, ids);
+       RB_REMOVE(ipsec_ids_flows, &ipsec_ids_flows, ids);
+       free(ids->id_local, M_CREDENTIALS, 0);
+       free(ids->id_remote, M_CREDENTIALS, 0);
+       free(ids, M_CREDENTIALS, 0);
+       splx(s);
 }
 
-/* Return true if the two structures match. */
-int
-ipsp_ref_match(struct ipsec_ref *ref1, struct ipsec_ref *ref2)
+/* decrements refcount, actual free happens in timeout */
+void
+ipsp_ids_free(struct ipsec_ids *ids)
 {
-       if (ref1->ref_type != ref2->ref_type ||
-           ref1->ref_len != ref2->ref_len ||
-           memcmp(ref1 + 1, ref2 + 1, ref1->ref_len))
-               return 0;
+       /*
+        * If the refcount becomes zero, then a timeout is started. This
+        * timeout must be cancelled if refcount is increased from zero.
+        */
+       DPRINTF(("%s: ids %p count %d\n", __func__, ids, ids->id_refcount));
+       KASSERT(ids->id_refcount > 0);
+       if (--ids->id_refcount == 0)
+               timeout_add_sec(&ids->id_timeout, ipsec_ids_idle);
+}
 
-       return 1;
+static int
+ipsp_id_cmp(struct ipsec_id *a, struct ipsec_id *b)
+{
+       if (a->type > b->type)
+               return 1;
+       if (a->type < b->type)
+               return -1;
+       if (a->len > b->len)
+               return 1;
+       if (a->len < b->len)
+               return -1;
+       return memcmp(a + 1, b + 1, a->len);
+}
+
+static int
+ipsp_ids_cmp(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+       int ret;
+
+       ret = ipsp_id_cmp(a->id_remote, b->id_remote);
+       if (ret != 0)
+               return ret;
+       return ipsp_id_cmp(a->id_local, b->id_local);
+}
+
+static int
+ipsp_ids_flow_cmp(struct ipsec_ids *a, struct ipsec_ids *b)
+{
+       if (a->id_flow > b->id_flow)
+               return 1;
+       if (a->id_flow < b->id_flow)
+               return -1;
+       return 0;
 }
index 365e985..3ff6c2d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_ipsp.h,v 1.169 2015/04/17 11:04:01 mikeb Exp $     */
+/*     $OpenBSD: ip_ipsp.h,v 1.170 2015/05/23 12:38:53 markus Exp $    */
 /*
  * The authors of this code are John Ioannidis (ji@tla.org),
  * Angelos D. Keromytis (kermit@csd.uch.gr),
@@ -47,6 +47,7 @@ struct m_tag;
 #include <sys/types.h>
 #ifdef _KERNEL
 #include <sys/timeout.h>
+#include <sys/tree.h>
 #endif
 #include <sys/queue.h>
 #include <netinet/in.h>
@@ -166,13 +167,23 @@ struct sockaddr_encap {
 
 #define        SENT_LEN        sizeof(struct sockaddr_encap)
 
-struct ipsec_ref {
-       u_int16_t       ref_type;       /* Subtype of data */
-       int16_t         ref_len;        /* Length of data following */
-       int             ref_count;      /* Reference count */
-       int             ref_malloctype; /* malloc(9) type, for freeing */
+struct ipsec_id {
+       u_int16_t       type;           /* Subtype of data */
+       int16_t         len;            /* Length of data following */
 };
 
+struct ipsec_ids {
+       RB_ENTRY(ipsec_ids)     id_node_id;
+       RB_ENTRY(ipsec_ids)     id_node_flow;
+       struct ipsec_id         *id_local;
+       struct ipsec_id         *id_remote;
+       u_int32_t               id_flow;
+       int                     id_refcount;
+       struct timeout          id_timeout;
+};
+RB_HEAD(ipsec_ids_flows, ipsec_ids);
+RB_HEAD(ipsec_ids_tree, ipsec_ids);
+
 struct ipsec_acquire {
        union sockaddr_union            ipa_addr;
        u_int32_t                       ipa_seq;
@@ -212,8 +223,7 @@ struct ipsec_policy {
 
        struct tdb              *ipo_tdb;               /* Cached entry */
 
-       struct ipsec_ref        *ipo_srcid;
-       struct ipsec_ref        *ipo_dstid;
+       struct ipsec_ids        *ipo_ids;
 
        TAILQ_HEAD(ipo_acquires_head, ipsec_acquire) ipo_acquires; /* List of acquires */
        TAILQ_ENTRY(ipsec_policy)       ipo_tdb_next;   /* List TDB policies */
@@ -328,8 +338,8 @@ struct tdb {                                /* tunnel descriptor block */
 
        u_int8_t        tdb_iv[4];      /* Used for HALF-IV ESP */
 
-       struct ipsec_ref        *tdb_srcid;     /* Source ID for this SA */
-       struct ipsec_ref        *tdb_dstid;     /* Destination ID for this SA */
+       struct ipsec_ids        *tdb_ids;       /* Src/Dst ID for this SA */
+       int             tdb_ids_swapped;        /* XXX */
 
        u_int32_t       tdb_mtu;        /* MTU at this point in the chain */
        u_int64_t       tdb_mtutimeout; /* When to ignore this entry */
@@ -448,10 +458,10 @@ uint32_t reserve_spi(u_int, u_int32_t, u_int32_t, union sockaddr_union *,
                union sockaddr_union *, u_int8_t, int *);
 struct tdb *gettdb(u_int, u_int32_t, union sockaddr_union *, u_int8_t);
 struct tdb *gettdbbydst(u_int, union sockaddr_union *, u_int8_t,
-               struct ipsec_ref *, struct ipsec_ref *,
+               struct ipsec_ids *,
                struct sockaddr_encap *, struct sockaddr_encap *);
 struct tdb *gettdbbysrc(u_int, union sockaddr_union *, u_int8_t,
-               struct ipsec_ref *, struct ipsec_ref *,
+               struct ipsec_ids *,
                struct sockaddr_encap *, struct sockaddr_encap *);
 struct tdb *gettdbbysrcdst(u_int, u_int32_t, union sockaddr_union *,
                union sockaddr_union *, u_int8_t);
@@ -541,10 +551,12 @@ struct    tdb *ipsp_spd_lookup(struct mbuf *, int, int, int *, int,
 struct tdb *ipsp_spd_inp(struct mbuf *, int, int, int *, int,
            struct tdb *, struct inpcb *, struct ipsec_policy *);
 int    ipsp_is_unspecified(union sockaddr_union);
-int    ipsp_ref_match(struct ipsec_ref *, struct ipsec_ref *);
-void   ipsp_reffree(struct ipsec_ref *);
-int    ipsp_aux_match(struct tdb *, struct ipsec_ref *, struct ipsec_ref *,
+int    ipsp_aux_match(struct tdb *, struct ipsec_ids *,
            struct sockaddr_encap *, struct sockaddr_encap *);
+int    ipsp_ids_match(struct ipsec_ids *, struct ipsec_ids *);
+struct ipsec_ids *ipsp_ids_insert(struct ipsec_ids *);
+struct ipsec_ids *ipsp_ids_lookup(u_int32_t);
+void   ipsp_ids_free(struct ipsec_ids *);
 
 int    ipsec_common_input(struct mbuf *, int, int, int, int, int);
 int    ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int);
index 6ade3de..1bd87d0 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_spd.c,v 1.84 2015/04/30 20:12:33 millert Exp $ */
+/* $OpenBSD: ip_spd.c,v 1.85 2015/05/23 12:38:53 markus Exp $ */
 /*
  * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
  *
@@ -84,8 +84,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
        union sockaddr_union sdst, ssrc;
        struct sockaddr_encap *ddst, dst;
        struct ipsec_policy *ipo;
-       struct ipsec_ref *dstid = NULL, *srcid = NULL;
-       struct tdb *tdbin = NULL;
+       struct ipsec_ids *ids = NULL;
        int signore = 0, dignore = 0;
        u_int rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
 
@@ -343,17 +342,8 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
                        }
                }
 
-               /*
-                * Fetch the incoming TDB based on the SPI passed
-                * in ipsecflow and use it's dstid when looking
-                * up the outgoing TDB.
-                */
-               if (ipsecflowinfo &&
-                  (tdbin = gettdb(rdomain, ipsecflowinfo, &ssrc,
-                   ipo->ipo_sproto)) != NULL) {
-                       srcid = tdbin->tdb_dstid;
-                       dstid = tdbin->tdb_srcid;
-               }
+               if (ipsecflowinfo)
+                       ids = ipsp_ids_lookup(ipsecflowinfo);
 
                /* Check that the cached TDB (if present), is appropriate. */
                if (ipo->ipo_tdb) {
@@ -365,8 +355,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
                                goto nomatchout;
 
                        if (!ipsp_aux_match(ipo->ipo_tdb,
-                           srcid ? srcid : ipo->ipo_srcid,
-                           dstid ? dstid : ipo->ipo_dstid,
+                           ids ? ids : ipo->ipo_ids,
                            &ipo->ipo_addr, &ipo->ipo_mask))
                                goto nomatchout;
 
@@ -402,8 +391,7 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
                            gettdbbydst(rdomain,
                                dignore ? &sdst : &ipo->ipo_dst,
                                ipo->ipo_sproto,
-                               srcid ? srcid : ipo->ipo_srcid,
-                               dstid ? dstid : ipo->ipo_dstid,
+                               ids ? ids: ipo->ipo_ids,
                                &ipo->ipo_addr, &ipo->ipo_mask);
                        if (ipo->ipo_tdb) {
                                TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head,
@@ -455,21 +443,11 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
                            (ipo->ipo_sproto != tdbp->tdb_sproto))
                                goto nomatchin;
 
-                       /* Match source ID. */
-                       if (ipo->ipo_srcid) {
-                               if (tdbp->tdb_dstid == NULL ||
-                                   !ipsp_ref_match(ipo->ipo_srcid,
-                                       tdbp->tdb_dstid))
-                                       goto nomatchin;
-                       }
-
-                       /* Match destination ID. */
-                       if (ipo->ipo_dstid) {
-                               if (tdbp->tdb_srcid == NULL ||
-                                   !ipsp_ref_match(ipo->ipo_dstid,
-                                       tdbp->tdb_srcid))
+                       /* Match source/dest IDs. */
+                       if (ipo->ipo_ids)
+                               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)
@@ -515,9 +493,8 @@ ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction,
                        ipo->ipo_tdb =
                            gettdbbysrc(rdomain,
                                dignore ? &ssrc : &ipo->ipo_dst,
-                               ipo->ipo_sproto, ipo->ipo_srcid,
-                               ipo->ipo_dstid, &ipo->ipo_addr,
-                               &ipo->ipo_mask);
+                               ipo->ipo_sproto, ipo->ipo_ids,
+                               &ipo->ipo_addr, &ipo->ipo_mask);
                        if (ipo->ipo_tdb)
                                TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head,
                                    ipo, ipo_tdb_next);
@@ -600,10 +577,8 @@ ipsec_delete_policy(struct ipsec_policy *ipo)
 
        TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list);
 
-       if (ipo->ipo_srcid)
-               ipsp_reffree(ipo->ipo_srcid);
-       if (ipo->ipo_dstid)
-               ipsp_reffree(ipo->ipo_dstid);
+       if (ipo->ipo_ids)
+               ipsp_ids_free(ipo->ipo_ids);
 
        ipsec_in_use--;
 
index ece6463..dd29e16 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: udp_usrreq.c,v 1.198 2015/04/16 19:24:13 markus Exp $ */
+/*     $OpenBSD: udp_usrreq.c,v 1.199 2015/05/23 12:38:53 markus Exp $ */
 /*     $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
 
 /*
@@ -615,8 +615,8 @@ udp_input(struct mbuf *m, ...)
                goto bad;
        }
        /* create ipsec options while we know that tdb cannot be modified */
-       if (tdb)
-               ipsecflowinfo = tdb->tdb_spi;
+       if (tdb && tdb->tdb_ids)
+               ipsecflowinfo = tdb->tdb_ids->id_flow;
 #endif /*IPSEC */
 
        opts = NULL;