From 4ec8e25772ec895c764863f5d4828a7311b1d90e Mon Sep 17 00:00:00 2001 From: mpi Date: Tue, 10 Jul 2018 11:34:12 +0000 Subject: [PATCH] Introduce new IPsec (per-CPU) statistics and refactor ESP input callbacks to be able to count dropped packet. Having more generic statistics will help troubleshooting problems with specific tunnels. Per-TDB counters are coming once all the refactoring bits are in. ok markus@ --- sys/netinet/in.h | 7 ++- sys/netinet/ip_esp.c | 55 +++-------------- sys/netinet/ip_input.c | 3 +- sys/netinet/ip_ipsp.h | 64 ++++++++++++++++--- sys/netinet/ipsec_input.c | 125 +++++++++++++++++++++++++++++++++----- 5 files changed, 181 insertions(+), 73 deletions(-) diff --git a/sys/netinet/in.h b/sys/netinet/in.h index c864c56d09c..7a83851a53a 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in.h,v 1.130 2018/06/07 08:46:24 bluhm Exp $ */ +/* $OpenBSD: in.h,v 1.131 2018/07/10 11:34:12 mpi Exp $ */ /* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */ /* @@ -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 */, \ diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c index 31499cdd3fd..ac80fa4b359 100644 --- a/sys/netinet/ip_esp.c +++ b/sys/netinet/ip_esp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp.c,v 1.154 2018/05/09 16:00:28 bluhm Exp $ */ +/* $OpenBSD: ip_esp.c,v 1.155 2018/07/10 11:34:12 mpi Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -70,7 +70,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 @@ -492,7 +491,7 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) 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 +530,26 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) /* * 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) { 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 +715,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; } /* diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index d02d5bed158..4585b7f8b06 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_input.c,v 1.337 2018/05/21 15:52:22 bluhm Exp $ */ +/* $OpenBSD: ip_input.c,v 1.338 2018/07/10 11:34:12 mpi Exp $ */ /* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */ /* @@ -1623,6 +1623,7 @@ ip_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, 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: diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h index 1d7fb6c3b2f..a3944c1e2c8 100644 --- a/sys/netinet/ip_ipsp.h +++ b/sys/netinet/ip_ipsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.h,v 1.189 2017/11/20 14:14:26 mpi Exp $ */ +/* $OpenBSD: ip_ipsp.h,v 1.190 2018/07/10 11:34:12 mpi Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr), @@ -40,14 +40,10 @@ #ifndef _NETINET_IPSP_H_ #define _NETINET_IPSP_H_ -struct m_tag; - /* IPSP global definitions. */ #include -#include #include -#include union sockaddr_union { struct sockaddr sa; @@ -125,9 +121,58 @@ struct sockaddr_encap { #define IPSP_DIRECTION_IN 0x1 #define IPSP_DIRECTION_OUT 0x2 +struct ipsecstat { + 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 */ +}; + #ifdef _KERNEL + #include #include +#include +#include +#include + +enum ipsec_counters { + 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_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 @@ -424,6 +469,7 @@ extern int ipsec_exp_first_use; /* seconds between 1st asso & expire */ * 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 +497,7 @@ extern int ipsec_exp_first_use; /* seconds between 1st asso & expire */ NULL, \ NULL, \ &encdebug, \ - NULL, \ + NULL, /* ipsecstat */ \ &ipsec_expire_acquire, \ &ipsec_keep_invalid, \ &ipsec_require_pfs, \ @@ -482,6 +528,8 @@ extern struct comp_algo comp_algo_deflate; 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); @@ -540,6 +588,7 @@ 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 esp_output(struct mbuf *, struct tdb *, struct mbuf **, int, int); int esp_sysctl(int *, u_int, void *, size_t *, void *, size_t); @@ -592,7 +641,8 @@ 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 *); +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); diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c index 112a5535bb0..50bba534b00 100644 --- a/sys/netinet/ipsec_input.c +++ b/sys/netinet/ipsec_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsec_input.c,v 1.163 2018/05/14 15:24:23 bluhm Exp $ */ +/* $OpenBSD: ipsec_input.c,v 1.164 2018/07/10 11:34:12 mpi Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -77,6 +77,9 @@ #include +#include +#include + #include "bpfilter.h" void ipsec_common_ctlinput(u_int, int, struct sockaddr *, void *, int); @@ -112,6 +115,7 @@ int *ipcompctl_vars[IPCOMPCTL_MAXID] = IPCOMPCTL_VARS; 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] = IPSECCTL_VARS; 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 skip, int protoff, int af, int sproto, 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) { @@ -322,6 +330,8 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto, * everything else. */ error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff); + if (error) + ipsecstat_inc(ipsec_idrops); return error; drop: @@ -329,11 +339,79 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto, 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 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; + } + + /* Release the crypto descriptors */ + crypto_freereq(crp); + + switch (tdb->tdb_sproto) { + case IPPROTO_ESP: + error = esp_input_cb(tdb, tc, m); + break; + case IPPROTO_AH: + break; + case IPPROTO_IPCOMP: + break; + default: + panic("%s: unknown/unsupported security protocol %d", + __func__, tdb->tdb_sproto); + } + + NET_UNLOCK(); + if (error) + ipsecstat_inc(ipsec_idrops); + return; + + baddone: + NET_UNLOCK(); + droponly: + ipsecstat_inc(ipsec_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 +442,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +452,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) __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 +467,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +481,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +502,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +517,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +529,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +553,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +572,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +602,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) 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 +644,8 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) if (tdbp->tdb_flags & TDBF_TUNNELING) m->m_flags |= M_TUNNEL; + ipsecstat_add(ipsec_idecompbytes, m->m_pkthdr.len); + #if NBPFILTER > 0 if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) != NULL) { encif->if_ipackets++; @@ -597,20 +677,21 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) /* 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 +720,8 @@ ipsec_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, 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(); @@ -762,6 +845,18 @@ ipcomp_sysctl_ipcompstat(void *oldp, size_t *oldlenp, void *newp) 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. */ int ah4_input(struct mbuf **mp, int *offp, int proto, int af) -- 2.20.1