the following diff adds support for the 64-bit extended sequence
numbers specified by RFC 4303.  briefly, interesting points are:

 - only lower 32-bit part is transmitted so packet is not changed;
 - high-oder part is kept private and is used differently by HMAC
   and other MAC hashes (such as GCM/GMAC);
 - ESN makes sense only if you do MAC;
 - ESN handling in AES-GCM is kinda obscure but will hopefully
   improve once IETF people will figure out the answer to this
   question:
   http://www.ietf.org/mail-archive/web/ipsec/current/msg07711.html
 - ESN handling in AES-GCM-16 and AES-GMAC-16 is different and
   AES-GMAC-16 with ESN will currently not work correctly RFC-wise.

implementation is more or less straightforward and as simple as i
could do.

ESN is specified as a crypto transformation CRYPTO_ESN and only
those crypto-engines are selected who support it.  right now it's
software crypto only.  crypto_newsession is passed an additional
crypto descriptor 

high-order 32-bits are copied to a new field crd_esn of the crypto
descriptor (it's made to share the memory with crd_iv as they are
not used together: crd_iv is not used for MAC).  unfortunately,
i haven't figured out a nice abstraction for this.

replay window size is bumped to 64 bits (although arguably it's
still too small).

checkreplaywindow was rewritten to implement algorithm described
in the Appendix A of RFC 4303.

tests were done with OpenBSD, Linux and Windows (although it doesn't
negotiate ESN with IKEv2).

comments?  any objections for this to go in soonish?

diff --git sys/crypto/cryptodev.h sys/crypto/cryptodev.h
index 8f345f4..2d8a943 100644
--- sys/crypto/cryptodev.h
+++ sys/crypto/cryptodev.h
@@ -108,7 +108,8 @@
 #define CRYPTO_AES_192_GMAC    25
 #define CRYPTO_AES_256_GMAC    26
 #define CRYPTO_AES_GMAC                27
-#define CRYPTO_ALGORITHM_MAX   27 /* Keep updated */
+#define CRYPTO_ESN             28 /* Support for Extended Sequence Numbers */
+#define CRYPTO_ALGORITHM_MAX   28 /* Keep updated */
 
 /* Algorithm flags */
 #define        CRYPTO_ALG_FLAG_SUPPORTED       0x01 /* Algorithm is supported 
*/
@@ -121,7 +122,12 @@ struct cryptoini {
        int             cri_klen;       /* Key length, in bits */
        int             cri_rnd;        /* Algorithm rounds, where relevant */
        caddr_t         cri_key;        /* key to use */
-       u_int8_t        cri_iv[EALG_MAX_BLOCK_LEN];     /* IV to use */
+       union {
+               u_int8_t        iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
+               u_int8_t        esn[4];                 /* high-order ESN */
+       } u;
+#define cri_iv         u.iv
+#define cri_esn                u.esn
        struct cryptoini *cri_next;
 };
 
