Diff below adds & exports per-TDB counters via ipsecctl(8) and adds &
exports global IPsec counters via netstat(1).  The goal of this diff
is to introduce a new framework to improve monitoring & error reporting
of IPsec traffic.

Here's an example of "# ipsecctl -s sa -v":

ipcomp tunnel from 10.188.106.17 to 10.188.106.70 spi 0x00008841 comp deflate
[bundle ipcomp to 10.188.106.70 spi 0x00008841 with esp to 10.188.106.70 spi 
0x10018841]                                                                     
            
        sa: cpi 0x00008841 comp deflate
                state mature replay 0 flags 0x4<tunnel>
        [...]
        counter:
                4 input packets
                1442 input bytes
                4232 input bytes, decompressed

"# netstat -ss" now contains a new global 'ipsec' section:

ipsec:
        24 active IPsec tunnels                  
        427 input IPsec packets                         
        578 output IPsec packets         
        94368 input bytes                        
        ...

Most of the kernel bits are refactoring required to count drops.  I
tried to keep the changes as small as possible.  But there's room
for sharing more code.

I'm using a new SADB extension to export counters to userland.

I'm of course really interested in adding/deleting/moving more counters. 
But I'd like to continue working on that in-tree as the size of the diff
is starting to be big.

As discussed with bluhm@ the '{de,un}compressed' counters might
confusing/not correctly named for AH & ESP because once there header
removed the size of the packet is (obviously) smaller.

Full diff below, if needed I can send smaller diffs to review.

Comments?  Oks?

Index: sbin/ipsecctl/pfkdump.c
===================================================================
RCS file: /cvs/src/sbin/ipsecctl/pfkdump.c,v
retrieving revision 1.46
diff -u -p -r1.46 pfkdump.c
--- sbin/ipsecctl/pfkdump.c     19 Apr 2017 15:59:38 -0000      1.46
+++ sbin/ipsecctl/pfkdump.c     3 Jul 2018 06:29:27 -0000
@@ -27,6 +27,8 @@
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/sysctl.h>
+#include <sys/queue.h>
+
 #include <net/pfkeyv2.h>
 #include <netinet/ip_ipsp.h>
 #include <netdb.h>
@@ -40,26 +42,27 @@
 #include "ipsecctl.h"
 #include "pfkey.h"
 