@@ -138,8 +144,10 @@ struct cryptodesc {
 #define        CRD_F_IV_EXPLICIT       0x04    /* IV explicitly provided */
 #define        CRD_F_DSA_SHA_NEEDED    0x08    /* Compute SHA-1 of buffer for 
DSA */
 #define CRD_F_COMP             0x10    /* Set when doing compression */
+#define CRD_F_ESN              0x20    /* Set when ESN field is provided */
 
        struct cryptoini        CRD_INI; /* Initialization/context data */
+#define crd_esn                CRD_INI.cri_esn
 #define crd_iv         CRD_INI.cri_iv
 #define crd_key                CRD_INI.cri_key
 #define crd_rnd                CRD_INI.cri_rnd
diff --git sys/crypto/cryptosoft.c sys/crypto/cryptosoft.c
index 6e2406b..2d3f983 100644
--- sys/crypto/cryptosoft.c
+++ sys/crypto/cryptosoft.c
@@ -446,6 +446,9 @@ swcr_authcompute(struct cryptop *crp, struct cryptodesc 
*crd,
        if (err)
                return err;
 
+       if (crd->crd_flags & CRD_F_ESN)
+               axf->Update(&ctx, crd->crd_esn, 4);
+
        switch (sw->sw_alg) {
        case CRYPTO_MD5_HMAC:
        case CRYPTO_SHA1_HMAC:
@@ -505,7 +508,7 @@ swcr_combined(struct cryptop *crp)
        struct uio *uio = NULL;
        caddr_t buf = (caddr_t)crp->crp_buf;
        uint32_t *blkp;
-       int i, blksz, ivlen, outtype, len;
+       int aadlen, blksz, i, ivlen, outtype, left, len;
 
        for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
                for (sw = swcr_sessions[crp->crp_sid & 0xffffffff];
@@ -576,10 +579,22 @@ swcr_combined(struct cryptop *crp)
                axf->Reinit(&ctx, iv, ivlen);
 
        /* Supply MAC with AAD */
-       for (i = 0; i < crda->crd_len; i += blksz) {
-               len = MIN(crda->crd_len - i, blksz);
-               COPYDATA(outtype, buf, crda->crd_skip + i, len, blk);
-               axf->Update(&ctx, blk, len);
+       aadlen = crda->crd_len;
+       if (crda->crd_flags & CRD_F_ESN)
+               aadlen += 4;
+       for (i = 0; i < aadlen; i += blksz) {
+               len = 0;
+               if (i < crda->crd_len) {
+                       len = MIN(crda->crd_len - i, blksz);
+                       COPYDATA(outtype, buf, crda->crd_skip + i, len, blk);
+               }
+               left = blksz - len;
+               if (crda->crd_flags & CRD_F_ESN && left > 0) {
+                       bcopy(crda->crd_esn, blk + len, MIN(left, aadlen - i));
+                       len += MIN(left, aadlen - i);
+               }
+               bzero(blk + len, blksz - len);
+               axf->Update(&ctx, blk, blksz);
        }
 
        if (exf->reinit)
@@ -937,6 +952,9 @@ swcr_newsession(u_int32_t *sid, struct cryptoini *cri)
                        cxf = &comp_algo_deflate;
                        (*swd)->sw_cxf = cxf;
                        break;
+               case CRYPTO_ESN:
+                       /* nothing to do */
+                       break;
                default:
                        swcr_freesession(i);
                        return EINVAL;
@@ -1192,6 +1210,7 @@ swcr_init(void)
        algs[CRYPTO_AES_128_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_AES_192_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_AES_256_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
+       algs[CRYPTO_ESN] = CRYPTO_ALG_FLAG_SUPPORTED;
 
        crypto_register(swcr_id, algs, swcr_newsession,
            swcr_freesession, swcr_process);
diff --git sys/net/if_pfsync.c sys/net/if_pfsync.c
index 534c9d3..2892818 100644
--- sys/net/if_pfsync.c
+++ sys/net/if_pfsync.c
@@ -1196,7 +1196,7 @@ pfsync_update_net_tdb(struct pfsync_tdb *pt)
        s = spltdb();
        tdb = gettdb(ntohs(pt->rdomain), pt->spi, &pt->dst, pt->sproto);
        if (tdb) {
-               pt->rpl = ntohl(pt->rpl);
+               pt->rpl = betoh64(pt->rpl);
                pt->cur_bytes = betoh64(pt->cur_bytes);
 
                /* Neither replay nor byte counter should ever decrease. */
@@ -2160,7 +2160,7 @@ pfsync_out_tdb(struct tdb *t, void *buf)
         * this edge case.
         */
 #define RPL_INCR 16384
-       ut->rpl = htonl(t->tdb_rpl + (ISSET(t->tdb_flags, TDBF_PFSYNC_RPL) ?
+       ut->rpl = htobe64(t->tdb_rpl + (ISSET(t->tdb_flags, TDBF_PFSYNC_RPL) ?
            RPL_INCR : 0));
        ut->cur_bytes = htobe64(t->tdb_cur_bytes);
        ut->sproto = t->tdb_sproto;
diff --git sys/net/if_pfsync.h sys/net/if_pfsync.h
index c22e14a..e8cfd10 100644
--- sys/net/if_pfsync.h
+++ sys/net/if_pfsync.h
@@ -212,7 +212,7 @@ struct pfsync_bus {
 struct pfsync_tdb {
        u_int32_t                       spi;
        union sockaddr_union            dst;
-       u_int32_t                       rpl;
+       u_int64_t                       rpl;
        u_int64_t                       cur_bytes;
        u_int8_t                        sproto;
        u_int8_t                        updates;
diff --git sys/net/pfkeyv2.h sys/net/pfkeyv2.h
index 641b9ff..f2b8b56 100644
--- sys/net/pfkeyv2.h
+++ sys/net/pfkeyv2.h
@@ -330,15 +330,16 @@ struct sadb_x_tap {
 #define SADB_X_CALG_LZS                3
 #define SADB_X_CALG_MAX                3
 
-#define SADB_SAFLAGS_PFS               0x001    /* perfect forward secrecy */
-#define SADB_X_SAFLAGS_HALFIV          0x002    /* Used for ESP-old */
-#define SADB_X_SAFLAGS_TUNNEL          0x004    /* Force tunneling */
-#define SADB_X_SAFLAGS_CHAINDEL        0x008    /* Delete whole SA chain */
-#define SADB_X_SAFLAGS_RANDOMPADDING    0x080    /* Random ESP padding */
-#define SADB_X_SAFLAGS_NOREPLAY         0x100    /* No replay counter */
-#define SADB_X_SAFLAGS_UDPENCAP         0x200    /* ESP in UDP  */
-
-#define SADB_X_POLICYFLAGS_POLICY       0x0001 /* This is a static policy */
+#define SADB_SAFLAGS_PFS               0x001   /* perfect forward secrecy */
+#define SADB_X_SAFLAGS_HALFIV          0x002   /* Used for ESP-old */
+#define SADB_X_SAFLAGS_TUNNEL          0x004   /* Force tunneling */
+#define SADB_X_SAFLAGS_CHAINDEL                0x008   /* Delete whole SA 
chain */
+#define SADB_X_SAFLAGS_RANDOMPADDING   0x080   /* Random ESP padding */
+#define SADB_X_SAFLAGS_NOREPLAY                0x100   /* No replay counter */
+#define SADB_X_SAFLAGS_UDPENCAP                0x200   /* ESP in UDP  */
+#define SADB_X_SAFLAGS_ESN             0x400   /* Extended Sequence Number */
+
+#define SADB_X_POLICYFLAGS_POLICY      0x0001  /* This is a static policy */
 
 #define SADB_IDENTTYPE_RESERVED     0
 #define SADB_IDENTTYPE_PREFIX       1
diff --git sys/net/pfkeyv2_convert.c sys/net/pfkeyv2_convert.c
index a029328..bc9fe7f 100644
--- sys/net/pfkeyv2_convert.c
+++ sys/net/pfkeyv2_convert.c
@@ -149,6 +149,9 @@ import_sa(struct tdb *tdb, struct sadb_sa *sadb_sa, struct 
ipsecinit *ii)
 
                if (sadb_sa->sadb_sa_flags & SADB_X_SAFLAGS_UDPENCAP)
                        tdb->tdb_flags |= TDBF_UDPENCAP;
+
+               if (sadb_sa->sadb_sa_flags & SADB_X_SAFLAGS_ESN)
+                       tdb->tdb_flags |= TDBF_ESN;
        }
 
        if (sadb_sa->sadb_sa_state != SADB_SASTATE_MATURE)
@@ -292,6 +295,9 @@ export_sa(void **p, struct tdb *tdb)
        if (tdb->tdb_flags & TDBF_UDPENCAP)
                sadb_sa->sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
 
+       if (tdb->tdb_flags & TDBF_ESN)
+               sadb_sa->sadb_sa_flags |= SADB_X_SAFLAGS_ESN;
+
        *p += sizeof(struct sadb_sa);
 }
 
diff --git sys/net/pfkeyv2_parsemessage.c sys/net/pfkeyv2_parsemessage.c
index b85eb91..58b84bd 100644
--- sys/net/pfkeyv2_parsemessage.c
+++ sys/net/pfkeyv2_parsemessage.c
@@ -430,7 +430,7 @@ pfkeyv2_parsemessage(void *p, int len, void **headers)
                                return (EINVAL);
                        }
 
-                       if (sadb_sa->sadb_sa_replay > 32) {
+                       if (sadb_sa->sadb_sa_replay > 64) {
                                DPRINTF(("pfkeyv2_parsemessage: unsupported "
                                    "replay window size %d in SA extension "
                                    "header %d\n", sadb_sa->sadb_sa_replay,
diff --git sys/netinet/ip_ah.c sys/netinet/ip_ah.c
index 0d0d4b6..78e0735 100644
--- sys/netinet/ip_ah.c
+++ sys/netinet/ip_ah.c
@@ -99,7 +99,7 @@ int
 ah_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
 {
        struct auth_hash *thash = NULL;
-       struct cryptoini cria;
+       struct cryptoini cria, crin;
 
        /* Authentication operation. */
        switch (ii->ii_authalg) {
@@ -166,6 +166,13 @@ ah_init(struct tdb *tdbp, struct xformsw *xsp, struct 
ipsecinit *ii)
        cria.cri_klen = ii->ii_authkeylen * 8;
        cria.cri_key = ii->ii_authkey;
 
+       if ((tdbp->tdb_wnd > 0) && !(tdbp->tdb_flags & TDBF_NOREPLAY) &&
+           (tdbp->tdb_flags & TDBF_ESN)) {
+               bzero(&crin, sizeof(crin));
+               crin.cri_alg = CRYPTO_ESN;
+               cria.cri_next = &crin;
+       }
+
        return crypto_newsession(&tdbp->tdb_cryptoid, &cria, 0);
 }
 
@@ -543,7 +550,7 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
        struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
        struct tdb_crypto *tc;
        struct m_tag *mtag;
-       u_int32_t btsx;
+       u_int32_t btsx, esn;
        u_int8_t hl;
        int rplen;
 
@@ -565,8 +572,8 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
                    sizeof(u_int32_t), (caddr_t) &btsx);
                btsx = ntohl(btsx);
 
-               switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
-                   tdb->tdb_wnd, &(tdb->tdb_bitmap), 0)) {
+               switch (checkreplaywindow(btsx, &tdb->tdb_rpl, tdb->tdb_wnd,
+                   &tdb->tdb_bitmap, &esn, tdb->tdb_flags & TDBF_ESN, 0)) {
                case 0: /* All's well. */
                        break;
 
@@ -585,15 +592,15 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
                            "SA %s/%08x\n", ipsp_address(tdb->tdb_dst),
                            ntohl(tdb->tdb_spi)));
 
+                       ahstat.ahs_replay++;
                        m_freem(m);
                        return ENOBUFS;
 
                default:
                        DPRINTF(("ah_input(): bogus value from "
-                           "checkreplaywindow32() in SA %s/%08x\n",
+                           "checkreplaywindow() in SA %s/%08x\n",
                            ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
 
-                       ahstat.ahs_replay++;
                        m_freem(m);
                        return ENOBUFS;
                }
@@ -652,6 +659,13 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
        crda->crd_key = tdb->tdb_amxkey;
        crda->crd_klen = tdb->tdb_amxkeylen * 8;
 
+       if ((tdb->tdb_wnd > 0) && !(tdb->tdb_flags & TDBF_NOREPLAY) &&
+           (tdb->tdb_flags & TDBF_ESN)) {
+               esn = htonl(esn);
+               bcopy(&esn, crda->crd_esn, 4);
+               crda->crd_flags |= CRD_F_ESN;
+       }
+
 #ifdef notyet
        /* Find out if we've already done crypto. */
        for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
@@ -745,7 +759,7 @@ ah_input_cb(void *op)
        struct cryptop *crp;
        struct m_tag *mtag;
        struct tdb *tdb;
-       u_int32_t btsx;
+       u_int32_t btsx, esn;
        u_int8_t prot;
        caddr_t ptr;
 
@@ -846,8 +860,8 @@ ah_input_cb(void *op)
                    sizeof(u_int32_t), (caddr_t) &btsx);
                btsx = ntohl(btsx);
 
-               switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
-                   tdb->tdb_wnd, &(tdb->tdb_bitmap), 1)) {
+               switch (checkreplaywindow(btsx, &tdb->tdb_rpl, tdb->tdb_wnd,
+                   &tdb->tdb_bitmap, &esn, tdb->tdb_flags & TDBF_ESN, 1)) {
                case 0: /* All's well. */
 #if NPFSYNC > 0
                        pfsync_update_tdb(tdb,0);
@@ -869,15 +883,15 @@ ah_input_cb(void *op)
                            "SA %s/%08x\n", ipsp_address(tdb->tdb_dst),
                            ntohl(tdb->tdb_spi)));
 
+                       ahstat.ahs_replay++;
                        error = ENOBUFS;
                        goto baddone;
 
                default:
                        DPRINTF(("ah_input_cb(): bogus value from "
-                           "checkreplaywindow32() in SA %s/%08x\n",
+                           "checkreplaywindow() in SA %s/%08x\n",
                            ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
 
-                       ahstat.ahs_replay++;
                        error = ENOBUFS;
                        goto baddone;
                }
@@ -1144,7 +1158,8 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf 
**mp, int skip,
        m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes, M_NOWAIT);
 
        if (!(tdb->tdb_flags & TDBF_NOREPLAY)) {
-               ah->ah_rpl = htonl(tdb->tdb_rpl++);
+               tdb->tdb_rpl++;
+               ah->ah_rpl = htonl((u_int32_t)(tdb->tdb_rpl & 0xffffffff));
 #if NPFSYNC > 0
                pfsync_update_tdb(tdb,1);
 #endif
@@ -1171,6 +1186,15 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf 
**mp, int skip,
        crda->crd_key = tdb->tdb_amxkey;
        crda->crd_klen = tdb->tdb_amxkeylen * 8;
 
+       if ((tdb->tdb_wnd > 0) && !(tdb->tdb_flags & TDBF_NOREPLAY) &&
+           (tdb->tdb_flags & TDBF_ESN)) {
+               u_int32_t esn;
+
+               esn = htonl((u_int32_t)(tdb->tdb_rpl >> 32));
+               bcopy(&esn, crda->crd_esn, 4);
+               crda->crd_flags |= CRD_F_ESN;
+       }
+
        /* Allocate IPsec-specific opaque crypto info. */
        if ((tdb->tdb_flags & TDBF_SKIPCRYPTO) == 0)
                tc = malloc(sizeof(*tc) + skip, M_XDATA, M_NOWAIT | M_ZERO);
diff --git sys/netinet/ip_esp.c sys/netinet/ip_esp.c
index 86b056b..03e5498 100644
--- sys/netinet/ip_esp.c
+++ sys/netinet/ip_esp.c
@@ -101,7 +101,7 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct 
ipsecinit *ii)
 {
        struct enc_xform *txform = NULL;
        struct auth_hash *thash = NULL;
-       struct cryptoini cria, crie;
+       struct cryptoini cria, crie, crin;
 
        if (!ii->ii_encalg && !ii->ii_authalg) {
                DPRINTF(("esp_init(): neither authentication nor encryption "
@@ -279,7 +279,15 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct 
ipsecinit *ii)
                bzero(&cria, sizeof(cria));
 
                cria.cri_alg = tdbp->tdb_authalgxform->type;
-               cria.cri_next = NULL;
+
+               if ((tdbp->tdb_wnd > 0) && !(tdbp->tdb_flags & TDBF_NOREPLAY) &&
+                   (tdbp->tdb_flags & TDBF_ESN)) {
+                       bzero(&crin, sizeof(crin));
+                       crin.cri_alg = CRYPTO_ESN;
+                       cria.cri_next = &crin;
+               } else
+                       cria.cri_next = NULL;
+
                cria.cri_klen = ii->ii_authkeylen * 8;
                cria.cri_key = ii->ii_authkey;
        }
@@ -328,7 +336,7 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
        struct tdb_crypto *tc;
        int plen, alen, hlen;
        struct m_tag *mtag;
-       u_int32_t btsx;
+       u_int32_t btsx, esn;
 
        /* Determine the ESP header length */
        if (tdb->tdb_flags & TDBF_NOREPLAY)
@@ -364,8 +372,8 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
                    (unsigned char *) &btsx);
                btsx = ntohl(btsx);
 
-               switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
-                   tdb->tdb_wnd, &(tdb->tdb_bitmap), 0)) {
+               switch (checkreplaywindow(btsx, &tdb->tdb_rpl, tdb->tdb_wnd,
+                   &tdb->tdb_bitmap, &esn, tdb->tdb_flags & TDBF_ESN, 0)) {
                case 0: /* All's well */
                        break;
 
@@ -379,12 +387,12 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
                case 3:
                        DPRINTF(("esp_input(): duplicate packet received in SA 
%s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
                        m_freem(m);
+                       espstat.esps_replay++;
                        return EACCES;
 
                default:
                        m_freem(m);
-                       DPRINTF(("esp_input(): bogus value from 
checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), 
ntohl(tdb->tdb_spi)));
-                       espstat.esps_replay++;
+                       DPRINTF(("esp_input(): bogus value from 
checkreplaywindow() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), 
ntohl(tdb->tdb_spi)));
                        return EACCES;
                }
        }
@@ -462,6 +470,13 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int 
protoff)
                crda->crd_key = tdb->tdb_amxkey;
                crda->crd_klen = tdb->tdb_amxkeylen * 8;
 
+               if ((tdb->tdb_wnd > 0) && !(tdb->tdb_flags & TDBF_NOREPLAY) &&
+                   (tdb->tdb_flags & TDBF_ESN)) {
+                       esn = htonl(esn);
+                       bcopy(&esn, crda->crd_esn, 4);
+                       crda->crd_flags |= CRD_F_ESN;
+               }
+
                if (espx && espx->type == CRYPTO_AES_GCM_16)
                        crda->crd_len = hlen - tdb->tdb_ivlen;
                else
@@ -536,7 +551,7 @@ esp_input_cb(void *op)
        struct cryptop *crp;
        struct m_tag *mtag;
        struct tdb *tdb;
-       u_int32_t btsx;
+       u_int32_t btsx, esn;
        caddr_t ptr;
 
        crp = (struct cryptop *) op;
@@ -619,8 +634,8 @@ esp_input_cb(void *op)
                    (unsigned char *) &btsx);
                btsx = ntohl(btsx);
 
-               switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
-                   tdb->tdb_wnd, &(tdb->tdb_bitmap), 1)) {
+               switch (checkreplaywindow(btsx, &tdb->tdb_rpl, tdb->tdb_wnd,
+                   &tdb->tdb_bitmap, &esn, tdb->tdb_flags & TDBF_ESN, 1)) {
                case 0: /* All's well */
 #if NPFSYNC > 0
                        pfsync_update_tdb(tdb,0);
@@ -636,12 +651,12 @@ esp_input_cb(void *op)
                case 2:
                case 3:
                        DPRINTF(("esp_input_cb(): duplicate packet received in 
SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+                       espstat.esps_replay++;
                        error = EACCES;
                        goto baddone;
 
                default:
-                       DPRINTF(("esp_input_cb(): bogus value from 
checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), 
ntohl(tdb->tdb_spi)));
-                       espstat.esps_replay++;
+                       DPRINTF(("esp_input_cb(): bogus value from 
checkreplaywindow() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), 
ntohl(tdb->tdb_spi)));
                        error = EACCES;
                        goto baddone;
                }
@@ -916,7 +931,9 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf 
**mp, int skip,
        /* Initialize ESP header. */
        bcopy((caddr_t) &tdb->tdb_spi, mtod(mo, caddr_t), sizeof(u_int32_t));
        if (!(tdb->tdb_flags & TDBF_NOREPLAY)) {
-               u_int32_t replay = htonl(tdb->tdb_rpl++);
+               u_int32_t replay;
+               tdb->tdb_rpl++;
+               replay = htonl((u_int32_t)tdb->tdb_rpl);
                bcopy((caddr_t) &replay, mtod(mo, caddr_t) + sizeof(u_int32_t),
                    sizeof(u_int32_t));
 #if NPFSYNC > 0
@@ -1032,6 +1049,15 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf 
**mp, int skip,
                crda->crd_key = tdb->tdb_amxkey;
                crda->crd_klen = tdb->tdb_amxkeylen * 8;
 
+               if ((tdb->tdb_wnd > 0) && !(tdb->tdb_flags & TDBF_NOREPLAY) &&
+                   (tdb->tdb_flags & TDBF_ESN)) {
+                       u_int32_t esn;
+
+                       esn = htonl((u_int32_t)(tdb->tdb_rpl >> 32));
+                       bcopy(&esn, crda->crd_esn, 4);
+                       crda->crd_flags |= CRD_F_ESN;
+               }
+
                if (espx && espx->type == CRYPTO_AES_GCM_16)
                        crda->crd_len = hlen - tdb->tdb_ivlen;
                else
@@ -1127,6 +1153,34 @@ esp_output_cb(void *op)
        return error;
 }
 
+static __inline int
+checkreplay(u_int64_t *bitmap, u_int32_t diff)
+{
+       if (*bitmap & (1ULL << diff))
+               return (1);
+       return (0);
+}
+
+static __inline void
+setreplay(u_int64_t *bitmap, u_int32_t diff, u_int32_t window, int wupdate)
+{
+       if (wupdate) {
+               if (diff < window)
+                       *bitmap = ((*bitmap) << diff) | 1;
+               else
+                       *bitmap = 1;
+       } else
+               *bitmap |= 1ULL << diff;
+}
+
+/*
+ * To prevent ESN desynchronization replay distance specifies maximum
+ * valid difference between the received SN and the last authenticated
+ * one.  It's arbitrary chosen to be 1000 packets, meaning that only
+ * up to 999 packets can be lost.
+ */
+#define REPLAY_DISTANCE (1000)
+
 /*
  * return 0 on success
  * return 1 for counter == 0
@@ -1134,46 +1188,85 @@ esp_output_cb(void *op)
  * return 3 for packet within current window but already received
  */
 int
-checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq,
-    u_int32_t window, u_int32_t *bitmap, int commit)
+checkreplaywindow(u_int32_t seq, u_int64_t *last, u_int32_t window,
+    u_int64_t *bitmap, u_int32_t *seqhigh, int esn, int commit)
 {
-       u_int32_t diff, llseq, lbitmap;
-
-       /* Just do the checking, without "committing" any changes. */
-       if (commit == 0) {
-               llseq = *lastseq;
-               lbitmap = *bitmap;
+       u_int32_t       tl, th, wl;
+       u_int32_t       seqh, diff;
 
-               lastseq = &llseq;
-               bitmap = &lbitmap;
-       }
+       tl = (u_int32_t)*last;
+       th = (u_int32_t)(*last >> 32);
 
-       seq -= initial;
+       /* Zero SN is not allowed */
+       if (seq == 0 && tl == 0 && th == 0)
+               return (1);
 
-       if (seq == 0)
-               return 1;
+       /* Current replay window starts here */
+       wl = tl - window + 1;
 
-       if (seq > *lastseq - initial) {
-               diff = seq - (*lastseq - initial);
-               if (diff < window)
-                       *bitmap = ((*bitmap) << diff) | 1;
-               else
-                       *bitmap = 1;
-               *lastseq = seq + initial;
-               return 0;
+       /*
+        * We keep the high part intact when:
+        * 1) the SN is within [wl, 0xffffffff] and the whole window is
+        *    within one subspace;
+        * 2) the SN is within [0, wl) and window spans two subspaces.
+        */
+       if ((tl >= window - 1 && seq >= wl) ||
+           (tl <  window - 1 && seq <  wl)) {
+               seqh = *seqhigh = th;
+               if (seq > tl) {
+                       if (seq - tl >= REPLAY_DISTANCE)
+                               return (2);
+                       if (commit) {
+                               setreplay(bitmap, seq - tl, window, 1);
+                               *last = ((u_int64_t)seqh << 32) | seq;
+                       }
+               } else {
+                       if (checkreplay(bitmap, tl - seq))
+                               return (3);
+                       if (commit)
+                               setreplay(bitmap, tl - seq, window, 0);
+               }
+               return (0);
        }
 
-       diff = *lastseq - initial - seq;
-       if (diff >= window) {
-               espstat.esps_wrap++;
-               return 2;
+       /* Can't wrap if not doing ESN */
+       if (!esn)
+               return (1);
+
+       /*
+        * SN is within [wl, 0xffffffff] and wl is within
+        * [0xffffffff-window, 0xffffffff].  This means we got a SN
+        * which is within our replay window, but in the previous
+        * subspace.
+        */
+       if (tl < window - 1 && seq >= wl) {
+               seqh = *seqhigh = th - 1;
+               diff = (u_int32_t)((((u_int64_t)th << 32) | tl) -
+                   (((u_int64_t)seqh << 32) | seq));
+               if (checkreplay(bitmap, diff))
+                       return (3);
+               if (commit)
+                       setreplay(bitmap, diff, window, 0);
+               return (0);
        }
 
-       if ((*bitmap) & (((u_int32_t) 1) << diff)) {
-               espstat.esps_replay++;
-               return 3;
+       /*
+        * SN has wrapped and the last authenticated SN is in the old
+        * subspace.
+        */
+
+       if (seq - tl >= REPLAY_DISTANCE)
+               return (2);
+
+       seqh = *seqhigh = th + 1;
+       if (seqh == 0)          /* Don't let high bit to wrap */
+               return (1);
+       if (commit) {
+               diff = (u_int32_t)((((u_int64_t)seqh << 32) | seq) -
+                   (((u_int64_t)th << 32) | tl));
+               setreplay(bitmap, diff, window, 1);
+               *last = ((u_int64_t)seqh << 32) | seq;
        }
 
-       *bitmap |= (((u_int32_t) 1) << diff);
-       return 0;
+       return (0);
 }
diff --git sys/netinet/ip_ipsp.h sys/netinet/ip_ipsp.h
index 0e9b8a1..c9e5f89 100644
--- sys/netinet/ip_ipsp.h
+++ sys/netinet/ip_ipsp.h
@@ -307,6 +307,7 @@ struct tdb {                                /* tunnel 
descriptor block */
 #define        TDBF_UDPENCAP           0x20000 /* UDP encapsulation */
 #define        TDBF_PFSYNC             0x40000 /* TDB will be synced */
 #define        TDBF_PFSYNC_RPL         0x80000 /* Replay counter should be 
bumped */
+#define TDBF_ESN               0x100000 /* 64-bit sequence numbers (ESN) */
 
        u_int32_t       tdb_flags;      /* Flags related to this TDB */
 
@@ -355,8 +356,8 @@ struct tdb {                                /* tunnel 
descriptor block */
        u_int8_t        *tdb_amxkey;    /* Raw authentication key */
        u_int8_t        *tdb_emxkey;    /* Raw encryption key */
 
-       u_int32_t       tdb_rpl;        /* Replay counter */
-       u_int32_t       tdb_bitmap;     /* Used for replay sliding window */
+       u_int64_t       tdb_rpl;        /* Replay counter */
+       u_int64_t       tdb_bitmap;     /* Used for replay sliding window */
 
        u_int8_t        tdb_iv[4];      /* Used for HALF-IV ESP */
 
@@ -624,8 +625,8 @@ extern int tcp_signature_tdb_output(struct mbuf *, struct 
tdb *,
     struct mbuf **, int, int);
 
 /* Replay window */
-extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t,
-    u_int32_t *, int);
+extern int checkreplaywindow(u_int32_t, u_int64_t *, u_int32_t, u_int64_t *,
+    u_int32_t *, int, int);
 
 extern unsigned char ipseczeroes[];

Reply via email to