-static void    print_proto(struct sadb_ext *, struct sadb_msg *);
-static void    print_flow(struct sadb_ext *, struct sadb_msg *);
-static void    print_supp(struct sadb_ext *, struct sadb_msg *);
-static void    print_prop(struct sadb_ext *, struct sadb_msg *);
-static void    print_sens(struct sadb_ext *, struct sadb_msg *);
-static void    print_spir(struct sadb_ext *, struct sadb_msg *);
-static void    print_policy(struct sadb_ext *, struct sadb_msg *);
-static void    print_sa(struct sadb_ext *, struct sadb_msg *);
-static void    print_addr(struct sadb_ext *, struct sadb_msg *);
-static void    print_key(struct sadb_ext *, struct sadb_msg *);
-static void    print_life(struct sadb_ext *, struct sadb_msg *);
-static void    print_ident(struct sadb_ext *, struct sadb_msg *);
-static void    print_udpenc(struct sadb_ext *, struct sadb_msg *);
-static void    print_tag(struct sadb_ext *, struct sadb_msg *);
-static void    print_tap(struct sadb_ext *, struct sadb_msg *);
-static void    print_satype(struct sadb_ext *, struct sadb_msg *);
+static void    print_proto(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_flow(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_supp(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_prop(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_sens(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_spir(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_policy(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_sa(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_addr(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_key(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_life(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_ident(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_udpenc(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_tag(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_tap(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_satype(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_counter(struct sadb_ext *, struct sadb_msg *, int);
 
 static struct idname *lookup(struct idname *, u_int32_t);
 static char    *lookup_name(struct idname *, u_int32_t);
-static void    print_ext(struct sadb_ext *, struct sadb_msg *);
+static void    print_ext(struct sadb_ext *, struct sadb_msg *, int);
 
 void           pfkey_print_raw(u_int8_t *, ssize_t);
 static char    *print_flags(uint32_t);
@@ -69,7 +72,7 @@ struct sadb_ext *extensions[SADB_EXT_MAX
 struct idname {
        u_int32_t id;
        char *name;
-       void (*func)(struct sadb_ext *, struct sadb_msg *);
+       void (*func)(struct sadb_ext *, struct sadb_msg *, int);
 };
 
 struct idname ext_types[] = {
@@ -105,6 +108,7 @@ struct idname ext_types[] = {
        { SADB_X_EXT_TAG,               "tag",                  print_tag },
        { SADB_X_EXT_TAP,               "tap",                  print_tap },
        { SADB_X_EXT_SATYPE2,           "satype2",              print_satype },
+       { SADB_X_EXT_COUNTER,           "counter",              print_counter },
        { 0,                            NULL,                   NULL }
 };
 
@@ -234,7 +238,7 @@ lookup_name(struct idname *tab, u_int32_
 }
 
 static void
-print_ext(struct sadb_ext *ext, struct sadb_msg *msg)
+print_ext(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct idname *entry;
 
@@ -245,7 +249,7 @@ print_ext(struct sadb_ext *ext, struct s
        }
        printf("\t%s: ", entry->name);
        if (entry->func != NULL)
-               (*entry->func)(ext, msg);
+               (*entry->func)(ext, msg, opts);
        else
                printf("type %u len %u",
                    ext->sadb_ext_type, ext->sadb_ext_len);
@@ -280,7 +284,7 @@ print_flags(uint32_t flags)
 }
 
 static void
-print_sa(struct sadb_ext *ext, struct sadb_msg *msg)
+print_sa(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_sa *sa = (struct sadb_sa *)ext;
 
@@ -300,7 +304,7 @@ print_sa(struct sadb_ext *ext, struct sa
 
 /* ARGSUSED1 */
 static void
-print_addr(struct sadb_ext *ext, struct sadb_msg *msg)
+print_addr(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_address *addr = (struct sadb_address *)ext;
        struct sockaddr *sa;
@@ -332,7 +336,7 @@ print_addr(struct sadb_ext *ext, struct 
 
 /* ARGSUSED1 */
 static void
-print_key(struct sadb_ext *ext, struct sadb_msg *msg)
+print_key(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_key *key = (struct sadb_key *)ext;
        u_int8_t *data;
@@ -348,7 +352,7 @@ print_key(struct sadb_ext *ext, struct s
 
 /* ARGSUSED1 */
 static void
-print_life(struct sadb_ext *ext, struct sadb_msg *msg)
+print_life(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_lifetime *life = (struct sadb_lifetime *)ext;
 
@@ -360,7 +364,7 @@ print_life(struct sadb_ext *ext, struct 
 }
 
 static void
-print_proto(struct sadb_ext *ext, struct sadb_msg *msg)
+print_proto(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_protocol *proto = (struct sadb_protocol *)ext;
 
@@ -376,7 +380,7 @@ print_proto(struct sadb_ext *ext, struct
 
 /* ARGSUSED1 */
 static void
-print_flow(struct sadb_ext *ext, struct sadb_msg *msg)
+print_flow(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_protocol *proto = (struct sadb_protocol *)ext;
        char *dir = "unknown";
@@ -394,7 +398,7 @@ print_flow(struct sadb_ext *ext, struct 
 }
 
 static void
-print_tag(struct sadb_ext *ext, struct sadb_msg *msg)
+print_tag(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_x_tag *stag = (struct sadb_x_tag *)ext;
        char *p;
@@ -404,7 +408,7 @@ print_tag(struct sadb_ext *ext, struct s
 }
 
 static void
-print_tap(struct sadb_ext *ext, struct sadb_msg *msg)
+print_tap(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_x_tap *stap = (struct sadb_x_tap *)ext;
 
@@ -412,13 +416,35 @@ print_tap(struct sadb_ext *ext, struct s
 }
 
 static void
-print_satype(struct sadb_ext *ext, struct sadb_msg *msg)
+print_satype(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_protocol *proto = (struct sadb_protocol *)ext;
 
        printf("type %s", lookup_name(sa_types, proto->sadb_protocol_proto));
 }
 
+static void
+print_counter(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
+{
+       struct sadb_x_counter *scnt = (struct sadb_x_counter *)ext;
+
+       printf("\n");
+
+#define plural(n) ((n) != 1 ? "s" : "")
+#define p(f, m) if (scnt->f || opts & IPSECCTL_OPT_VERBOSE2) \
+       printf(m, scnt->f, plural(scnt->f))
+       p(sadb_x_cnt_ipackets, "\t\t%llu input packet%s\n");
+       p(sadb_x_cnt_opackets, "\t\t%llu output packet%s\n");
+       p(sadb_x_cnt_ibytes, "\t\t%llu input byte%s\n");
+       p(sadb_x_cnt_obytes, "\t\t%llu output byte%s\n");
+       p(sadb_x_cnt_idecompbytes, "\t\t%llu input byte%s, decompressed\n");
+       p(sadb_x_cnt_ouncompbytes, "\t\t%llu output byte%s, uncompressed\n");
+       p(sadb_x_cnt_idrops, "\t\t%llu packet%s dropped on input\n");
+       p(sadb_x_cnt_odrops, "\t\t%llu packet%s dropped on output\n");
+#undef p
+#undef plural
+}
+
 static char *
 alg_by_ext(u_int8_t ext_type, u_int8_t id)
 {
@@ -444,7 +470,7 @@ print_alg(struct sadb_alg *alg, u_int8_t
 
 /* ARGSUSED1 */
 static void
-print_supp(struct sadb_ext *ext, struct sadb_msg *msg)
+print_supp(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_supported *supported = (struct sadb_supported *)ext;
        struct sadb_alg *alg;
@@ -464,7 +490,7 @@ print_supp(struct sadb_ext *ext, struct 
 
 /* ARGSUSED1 */
 static void
-print_comb(struct sadb_comb *comb, struct sadb_msg *msg)
+print_comb(struct sadb_comb *comb, struct sadb_msg *msg, int opts)
 {
        printf("\t\tauth %s min %u max %u\n"
            "\t\tenc %s min %u max %u\n"
@@ -492,7 +518,7 @@ print_comb(struct sadb_comb *comb, struc
 
 /* ARGSUSED1 */
 static void
-print_prop(struct sadb_ext *ext, struct sadb_msg *msg)
+print_prop(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_prop *prop = (struct sadb_prop *)ext;
        struct sadb_comb *comb;
@@ -502,12 +528,12 @@ print_prop(struct sadb_ext *ext, struct 
            (size_t)((u_int8_t *)comb - (u_int8_t *)ext) <
            ext->sadb_ext_len * PFKEYV2_CHUNK;
            comb++)
-               print_comb(comb, msg);
+               print_comb(comb, msg, opts);
 }
 
 /* ARGSUSED1 */
 static void
-print_sens(struct sadb_ext *ext, struct sadb_msg *msg)
+print_sens(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_sens *sens = (struct sadb_sens *)ext;
 
@@ -519,7 +545,7 @@ print_sens(struct sadb_ext *ext, struct 
 
 /* ARGSUSED1 */
 static void
-print_spir(struct sadb_ext *ext, struct sadb_msg *msg)
+print_spir(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_spirange *spirange = (struct sadb_spirange *)ext;
 
@@ -529,7 +555,7 @@ print_spir(struct sadb_ext *ext, struct 
 
 /* ARGSUSED1 */
 static void
-print_ident(struct sadb_ext *ext, struct sadb_msg *msg)
+print_ident(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_ident *ident = (struct sadb_ident *)ext;
 
@@ -540,7 +566,7 @@ print_ident(struct sadb_ext *ext, struct
 
 /* ARGSUSED1 */
 static void
-print_policy(struct sadb_ext *ext, struct sadb_msg *msg)
+print_policy(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_x_policy *x_policy = (struct sadb_x_policy *)ext;
 
@@ -549,7 +575,7 @@ print_policy(struct sadb_ext *ext, struc
 
 /* ARGSUSED1 */
 static void
-print_udpenc(struct sadb_ext *ext, struct sadb_msg *msg)
+print_udpenc(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
 {
        struct sadb_x_udpencap *x_udpencap = (struct sadb_x_udpencap *)ext;
 
@@ -831,7 +857,7 @@ pfkey_print_sa(struct sadb_msg *msg, int
        if (opts & IPSECCTL_OPT_VERBOSE) {
                for (i = 0; i <= SADB_EXT_MAX; i++)
                        if (extensions[i])
-                               print_ext(extensions[i], msg);
+                               print_ext(extensions[i], msg, opts);
        }
        fflush(stdout);
 }
@@ -855,7 +881,7 @@ pfkey_monitor_sa(struct sadb_msg *msg, i
                    strerror(msg->sadb_msg_errno));
        for (i = 0; i <= SADB_EXT_MAX; i++)
                if (extensions[i])
-                       print_ext(extensions[i], msg);
+                       print_ext(extensions[i], msg, opts);
        fflush(stdout);
 }
 
Index: sys/net/pfkeyv2.c
===================================================================
RCS file: /cvs/src/sys/net/pfkeyv2.c,v
retrieving revision 1.186
diff -u -p -r1.186 pfkeyv2.c
--- sys/net/pfkeyv2.c   25 Jun 2018 09:48:17 -0000      1.186
+++ sys/net/pfkeyv2.c   3 Jul 2018 06:29:27 -0000
@@ -727,7 +727,8 @@ pfkeyv2_get(struct tdb *sa, void **heade
        void *p;
 
        /* Find how much space we need */
-       i = sizeof(struct sadb_sa) + sizeof(struct sadb_lifetime);
+       i = sizeof(struct sadb_sa) + sizeof(struct sadb_lifetime) +
+           sizeof(struct sadb_x_counter);
 
        if (sa->tdb_soft_allocations || sa->tdb_soft_bytes ||
            sa->tdb_soft_timeout || sa->tdb_soft_first_use)
@@ -888,6 +889,9 @@ pfkeyv2_get(struct tdb *sa, void **heade
                export_tap(&p, sa);
        }
 #endif
+
+       headers[SADB_X_EXT_COUNTER] = p;
+       export_counter(&p, sa);
 
        rval = 0;
 
Index: sys/net/pfkeyv2.h
===================================================================
RCS file: /cvs/src/sys/net/pfkeyv2.h,v
retrieving revision 1.79
diff -u -p -r1.79 pfkeyv2.h
--- sys/net/pfkeyv2.h   20 Nov 2017 10:56:51 -0000      1.79
+++ sys/net/pfkeyv2.h   3 Jul 2018 06:29:27 -0000
@@ -218,6 +218,19 @@ struct sadb_x_tap {
        u_int32_t sadb_x_tap_unit;
 };
 
+struct sadb_x_counter {
+       uint16_t  sadb_x_cnt_len;
+       uint16_t  sadb_x_cnt_exttype;
+       uint64_t  sadb_x_cnt_ipackets;          /* Input IPsec packets */
+       uint64_t  sadb_x_cnt_opackets;          /* Output IPsec packets */
+       uint64_t  sadb_x_cnt_ibytes;            /* Input bytes */
+       uint64_t  sadb_x_cnt_obytes;            /* Output bytes */
+       uint64_t  sadb_x_cnt_idrops;            /* Dropped on input */
+       uint64_t  sadb_x_cnt_odrops;            /* Dropped on output */
+       uint64_t  sadb_x_cnt_idecompbytes;      /* Input bytes, decompressed */
+       uint64_t  sadb_x_cnt_ouncompbytes;      /* Output bytes, uncompressed */
+};
+
 #ifdef _KERNEL
 #define SADB_X_GETSPROTO(x) \
        ( (x) == SADB_SATYPE_AH ? IPPROTO_AH :\
@@ -262,7 +275,8 @@ struct sadb_x_tap {
 #define SADB_X_EXT_TAG                33
 #define SADB_X_EXT_TAP                34
 #define SADB_X_EXT_SATYPE2            35
-#define SADB_EXT_MAX                  35
+#define SADB_X_EXT_COUNTER            36
+#define SADB_EXT_MAX                  36
 
 /* Fix pfkeyv2.c struct pfkeyv2_socket if SATYPE_MAX > 31 */
 #define SADB_SATYPE_UNSPEC              0
@@ -396,6 +410,7 @@ void export_udpencap(void **, struct tdb
 void export_tag(void **, struct tdb *);
 void export_tap(void **, struct tdb *);
 void export_satype(void **, struct tdb *);
+void export_counter(void **, struct tdb *);
 
 void import_address(struct sockaddr *, struct sadb_address *);
 void import_identities(struct ipsec_ids **, int, struct sadb_ident *,
Index: sys/net/pfkeyv2_convert.c
===================================================================
RCS file: /cvs/src/sys/net/pfkeyv2_convert.c,v
retrieving revision 1.63
diff -u -p -r1.63 pfkeyv2_convert.c
--- sys/net/pfkeyv2_convert.c   11 Jan 2018 16:02:31 -0000      1.63
+++ sys/net/pfkeyv2_convert.c   3 Jul 2018 06:29:27 -0000
@@ -898,3 +898,21 @@ export_satype(void **p, struct tdb *tdb)
        sab->sadb_protocol_proto = tdb->tdb_satype;
        *p += sizeof(struct sadb_protocol);
 }
+
+void
+export_counter(void **p, struct tdb *tdb)
+{
+       struct sadb_x_counter *scnt = (struct sadb_x_counter *)*p;
+
+       scnt->sadb_x_cnt_len = sizeof(struct sadb_x_counter) / sizeof(uint64_t);
+       scnt->sadb_x_cnt_exttype = SADB_X_EXT_COUNTER;
+       scnt->sadb_x_cnt_ipackets = tdb->tdb_ipackets;
+       scnt->sadb_x_cnt_opackets = tdb->tdb_opackets;
+       scnt->sadb_x_cnt_ibytes = tdb->tdb_ibytes;
+       scnt->sadb_x_cnt_obytes = tdb->tdb_obytes;
+       scnt->sadb_x_cnt_idrops = tdb->tdb_idrops;
+       scnt->sadb_x_cnt_odrops = tdb->tdb_odrops;
+       scnt->sadb_x_cnt_idecompbytes = tdb->tdb_idecompbytes;
+       scnt->sadb_x_cnt_ouncompbytes = tdb->tdb_ouncompbytes;
+       *p += sizeof(struct sadb_x_counter);
+}
Index: sys/netinet/in.h
===================================================================
RCS file: /cvs/src/sys/netinet/in.h,v
retrieving revision 1.130
diff -u -p -r1.130 in.h
--- sys/netinet/in.h    7 Jun 2018 08:46:24 -0000       1.130
+++ sys/netinet/in.h    3 Jul 2018 06:29:27 -0000
@@ -660,6 +660,7 @@ struct ip_mreq {
 #define IPCTL_IPPORT_HILASTAUTO        10
 #define        IPCTL_IPPORT_MAXQUEUE   11
 #define        IPCTL_ENCDEBUG          12
+#define IPCTL_IPSEC_STATS      13
 #define IPCTL_IPSEC_EXPIRE_ACQUIRE 14   /* How long to wait for key mgmt. */
 #define IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT       15 /* new SA lifetime */
 #define IPCTL_IPSEC_REQUIRE_PFS 16
@@ -703,7 +704,7 @@ struct ip_mreq {
        { "porthilast", CTLTYPE_INT }, \
        { "maxqueue", CTLTYPE_INT }, \
        { "encdebug", CTLTYPE_INT }, \
-       { 0, 0 }, \
+       { 0, 0 /* ipsecstat */ }, \
        { "ipsec-expire-acquire", CTLTYPE_INT }, \
        { "ipsec-invalid-life", CTLTYPE_INT }, \
        { "ipsec-pfs", CTLTYPE_INT }, \
@@ -746,7 +747,7 @@ struct ip_mreq {
        &ipport_hilastauto, \
        &ip_maxqueue, \
        NULL /* encdebug */, \
-       NULL, \
+       NULL /* ipsecstat */, \
        NULL /* ipsec_expire_acquire */, \
        NULL /* ipsec_keep_invalid */, \
        NULL /* ipsec_require_pfs */, \
Index: sys/netinet/ip_ah.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ah.c,v
retrieving revision 1.140
diff -u -p -r1.140 ip_ah.c
--- sys/netinet/ip_ah.c 9 May 2018 12:48:59 -0000       1.140
+++ sys/netinet/ip_ah.c 3 Jul 2018 06:29:27 -0000
@@ -76,8 +76,6 @@
 #define DPRINTF(x)
 #endif
 
-void   ah_output_cb(struct cryptop *);
-void   ah_input_cb(struct cryptop *);
 int    ah_massage_headers(struct mbuf **, int, int, int, int);
 
 const unsigned char ipseczeroes[IPSEC_ZEROES_SIZE]; /* zeroes! */
@@ -531,6 +529,7 @@ ah_input(struct mbuf *m, struct tdb *tdb
        u_int32_t btsx, esn;
        u_int8_t hl;
        int error, rplen;
+       u_int64_t ibytes;
 #ifdef ENCDEBUG
        char buf[INET6_ADDRSTRLEN];
 #endif
@@ -607,9 +606,10 @@ ah_input(struct mbuf *m, struct tdb *tdb
        }
 
        /* Update the counters. */
-       tdb->tdb_cur_bytes +=
-           (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t));
-       ahstat_add(ahs_ibytes, m->m_pkthdr.len - skip - hl * sizeof(u_int32_t));
+       ibytes = (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t));
+       tdb->tdb_cur_bytes += ibytes;
+       tdb->tdb_ibytes += ibytes;
+       ahstat_add(ahs_ibytes, ibytes);
 
        /* Hard expiration. */
        if (tdb->tdb_flags & TDBF_BYTES &&
@@ -686,7 +686,7 @@ ah_input(struct mbuf *m, struct tdb *tdb
        crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = ah_input_cb;
+       crp->crp_callback = ipsec_input_cb;
        crp->crp_sid = tdb->tdb_cryptoid;
        crp->crp_opaque = (caddr_t)tc;
 
@@ -707,62 +707,24 @@ ah_input(struct mbuf *m, struct tdb *tdb
        return error;
 }
 
-/*
- * AH input callback, called directly by the crypto driver.
- */
-void
-ah_input_cb(struct cryptop *crp)
+int
+ah_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen)
 {
+       struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
        int roff, rplen, skip, protoff;
-       unsigned char calc[AH_ALEN_MAX];
-       struct mbuf *m1, *m0, *m;
-       struct auth_hash *ahx;
-       struct tdb_crypto *tc = NULL;
-       struct tdb *tdb;
        u_int32_t btsx, esn;
        caddr_t ptr;
+       unsigned char calc[AH_ALEN_MAX];
+       struct mbuf *m1, *m0;
 #ifdef ENCDEBUG
        char buf[INET6_ADDRSTRLEN];
 #endif
 
-       tc = (struct tdb_crypto *) crp->crp_opaque;
+       NET_ASSERT_LOCKED();
+
        skip = tc->tc_skip;
        protoff = tc->tc_protoff;
 
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               ahstat_inc(ahs_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB is expired while in crypto", __func__));
-               ahstat_inc(ahs_notdb);
-               goto baddone;
-       }
-
-       ahx = (struct auth_hash *) tdb->tdb_authalgxform;
-
-       /* Check for crypto errors. */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
-               ahstat_inc(ahs_noxform);
-               goto baddone;
-       }
-
        rplen = AH_FLENGTH + sizeof(u_int32_t);
 
        /* Copy authenticator off the packet. */
@@ -906,19 +868,14 @@ ah_input_cb(struct cryptop *crp)
                        m->m_pkthdr.len -= rplen + ahx->authsize;
                }
 
-       crypto_freereq(crp); /* No longer needed. */
        free(tc, M_XDATA, 0);
 
-       ipsec_common_input_cb(m, tdb, skip, protoff);
-       NET_UNLOCK();
-       return;
+       return ipsec_common_input_cb(m, tdb, skip, protoff);
 
  baddone:
-       NET_UNLOCK();
- droponly:
        m_freem(m);
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
+       return -1;
 }
 
 /*
@@ -1176,7 +1133,7 @@ ah_output(struct mbuf *m, struct tdb *td
        crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = ah_output_cb;
+       crp->crp_callback = ipsec_output_cb;
        crp->crp_sid = tdb->tdb_cryptoid;
        crp->crp_opaque = (caddr_t)tc;
 
@@ -1198,52 +1155,14 @@ ah_output(struct mbuf *m, struct tdb *td
 }
 
 /*
- * AH output callback, called directly from the crypto handler.
+ * AH output callback.
  */
-void
-ah_output_cb(struct cryptop *crp)
+int
+ah_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int ilen,
+    int olen)
 {
-       int skip;
-       struct tdb_crypto *tc = NULL;
-       struct tdb *tdb = NULL;
-       struct mbuf *m;
-       caddr_t ptr;
-
-       tc = (struct tdb_crypto *) crp->crp_opaque;
-       skip = tc->tc_skip;
-       ptr = (caddr_t) (tc + 1);
-
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               ahstat_inc(ahs_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB is expired while in crypto\n", __func__));
-               ahstat_inc(ahs_notdb);
-               goto baddone;
-       }
-
-       /* Check for crypto errors. */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
-               ahstat_inc(ahs_noxform);
-               goto baddone;
-       }
+       int skip = tc->tc_skip;
+       caddr_t ptr = (caddr_t) (tc + 1);
 
        /*
         * Copy original headers (with the new protocol number) back
@@ -1252,18 +1171,13 @@ ah_output_cb(struct cryptop *crp)
        m_copyback(m, 0, skip, ptr, M_NOWAIT);
 
        /* No longer needed. */
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
 
-       if (ipsp_process_done(m, tdb))
+       /* Call the IPsec input callback. */
+       if (ipsp_process_done(m, tdb)) {
                ahstat_inc(ahs_outfail);
-       NET_UNLOCK();
-       return;
+               return -1;
+       }
 
- baddone:
-       NET_UNLOCK();
- droponly:
-       m_freem(m);
-       crypto_freereq(crp);
-       free(tc, M_XDATA, 0);
+       return 0;
 }
Index: sys/netinet/ip_esp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_esp.c,v
retrieving revision 1.154
diff -u -p -r1.154 ip_esp.c
--- sys/netinet/ip_esp.c        9 May 2018 16:00:28 -0000       1.154
+++ sys/netinet/ip_esp.c        3 Jul 2018 06:29:27 -0000
@@ -69,9 +69,6 @@
 
 #include "bpfilter.h"
 
-void esp_output_cb(struct cryptop *);
-void esp_input_cb(struct cryptop *);
-
 #ifdef ENCDEBUG
 #define DPRINTF(x)     if (encdebug) printf x
 #else
@@ -341,6 +338,7 @@ esp_input(struct mbuf *m, struct tdb *td
        struct tdb_crypto *tc = NULL;
        int plen, alen, hlen, error;
        u_int32_t btsx, esn;
+       u_int64_t ibytes;
 #ifdef ENCDEBUG
        char buf[INET6_ADDRSTRLEN];
 #endif
@@ -418,8 +416,10 @@ esp_input(struct mbuf *m, struct tdb *td
        }
 
        /* Update the counters */
-       tdb->tdb_cur_bytes += m->m_pkthdr.len - skip - hlen - alen;
-       espstat_add(esps_ibytes, m->m_pkthdr.len - skip - hlen - alen);
+       ibytes = m->m_pkthdr.len - skip - hlen - alen;
+       tdb->tdb_cur_bytes += ibytes;
+       tdb->tdb_ibytes += ibytes;
+       espstat_add(esps_ibytes, ibytes);
 
        /* Hard expiration */
        if ((tdb->tdb_flags & TDBF_BYTES) &&
@@ -492,7 +492,7 @@ esp_input(struct mbuf *m, struct tdb *td
        crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = esp_input_cb;
+       crp->crp_callback = ipsec_input_cb;
        crp->crp_sid = tdb->tdb_cryptoid;
        crp->crp_opaque = (caddr_t)tc;
 
@@ -531,59 +531,26 @@ esp_input(struct mbuf *m, struct tdb *td
 /*
  * ESP input callback, called directly by the crypto driver.
  */
-void
-esp_input_cb(struct cryptop *crp)
+int
+esp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen)
 {
        u_int8_t lastthree[3], aalg[AH_HMAC_MAX_HASHLEN];
        int hlen, roff, skip, protoff;
-       struct mbuf *m1, *mo, *m;
+       struct mbuf *m1, *mo;
        struct auth_hash *esph;
-       struct tdb_crypto *tc;
-       struct tdb *tdb;
        u_int32_t btsx, esn;
        caddr_t ptr;
 #ifdef ENCDEBUG
        char buf[INET6_ADDRSTRLEN];
 #endif
 
-       tc = (struct tdb_crypto *) crp->crp_opaque;
        skip = tc->tc_skip;
        protoff = tc->tc_protoff;
 
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               espstat_inc(esps_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB is expired while in crypto", __func__));
-               espstat_inc(esps_notdb);
-               goto baddone;
-       }
+       NET_ASSERT_LOCKED();
 
        esph = (struct auth_hash *) tdb->tdb_authalgxform;
 
-       /* Check for crypto errors */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
-               espstat_inc(esps_noxform);
-               goto baddone;
-       }
-
        /* If authentication was performed, check now. */
        if (esph != NULL) {
                /* Copy the authenticator from the packet */
@@ -749,20 +716,15 @@ esp_input_cb(struct cryptop *crp)
        m_copyback(m, protoff, sizeof(u_int8_t), lastthree + 2, M_NOWAIT);
 
        /* Release the crypto descriptors */
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
 
        /* Back to generic IPsec input processing */
-       ipsec_common_input_cb(m, tdb, skip, protoff);
-       NET_UNLOCK();
-       return;
+       return ipsec_common_input_cb(m, tdb, skip, protoff);
 
  baddone:
-       NET_UNLOCK();
- droponly:
        m_freem(m);
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
+       return -1;
 }
 
 /*
@@ -1005,7 +967,7 @@ esp_output(struct mbuf *m, struct tdb *t
        crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = esp_output_cb;
+       crp->crp_callback = ipsec_output_cb;
        crp->crp_opaque = (caddr_t)tc;
        crp->crp_sid = tdb->tdb_cryptoid;
 
@@ -1044,66 +1006,20 @@ esp_output(struct mbuf *m, struct tdb *t
        return error;
 }
 
-/*
- * ESP output callback, called directly by the crypto driver.
- */
-void
-esp_output_cb(struct cryptop *crp)
+int
+esp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int ilen,
+    int olen)
 {
-       struct tdb_crypto *tc;
-       struct tdb *tdb;
-       struct mbuf *m;
-
-       tc = (struct tdb_crypto *) crp->crp_opaque;
-
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               espstat_inc(esps_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB is expired while in crypto\n", __func__));
-               espstat_inc(esps_notdb);
-               goto baddone;
-       }
-
-       /* Check for crypto errors. */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
-               espstat_inc(esps_noxform);
-               goto baddone;
-       }
-
        /* Release crypto descriptors. */
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
 
        /* Call the IPsec input callback. */
-       if (ipsp_process_done(m, tdb))
+       if (ipsp_process_done(m, tdb)) {
                espstat_inc(esps_outfail);
-       NET_UNLOCK();
-       return;
+               return -1;
+       }
 
- baddone:
-       NET_UNLOCK();
- droponly:
-       m_freem(m);
-       crypto_freereq(crp);
-       free(tc, M_XDATA, 0);
+       return 0;
 }
 
 #define SEEN_SIZE      howmany(TDB_REPLAYMAX, 32)
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.337
diff -u -p -r1.337 ip_input.c
--- sys/netinet/ip_input.c      21 May 2018 15:52:22 -0000      1.337
+++ sys/netinet/ip_input.c      3 Jul 2018 06:29:27 -0000
@@ -1623,6 +1623,7 @@ ip_sysctl(int *name, u_int namelen, void
                return (error);
 #ifdef IPSEC
        case IPCTL_ENCDEBUG:
+       case IPCTL_IPSEC_STATS:
        case IPCTL_IPSEC_EXPIRE_ACQUIRE:
        case IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT:
        case IPCTL_IPSEC_REQUIRE_PFS:
Index: sys/netinet/ip_ipcomp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ipcomp.c,v
retrieving revision 1.61
diff -u -p -r1.61 ip_ipcomp.c
--- sys/netinet/ip_ipcomp.c     12 May 2018 09:38:33 -0000      1.61
+++ sys/netinet/ip_ipcomp.c     3 Jul 2018 06:29:27 -0000
@@ -56,9 +56,6 @@
 
 #include "bpfilter.h"
 
-void ipcomp_output_cb(struct cryptop *);
-void ipcomp_input_cb(struct cryptop *);
-
 #ifdef ENCDEBUG
 #define DPRINTF(x)      if (encdebug) printf x
 #else
@@ -169,7 +166,7 @@ ipcomp_input(struct mbuf *m, struct tdb 
        crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = ipcomp_input_cb;
+       crp->crp_callback = ipsec_input_cb;
        crp->crp_sid = tdb->tdb_cryptoid;
        crp->crp_opaque = (caddr_t)tc;
 
@@ -184,46 +181,25 @@ ipcomp_input(struct mbuf *m, struct tdb 
        return crypto_dispatch(crp);
 }
 
-/*
- * IPComp input callback, called directly by the crypto driver
- */
-void
-ipcomp_input_cb(struct cryptop *crp)
+int
+ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int 
clen)
 {
-       int skip, protoff, roff, hlen = IPCOMP_HLENGTH, clen;
+       int skip, protoff, roff, hlen = IPCOMP_HLENGTH;
        u_int8_t nproto;
-       struct mbuf *m, *m1, *mo;
-       struct tdb_crypto *tc;
-       struct tdb *tdb;
+       u_int64_t ibytes;
+       struct mbuf *m1, *mo;
        struct ipcomp  *ipcomp;
        caddr_t addr;
 #ifdef ENCDEBUG
        char buf[INET6_ADDRSTRLEN];
 #endif
 
-       tc = (struct tdb_crypto *) crp->crp_opaque;
-       skip = tc->tc_skip;
-       protoff = tc->tc_protoff;
-
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               ipcompstat_inc(ipcomps_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB expired while in crypto", __func__));
-               ipcompstat_inc(ipcomps_notdb);
-               goto baddone;
-       }
+       NET_ASSERT_LOCKED();
 
        /* update the counters */
-       tdb->tdb_cur_bytes += m->m_pkthdr.len - (skip + hlen);
+       ibytes = m->m_pkthdr.len - (skip + hlen);
+       tdb->tdb_cur_bytes += ibytes;
+       tdb->tdb_ibytes += ibytes;
        ipcompstat_add(ipcomps_ibytes, m->m_pkthdr.len - (skip + hlen));
 
        /* Hard expiration */
@@ -240,24 +216,8 @@ ipcomp_input_cb(struct cryptop *crp)
                tdb->tdb_flags &= ~TDBF_SOFT_BYTES;     /* Turn off checking */
        }
 
-       /* Check for crypto errors */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__,
-                   crp->crp_etype));
-               ipcompstat_inc(ipcomps_noxform);
-               goto baddone;
-       }
-
-       /* Length of data after processing */
-       clen = crp->crp_olen;
+       skip = tc->tc_skip;
+       protoff = tc->tc_protoff;
 
        /* In case it's not done already, adjust the size of the mbuf chain */
        m->m_pkthdr.len = clen + hlen + skip;
@@ -331,23 +291,18 @@ ipcomp_input_cb(struct cryptop *crp)
        }
 
        /* Release the crypto descriptors */
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
 
        /* Restore the Next Protocol field */
        m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
 
        /* Back to generic IPsec input processing */
-       ipsec_common_input_cb(m, tdb, skip, protoff);
-       NET_UNLOCK();
-       return;
+       return ipsec_common_input_cb(m, tdb, skip, protoff);
 
  baddone:
-       NET_UNLOCK();
- droponly:
        m_freem(m);
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
+       return -1;
 }
 
 /*
@@ -513,7 +468,7 @@ ipcomp_output(struct mbuf *m, struct tdb
        crp->crp_ilen = m->m_pkthdr.len;        /* Total input length */
        crp->crp_flags = CRYPTO_F_IMBUF;
        crp->crp_buf = (caddr_t)m;
-       crp->crp_callback = ipcomp_output_cb;
+       crp->crp_callback = ipsec_output_cb;
        crp->crp_opaque = (caddr_t)tc;
        crp->crp_sid = tdb->tdb_cryptoid;
 
@@ -526,14 +481,13 @@ ipcomp_output(struct mbuf *m, struct tdb
 }
 
 /*
- * IPComp output callback, called directly from the crypto driver
+ * IPComp output callback.
  */
-void
-ipcomp_output_cb(struct cryptop *crp)
+int
+ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m,
+    int ilen, int olen)
 {
-       struct tdb_crypto *tc;
-       struct tdb *tdb;
-       struct mbuf *m, *mo;
+       struct mbuf *mo;
        int skip, rlen, roff;
        u_int16_t cpi;
        struct ip *ip;
@@ -545,51 +499,13 @@ ipcomp_output_cb(struct cryptop *crp)
        char buf[INET6_ADDRSTRLEN];
 #endif
 
-       tc = (struct tdb_crypto *) crp->crp_opaque;
        skip = tc->tc_skip;
-       rlen = crp->crp_ilen - skip;
-
-       m = (struct mbuf *) crp->crp_buf;
-       if (m == NULL) {
-               /* Shouldn't happen... */
-               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
-               ipcompstat_inc(ipcomps_crypto);
-               goto droponly;
-       }
-
-       NET_LOCK();
-
-       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
-       if (tdb == NULL) {
-               DPRINTF(("%s: TDB expired while in crypto\n", __func__));
-               ipcompstat_inc(ipcomps_notdb);
-               goto baddone;
-       }
-
-       /* Check for crypto errors. */
-       if (crp->crp_etype) {
-               if (crp->crp_etype == EAGAIN) {
-                       /* Reset the session ID */
-                       if (tdb->tdb_cryptoid != 0)
-                               tdb->tdb_cryptoid = crp->crp_sid;
-                       NET_UNLOCK();
-                       crypto_dispatch(crp);
-                       return;
-               }
-               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
-               ipcompstat_inc(ipcomps_noxform);
-               goto baddone;
-       }
+       rlen = ilen - skip;
 
        /* Check sizes. */
-       if (rlen < crp->crp_olen) {
+       if (rlen < olen) {
                /* Compression was useless, we have lost time. */
-               ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count 
*/
-               crypto_freereq(crp);
-               if (ipsp_process_done(m, tdb))
-                       ipcompstat_inc(ipcomps_outfail);
-               NET_UNLOCK();
-               return;
+               goto baddone;
        }
 
        /* Inject IPCOMP header */
@@ -599,7 +515,7 @@ ipcomp_output_cb(struct cryptop *crp)
                    "IPCA %s/%08x\n", __func__, ipsp_address(&tdb->tdb_dst, buf,
                     sizeof(buf)), ntohl(tdb->tdb_spi)));
                ipcompstat_inc(ipcomps_wrap);
-               goto baddone;
+               return -1;
        }
 
        /* Initialize the IPCOMP header */
@@ -629,22 +545,19 @@ ipcomp_output_cb(struct cryptop *crp)
                    ntohl(tdb->tdb_spi)));
                ipcompstat_inc(ipcomps_nopf);
                goto baddone;
-               break;
        }
 
        /* Release the crypto descriptor. */
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
 
-       if (ipsp_process_done(m, tdb))
+       if (ipsp_process_done(m, tdb)) {
                ipcompstat_inc(ipcomps_outfail);
-       NET_UNLOCK();
-       return;
+               return -1;
+       }
+       return 0;
 
  baddone:
-       NET_UNLOCK();
- droponly:
        m_freem(m);
-       crypto_freereq(crp);
        free(tc, M_XDATA, 0);
+       return -1;
 }
Index: sys/netinet/ip_ipip.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ipip.c,v
retrieving revision 1.87
diff -u -p -r1.87 ip_ipip.c
--- sys/netinet/ip_ipip.c       9 Oct 2017 08:35:38 -0000       1.87
+++ sys/netinet/ip_ipip.c       3 Jul 2018 06:29:27 -0000
@@ -331,11 +331,9 @@ int
 ipip_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int dummy,
     int dummy2)
 {
-       u_int8_t tp, otos;
-
-       u_int8_t itos;
+       u_int8_t tp, otos, itos;
+       u_int64_t ibytes;
        struct ip *ipo;
-
 #ifdef INET6
        struct ip6_hdr *ip6, *ip6o;
 #endif /* INET6 */
@@ -525,21 +523,20 @@ ipip_output(struct mbuf *m, struct tdb *
        *mp = m;
 
        if (tdb->tdb_dst.sa.sa_family == AF_INET) {
+               ibytes = m->m_pkthdr.len - sizeof(struct ip);
                if (tdb->tdb_xform->xf_type == XF_IP4)
-                       tdb->tdb_cur_bytes +=
-                           m->m_pkthdr.len - sizeof(struct ip);
+                       tdb->tdb_cur_bytes += ibytes;
 
-               ipipstat_add(ipips_obytes, m->m_pkthdr.len - sizeof(struct ip));
+               ipipstat_add(ipips_obytes, ibytes);
        }
 
 #ifdef INET6
        if (tdb->tdb_dst.sa.sa_family == AF_INET6) {
+               ibytes = m->m_pkthdr.len - sizeof(struct ip6_hdr);
                if (tdb->tdb_xform->xf_type == XF_IP4)
-                       tdb->tdb_cur_bytes +=
-                           m->m_pkthdr.len - sizeof(struct ip6_hdr);
+                       tdb->tdb_cur_bytes += ibytes;
 
-               ipipstat_add(ipips_obytes,
-                   m->m_pkthdr.len - sizeof(struct ip6_hdr));
+               ipipstat_add(ipips_obytes, ibytes);
        }
 #endif /* INET6 */
 
Index: sys/netinet/ip_ipsp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ipsp.c,v
retrieving revision 1.231
diff -u -p -r1.231 ip_ipsp.c
--- sys/netinet/ip_ipsp.c       19 May 2018 12:34:35 -0000      1.231
+++ sys/netinet/ip_ipsp.c       3 Jul 2018 06:29:27 -0000
@@ -708,6 +708,8 @@ puttdb(struct tdb *tdbp)
        tdbsrc[hashval] = tdbp;
 
        tdb_count++;
+       if ((tdbp->tdb_flags & (TDBF_INVALID|TDBF_TUNNELING)) == TDBF_TUNNELING)
+               ipsecstat_inc(ipsec_tunnels);
 
        ipsec_last_added = time_second;
 }
@@ -775,6 +777,11 @@ tdb_unlink(struct tdb *tdbp)
 
        tdbp->tdb_snext = NULL;
        tdb_count--;
+       if ((tdbp->tdb_flags & (TDBF_INVALID|TDBF_TUNNELING)) ==
+           TDBF_TUNNELING) {
+               ipsecstat_dec(ipsec_tunnels);
+               ipsecstat_inc(ipsec_prevtunnels);
+       }
 }
 
 void
Index: sys/netinet/ip_ipsp.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_ipsp.h,v
retrieving revision 1.189
diff -u -p -r1.189 ip_ipsp.h
--- sys/netinet/ip_ipsp.h       20 Nov 2017 14:14:26 -0000      1.189
+++ sys/netinet/ip_ipsp.h       3 Jul 2018 06:29:27 -0000
@@ -40,14 +40,10 @@
 #ifndef _NETINET_IPSP_H_
 #define _NETINET_IPSP_H_
 
-struct m_tag;
-
 /* IPSP global definitions. */
 
 #include <sys/types.h>
-#include <sys/queue.h>
 #include <netinet/in.h>
-#include <net/radix.h>
 
 union sockaddr_union {
        struct sockaddr         sa;
@@ -125,9 +121,79 @@ struct sockaddr_encap {
 #define        IPSP_DIRECTION_IN       0x1
 #define        IPSP_DIRECTION_OUT      0x2
 
+struct ipsecstat {
+       uint64_t        ipsec_tunnels;          /* Number of active tunnels */
+       uint64_t        ipsec_prevtunnels;      /* Past number of tunnels */
+       uint64_t        ipsec_ipackets;         /* Input IPsec packets */
+       uint64_t        ipsec_opackets;         /* Output IPsec packets */
+       uint64_t        ipsec_ibytes;           /* Input bytes */
+       uint64_t        ipsec_obytes;           /* Output bytes */
+       uint64_t        ipsec_idecompbytes;     /* Input bytes, decompressed */
+       uint64_t        ipsec_ouncompbytes;     /* Output bytes, uncompressed */
+       uint64_t        ipsec_idrops;           /* Dropped on input */
+       uint64_t        ipsec_odrops;           /* Dropped on output */
+       uint64_t        ipsec_crypto;           /* Crypto processing failure */
+       uint64_t        ipsec_notdb;            /* Expired while in crypto */
+       uint64_t        ipsec_noxform;          /* Crypto error */
+};
+
+struct tdb_data {
+       uint64_t        tdd_ipackets;           /* Input IPsec packets */
+       uint64_t        tdd_opackets;           /* Output IPsec packets */
+       uint64_t        tdd_ibytes;             /* Input bytes */
+       uint64_t        tdd_obytes;             /* Output bytes */
+       uint64_t        tdd_idrops;             /* Dropped on input */
+       uint64_t        tdd_odrops;             /* Dropped on output */
+       uint64_t        tdd_idecompbytes;       /* Input bytes, decompressed */
+       uint64_t        tdd_ouncompbytes;       /* Output bytes, uncompressed */
+};
+
 #ifdef _KERNEL
+
 #include <sys/timeout.h>
 #include <sys/tree.h>
+#include <sys/queue.h>
+#include <net/radix.h>
+#include <sys/percpu.h>
+
+enum ipsec_counters {
+       ipsec_tunnels,
+       ipsec_prevtunnels,
+       ipsec_ipackets,
+       ipsec_opackets,
+       ipsec_ibytes,
+       ipsec_obytes,
+       ipsec_idecompbytes,
+       ipsec_ouncompbytes,
+       ipsec_idrops,
+       ipsec_odrops,
+       ipsec_crypto,
+       ipsec_notdb,
+       ipsec_noxform,
+       ipsec_ncounters
+};
+
+extern struct cpumem *ipseccounters;
+
+static inline void
+ipsecstat_inc(enum ipsec_counters c)
+{
+       counters_inc(ipseccounters, c);
+}
+
+static inline void
+ipsecstat_dec(enum ipsec_counters c)
+{
+       counters_dec(ipseccounters, c);
+}
+
+static inline void
+ipsecstat_add(enum ipsec_counters c, uint64_t v)
+{
+       counters_add(ipseccounters, c, v);
+}
+
+struct m_tag;
 
 #define        sen_data                Sen.Data
 #define        sen_ip_src              Sen.Sip4.Src
@@ -305,6 +371,7 @@ struct tdb {                                /* tunnel 
descriptor blo
        u_int64_t       tdb_last_used;  /* When was this SA last used */
        u_int64_t       tdb_last_marked;/* Last SKIPCRYPTO status change */
 
+       struct tdb_data tdb_data;       /* stats about this TDB */
        u_int64_t       tdb_cryptoid;   /* Crypto session ID */
 
        u_int32_t       tdb_spi;        /* SPI */
@@ -349,6 +416,14 @@ struct tdb {                               /* tunnel 
descriptor blo
        TAILQ_HEAD(tdb_policy_head, ipsec_policy)       tdb_policy_head;
        TAILQ_ENTRY(tdb)        tdb_sync_entry;
 };
+#define tdb_ipackets           tdb_data.tdd_ipackets
+#define tdb_opackets           tdb_data.tdd_opackets
+#define tdb_ibytes             tdb_data.tdd_ibytes
+#define tdb_obytes             tdb_data.tdd_obytes
+#define tdb_idrops             tdb_data.tdd_idrops
+#define tdb_odrops             tdb_data.tdd_odrops
+#define tdb_idecompbytes       tdb_data.tdd_idecompbytes
+#define tdb_ouncompbytes       tdb_data.tdd_ouncompbytes
 
 
 struct tdb_ident {
@@ -424,6 +499,7 @@ extern int ipsec_exp_first_use;             /* seco
  * Names for IPsec sysctl objects
  */
 #define        IPSEC_ENCDEBUG                  IPCTL_ENCDEBUG                  
/* 12 */
+#define        IPSEC_STATS                     IPCTL_IPSEC_STATS               
/* 13 */
 #define IPSEC_EXPIRE_ACQUIRE           IPCTL_IPSEC_EXPIRE_ACQUIRE      /* 14 */
 #define IPSEC_EMBRYONIC_SA_TIMEOUT     IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT/* 15 */
 #define IPSEC_REQUIRE_PFS              IPCTL_IPSEC_REQUIRE_PFS         /* 16 */
@@ -451,7 +527,7 @@ extern int ipsec_exp_first_use;             /* seco
        NULL, \
        NULL, \
        &encdebug, \
-       NULL, \
+       NULL, /* ipsecstat */ \
        &ipsec_expire_acquire, \
        &ipsec_keep_invalid, \
        &ipsec_require_pfs, \
@@ -482,6 +558,8 @@ extern struct comp_algo comp_algo_deflat
 
 extern TAILQ_HEAD(ipsec_policy_head, ipsec_policy) ipsec_policy_head;
 
+struct cryptop;
+
 /* Misc. */
 #ifdef ENCDEBUG
 const char *ipsp_address(union sockaddr_union *, char *, socklen_t);
@@ -524,7 +602,10 @@ int        ah_attach(void);
 int    ah_init(struct tdb *, struct xformsw *, struct ipsecinit *);
 int    ah_zeroize(struct tdb *);
 int    ah_input(struct mbuf *, struct tdb *, int, int);
+int    ah_input_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int);
 int    ah_output(struct mbuf *, struct tdb *, struct mbuf **, int, int);
+int    ah_output_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int,
+           int);
 int    ah_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 
 int    ah4_input(struct mbuf **, int *, int, int);
@@ -540,7 +621,10 @@ int        esp_attach(void);
 int    esp_init(struct tdb *, struct xformsw *, struct ipsecinit *);
 int    esp_zeroize(struct tdb *);
 int    esp_input(struct mbuf *, struct tdb *, int, int);
+int    esp_input_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int);
 int    esp_output(struct mbuf *, struct tdb *, struct mbuf **, int, int);
+int    esp_output_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int,
+           int);
 int    esp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 
 int    esp4_input(struct mbuf **, int *, int, int);
@@ -555,7 +639,10 @@ int        ipcomp_attach(void);
 int    ipcomp_init(struct tdb *, struct xformsw *, struct ipsecinit *);
 int    ipcomp_zeroize(struct tdb *);
 int    ipcomp_input(struct mbuf *, struct tdb *, int, int);
+int    ipcomp_input_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int);
 int    ipcomp_output(struct mbuf *, struct tdb *, struct mbuf **, int, int);
+int    ipcomp_output_cb(struct tdb *, struct tdb_crypto *, struct mbuf *, int,
+           int);
 int    ipcomp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 int    ipcomp4_input(struct mbuf **, int *, int, int);
 #ifdef INET6
@@ -592,7 +679,9 @@ void        ipsp_ids_free(struct ipsec_ids *);
 void   ipsec_init(void);
 int    ipsec_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 int    ipsec_common_input(struct mbuf *, int, int, int, int, int);
-void   ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int);
+void   ipsec_input_cb(struct cryptop *);
+void   ipsec_output_cb(struct cryptop *);
+int    ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int);
 int    ipsec_delete_policy(struct ipsec_policy *);
 ssize_t        ipsec_hdrsz(struct tdb *);
 void   ipsec_adjust_mtu(struct mbuf *, u_int32_t);
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.346
diff -u -p -r1.346 ip_output.c
--- sys/netinet/ip_output.c     21 Mar 2018 14:42:41 -0000      1.346
+++ sys/netinet/ip_output.c     3 Jul 2018 06:29:27 -0000
@@ -564,6 +564,7 @@ ip_output_ipsec_send(struct tdb *tdb, st
        struct ifnet *encif;
 #endif
        struct ip *ip;
+       int error;
 
 #if NPF > 0
        /*
@@ -633,7 +634,12 @@ ip_output_ipsec_send(struct tdb *tdb, st
        m->m_flags &= ~(M_MCAST | M_BCAST);
 
        /* Callee frees mbuf */
-       return ipsp_process_packet(m, tdb, AF_INET, 0);
+       error = ipsp_process_packet(m, tdb, AF_INET, 0);
+       if (error) {
+               ipsecstat_inc(ipsec_odrops);
+               tdb->tdb_odrops++;
+       }
+       return error;
 }
 #endif /* IPSEC */
 
Index: sys/netinet/ipsec_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ipsec_input.c,v
retrieving revision 1.163
diff -u -p -r1.163 ipsec_input.c
--- sys/netinet/ipsec_input.c   14 May 2018 15:24:23 -0000      1.163
+++ sys/netinet/ipsec_input.c   3 Jul 2018 06:29:27 -0000
@@ -77,6 +77,9 @@
 
 #include <net/if_enc.h>
 
+#include <crypto/cryptodev.h>
+#include <crypto/xform.h>
+
 #include "bpfilter.h"
 
 void ipsec_common_ctlinput(u_int, int, struct sockaddr *, void *, int);
@@ -112,6 +115,7 @@ int *ipcompctl_vars[IPCOMPCTL_MAXID] = I
 struct cpumem *espcounters;
 struct cpumem *ahcounters;
 struct cpumem *ipcompcounters;
+struct cpumem *ipseccounters;
 
 char ipsec_def_enc[20];
 char ipsec_def_auth[20];
@@ -122,6 +126,7 @@ int *ipsecctl_vars[IPSEC_MAXID] = IPSECC
 int esp_sysctl_espstat(void *, size_t *, void *);
 int ah_sysctl_ahstat(void *, size_t *, void *);
 int ipcomp_sysctl_ipcompstat(void *, size_t *, void *);
+int ipsec_sysctl_ipsecstat(void *, size_t *, void *);
 
 void
 ipsec_init(void)
@@ -129,6 +134,7 @@ ipsec_init(void)
        espcounters = counters_alloc(esps_ncounters);
        ahcounters = counters_alloc(ahs_ncounters);
        ipcompcounters = counters_alloc(ipcomps_ncounters);
+       ipseccounters = counters_alloc(ipsec_ncounters);
 
        strlcpy(ipsec_def_enc, IPSEC_DEFAULT_DEF_ENC, sizeof(ipsec_def_enc));
        strlcpy(ipsec_def_auth, IPSEC_DEFAULT_DEF_AUTH, sizeof(ipsec_def_auth));
@@ -167,6 +173,8 @@ ipsec_common_input(struct mbuf *m, int s
 
        NET_ASSERT_LOCKED();
 
+       ipsecstat_inc(ipsec_ipackets);
+       ipsecstat_add(ipsec_ibytes, m->m_pkthdr.len);
        IPSEC_ISTAT(esps_input, ahs_input, ipcomps_input);
 
        if (m == NULL) {
@@ -317,23 +325,110 @@ ipsec_common_input(struct mbuf *m, int s
                            tdbp->tdb_soft_first_use);
        }
 
+       tdbp->tdb_ipackets++;
+       tdbp->tdb_ibytes += m->m_pkthdr.len;
+
        /*
         * Call appropriate transform and return -- callback takes care of
         * everything else.
         */
        error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff);
+       if (error) {
+               ipsecstat_inc(ipsec_idrops);
+               tdbp->tdb_idrops++;
+       }
        return error;
 
  drop:
+       ipsecstat_inc(ipsec_idrops);
+       if (tdbp != NULL)
+               tdbp->tdb_idrops++;
        m_freem(m);
        return error;
 }
 
+void
+ipsec_input_cb(struct cryptop *crp)
+{
+       struct tdb_crypto *tc = (struct tdb_crypto *) crp->crp_opaque;
+       struct mbuf *m = (struct mbuf *) crp->crp_buf;
+       struct tdb *tdb;
+       int clen, error;
+
+       if (m == NULL) {
+               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
+               ipsecstat_inc(ipsec_crypto);
+               goto droponly;
+       }
+
+
+       NET_LOCK();
+       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
+       if (tdb == NULL) {
+               DPRINTF(("%s: TDB is expired while in crypto", __func__));
+               ipsecstat_inc(ipsec_notdb);
+               goto baddone;
+       }
+
+       /* Check for crypto errors */
+       if (crp->crp_etype) {
+               if (crp->crp_etype == EAGAIN) {
+                       /* Reset the session ID */
+                       if (tdb->tdb_cryptoid != 0)
+                               tdb->tdb_cryptoid = crp->crp_sid;
+                       NET_UNLOCK();
+                       crypto_dispatch(crp);
+                       return;
+               }
+               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
+               ipsecstat_inc(ipsec_noxform);
+               goto baddone;
+       }
+
+       /* Length of data after processing */
+       clen = crp->crp_olen;
+
+       /* Release the crypto descriptors */
+       crypto_freereq(crp);
+
+       switch (tdb->tdb_sproto) {
+       case IPPROTO_ESP:
+               error = esp_input_cb(tdb, tc, m, clen);
+               break;
+       case IPPROTO_AH:
+               error = ah_input_cb(tdb, tc, m, clen);
+               break;
+       case IPPROTO_IPCOMP:
+               error = ipcomp_input_cb(tdb, tc, m, clen);
+               break;
+       default:
+               panic("%s: unknown/unsupported security protocol %d",
+                   __func__, tdb->tdb_sproto);
+       }
+
+       NET_UNLOCK();
+       if (error) {
+               ipsecstat_inc(ipsec_idrops);
+               tdb->tdb_idrops++;
+       }
+       return;
+
+ baddone:
+       NET_UNLOCK();
+ droponly:
+       ipsecstat_inc(ipsec_idrops);
+       if (tdb != NULL)
+               tdb->tdb_idrops++;
+       free(tc, M_XDATA, 0);
+       m_freem(m);
+       crypto_freereq(crp);
+}
+
 /*
  * IPsec input callback, called by the transform callback. Takes care of
  * filtering and other sanity checks on the processed packet.
  */
-void
+int
 ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff)
 {
        int af, sproto;
@@ -364,7 +459,7 @@ ipsec_common_input_cb(struct mbuf *m, st
        if (m == NULL) {
                /* The called routine will print a message if necessary */
                IPSEC_ISTAT(esps_badkcr, ahs_badkcr, ipcomps_badkcr);
-               return;
+               return -1;
        }
 
        /* Fix IPv4 header */
@@ -374,7 +469,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                            __func__, ipsp_address(&tdbp->tdb_dst,
                            buf, sizeof(buf)), ntohl(tdbp->tdb_spi)));
                        IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
-                       return;
+                       return -1;
                }
 
                ip = mtod(m, struct ip *);
@@ -389,7 +484,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        /* ipn will now contain the inner IPv4 header */
                        m_copydata(m, skip, sizeof(struct ip),
@@ -403,7 +498,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        /* ip6n will now contain the inner IPv6 header. */
                        m_copydata(m, skip, sizeof(struct ip6_hdr),
@@ -424,7 +519,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                            buf, sizeof(buf)), ntohl(tdbp->tdb_spi)));
 
                        IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
-                       return;
+                       return -1;
                }
 
                ip6 = mtod(m, struct ip6_hdr *);
@@ -439,7 +534,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        /* ipn will now contain the inner IPv4 header */
                        m_copydata(m, skip, sizeof(struct ip), (caddr_t) &ipn);
@@ -451,7 +546,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        /* ip6n will now contain the inner IPv6 header. */
                        m_copydata(m, skip, sizeof(struct ip6_hdr),
@@ -475,7 +570,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        cksum = 0;
                        m_copyback(m, skip + offsetof(struct udphdr, uh_sum),
@@ -494,7 +589,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                                m_freem(m);
                                IPSEC_ISTAT(esps_hdrops, ahs_hdrops,
                                    ipcomps_hdrops);
-                               return;
+                               return -1;
                        }
                        cksum = 0;
                        m_copyback(m, skip + offsetof(struct tcphdr, th_sum),
@@ -524,7 +619,7 @@ ipsec_common_input_cb(struct mbuf *m, st
                        m_freem(m);
                        DPRINTF(("%s: failed to get tag\n", __func__));
                        IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
-                       return;
+                       return -1;
                }
 
                tdbi = (struct tdb_ident *)(mtag + 1);
@@ -566,6 +661,9 @@ ipsec_common_input_cb(struct mbuf *m, st
        if (tdbp->tdb_flags & TDBF_TUNNELING)
                m->m_flags |= M_TUNNEL;
 
+       ipsecstat_add(ipsec_idecompbytes, m->m_pkthdr.len);
+       tdbp->tdb_idecompbytes += m->m_pkthdr.len;
+
 #if NBPFILTER > 0
        if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) != NULL) {
                encif->if_ipackets++;
@@ -597,20 +695,21 @@ ipsec_common_input_cb(struct mbuf *m, st
                /* This is the enc0 interface unless for ipcomp. */
                if ((ifp = if_get(m->m_pkthdr.ph_ifidx)) == NULL) {
                        m_freem(m);
-                       return;
+                       return -1;
                }
                if (pf_test(af, PF_IN, ifp, &m) != PF_PASS) {
                        if_put(ifp);
                        m_freem(m);
-                       return;
+                       return -1;
                }
                if_put(ifp);
                if (m == NULL)
-                       return;
+                       return -1;
        }
 #endif
        /* Call the appropriate IPsec transform callback. */
        ip_deliver(&m, &skip, prot, af);
+       return 0;
 #undef IPSEC_ISTAT
 }
 
@@ -639,6 +738,8 @@ ipsec_sysctl(int *name, u_int namelen, v
                    ipsec_def_comp, sizeof(ipsec_def_comp));
                NET_UNLOCK();
                return (error);
+       case IPCTL_IPSEC_STATS:
+               return (ipsec_sysctl_ipsecstat(oldp, oldlenp, newp));
        default:
                if (name[0] < IPSEC_MAXID) {
                        NET_LOCK();
@@ -760,6 +861,18 @@ ipcomp_sysctl_ipcompstat(void *oldp, siz
            ipcomps_ncounters);
        return (sysctl_rdstruct(oldp, oldlenp, newp, &ipcompstat,
            sizeof(ipcompstat)));
+}
+
+int
+ipsec_sysctl_ipsecstat(void *oldp, size_t *oldlenp, void *newp)
+{
+       struct ipsecstat ipsecstat;
+
+       CTASSERT(sizeof(ipsecstat) == (ipsec_ncounters * sizeof(uint64_t)));
+       memset(&ipsecstat, 0, sizeof ipsecstat);
+       counters_read(ipseccounters, (uint64_t *)&ipsecstat, ipsec_ncounters);
+       return (sysctl_rdstruct(oldp, oldlenp, newp, &ipsecstat,
+           sizeof(ipsecstat)));
 }
 
 /* IPv4 AH wrapper. */
Index: sys/netinet/ipsec_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ipsec_output.c,v
retrieving revision 1.72
diff -u -p -r1.72 ipsec_output.c
--- sys/netinet/ipsec_output.c  4 Jun 2018 12:13:01 -0000       1.72
+++ sys/netinet/ipsec_output.c  3 Jul 2018 06:29:27 -0000
@@ -46,6 +46,8 @@
 #include <netinet/ip_ah.h>
 #include <netinet/ip_esp.h>
 #include <netinet/ip_ipcomp.h>
+
+#include <crypto/cryptodev.h>
 #include <crypto/xform.h>
 
 #ifdef ENCDEBUG
@@ -358,6 +360,9 @@ ipsp_process_packet(struct mbuf *m, stru
                goto drop;
        }
 
+        ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len);
+        tdb->tdb_ouncompbytes += m->m_pkthdr.len;
+
        /* Non expansion policy for IPCOMP */
        if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
                if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) {
@@ -376,6 +381,85 @@ ipsp_process_packet(struct mbuf *m, stru
 }
 
 /*
+ * IPsec output callback, called directly by the crypto driver.
+ */
+void
+ipsec_output_cb(struct cryptop *crp)
+{
+       struct tdb_crypto *tc = (struct tdb_crypto *) crp->crp_opaque;
+       struct mbuf *m = (struct mbuf *) crp->crp_buf;
+       struct tdb *tdb;
+       int error, ilen, olen;
+
+       if (m == NULL) {
+               DPRINTF(("%s: bogus returned buffer from crypto\n", __func__));
+               ipsecstat_inc(ipsec_crypto);
+               goto droponly;
+       }
+
+       NET_LOCK();
+       tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
+       if (tdb == NULL) {
+               DPRINTF(("%s: TDB is expired while in crypto\n", __func__));
+               ipsecstat_inc(ipsec_notdb);
+               goto baddone;
+       }
+
+       /* Check for crypto errors. */
+       if (crp->crp_etype) {
+               if (crp->crp_etype == EAGAIN) {
+                       /* Reset the session ID */
+                       if (tdb->tdb_cryptoid != 0)
+                               tdb->tdb_cryptoid = crp->crp_sid;
+                       NET_UNLOCK();
+                       crypto_dispatch(crp);
+                       return;
+               }
+               DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
+               ipsecstat_inc(ipsec_noxform);
+               goto baddone;
+       }
+
+       olen = crp->crp_olen;
+       ilen = crp->crp_ilen;
+
+       /* Release crypto descriptors. */
+       crypto_freereq(crp);
+
+       switch (tdb->tdb_sproto) {
+       case IPPROTO_ESP:
+               error = esp_output_cb(tdb, tc, m, ilen, olen);
+               break;
+       case IPPROTO_AH:
+               error = ah_output_cb(tdb, tc, m, ilen, olen);
+               break;
+       case IPPROTO_IPCOMP:
+               error = ipcomp_output_cb(tdb, tc, m, ilen, olen);
+               break;
+       default:
+               panic("%s: unknown/unsupported security protocol %d",
+                   __func__, tdb->tdb_sproto);
+       }
+
+       NET_UNLOCK();
+       if (error) {
+               ipsecstat_inc(ipsec_odrops);
+               tdb->tdb_odrops++;
+       }
+       return;
+
+ baddone:
+       NET_UNLOCK();
+ droponly:
+       if (tdb != NULL)
+               tdb->tdb_odrops++;
+       m_freem(m);
+       free(tc, M_XDATA, 0);
+       crypto_freereq(crp);
+       ipsecstat_inc(ipsec_odrops);
+}
+
+/*
  * Called by the IPsec output transform callbacks, to transmit the packet
  * or do further processing, as necessary.
  */
@@ -492,6 +576,11 @@ ipsp_process_done(struct mbuf *m, struct
        tdbi->rdomain = tdb->tdb_rdomain;
 
        m_tag_prepend(m, mtag);
+
+       ipsecstat_inc(ipsec_opackets);
+       ipsecstat_add(ipsec_obytes, m->m_pkthdr.len);
+       tdb->tdb_opackets++;
+       tdb->tdb_obytes += m->m_pkthdr.len;
 
        /* If there's another (bundled) TDB to apply, do so. */
        if (tdb->tdb_onext)
Index: sys/netinet6/ip6_output.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.237
diff -u -p -r1.237 ip6_output.c
--- sys/netinet6/ip6_output.c   27 Mar 2018 15:03:52 -0000      1.237
+++ sys/netinet6/ip6_output.c   3 Jul 2018 06:29:27 -0000
@@ -2765,6 +2765,7 @@ ip6_output_ipsec_send(struct tdb *tdb, s
 #if NPF > 0
        struct ifnet *encif;
 #endif
+       int error;
 
 #if NPF > 0
        if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL ||
@@ -2786,6 +2787,11 @@ ip6_output_ipsec_send(struct tdb *tdb, s
        m->m_flags &= ~(M_BCAST | M_MCAST);     /* just in case */
 
        /* Callee frees mbuf */
-       return ipsp_process_packet(m, tdb, AF_INET6, tunalready);
+       error = ipsp_process_packet(m, tdb, AF_INET6, tunalready);
+       if (error) {
+               ipsecstat_inc(ipsec_odrops);
+               tdb->tdb_odrops++;
+       }
+       return error;
 }
 #endif /* IPSEC */
Index: sys/sys/percpu.h
===================================================================
RCS file: /cvs/src/sys/sys/percpu.h,v
retrieving revision 1.7
diff -u -p -r1.7 percpu.h
--- sys/sys/percpu.h    23 Feb 2017 00:15:12 -0000      1.7
+++ sys/sys/percpu.h    3 Jul 2018 06:29:27 -0000
@@ -151,6 +151,17 @@ counters_inc(struct cpumem *cm, unsigned
 }
 
 static inline void
+counters_dec(struct cpumem *cm, unsigned int c)
+{
+       struct counters_ref ref;
+       uint64_t *counters;
+
+       counters = counters_enter(&ref, cm);
+       counters[c]--;
+       counters_leave(&ref, cm);
+}
+
+static inline void
 counters_add(struct cpumem *cm, unsigned int c, uint64_t v)
 {
        struct counters_ref ref;
Index: usr.bin/netstat/inet.c
===================================================================
RCS file: /cvs/src/usr.bin/netstat/inet.c,v
retrieving revision 1.162
diff -u -p -r1.162 inet.c
--- usr.bin/netstat/inet.c      7 Nov 2017 16:51:23 -0000       1.162
+++ usr.bin/netstat/inet.c      3 Jul 2018 06:29:27 -0000
@@ -1010,6 +1010,42 @@ etherip_stats(char *name)
 }
 
 /*
+ * Dump IPsec statistics structure.
+ */
+void
+ipsec_stats(char *name)
+{
+       struct ipsecstat ipsecstat;
+       int mib[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPSEC_STATS };
+       size_t len = sizeof(ipsecstat);
+
+       if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
+           &ipsecstat, &len, NULL, 0) == -1) {
+               if (errno != ENOPROTOOPT)
+                       warn("%s", name);
+               return;
+       }
+
+       printf("%s:\n", name);
+#define p(f, m) if (ipsecstat.f || sflag <= 1) \
+       printf(m, ipsecstat.f, plural(ipsecstat.f))
+       p(ipsec_tunnels, "\t%llu active IPsec tunnel%s\n");
+       p(ipsec_prevtunnels, "\t%llu previous IPsec tunnel%s\n");
+       p(ipsec_ipackets, "\t%llu input IPsec packet%s\n");
+       p(ipsec_opackets, "\t%llu output IPsec packet%s\n");
+       p(ipsec_ibytes, "\t%llu input byte%s\n");
+       p(ipsec_obytes, "\t%llu output byte%s\n");
+       p(ipsec_idecompbytes, "\t%llu input byte%s, decompressed\n");
+       p(ipsec_ouncompbytes, "\t%llu output byte%s, uncompressed\n");
+       p(ipsec_idrops, "\t%llu packet%s dropped on input\n");
+       p(ipsec_odrops, "\t%llu packet%s dropped on output\n");
+       p(ipsec_crypto, "\t%llu packet%s that failed crypto processing\n");
+       p(ipsec_noxform, "\t%llu packet%s for which no XFORM was set in TDB 
received\n");
+       p(ipsec_notdb, "\t%llu packet%s for which no TDB was found\n");
+#undef p
+}
+
+/*
  * Dump ESP statistics structure.
  */
 void
Index: usr.bin/netstat/main.c
===================================================================
RCS file: /cvs/src/usr.bin/netstat/main.c,v
retrieving revision 1.112
diff -u -p -r1.112 main.c
--- usr.bin/netstat/main.c      12 Aug 2017 03:21:02 -0000      1.112
+++ usr.bin/netstat/main.c      3 Jul 2018 06:29:27 -0000
@@ -77,6 +77,7 @@ struct protox {
        { ipip_stats,   "ipencap", 0 },
        { tcp_stats,    "tcp",  IPPROTO_TCP },
        { udp_stats,    "udp",  IPPROTO_UDP },
+       { ipsec_stats,  "ipsec", 0 },
        { esp_stats,    "esp", 0 },
        { ah_stats,     "ah", 0 },
        { etherip_stats,"etherip", 0 },
Index: usr.bin/netstat/netstat.h
===================================================================
RCS file: /cvs/src/usr.bin/netstat/netstat.h,v
retrieving revision 1.71
diff -u -p -r1.71 netstat.h
--- usr.bin/netstat/netstat.h   12 Aug 2017 03:21:02 -0000      1.71
+++ usr.bin/netstat/netstat.h   3 Jul 2018 06:29:27 -0000
@@ -92,6 +92,7 @@ void  icmp_stats(char *);
 void   igmp_stats(char *);
 void   pim_stats(char *);
 void   ah_stats(char *);
+void   ipsec_stats(char *);
 void   esp_stats(char *);
 void   ipip_stats(char *);
 void   carp_stats (char *);

Reply via email to