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@
-/* $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 $ */
/*
#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
{ "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 }, \
&ipport_hilastauto, \
&ip_maxqueue, \
NULL /* encdebug */, \
- NULL, \
+ NULL /* ipsecstat */, \
NULL /* ipsec_expire_acquire */, \
NULL /* ipsec_keep_invalid */, \
NULL /* ipsec_require_pfs */, \
-/* $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
#include "bpfilter.h"
void esp_output_cb(struct cryptop *);
-void esp_input_cb(struct cryptop *);
#ifdef ENCDEBUG
#define DPRINTF(x) if (encdebug) printf x
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;
/*
* 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 */
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;
}
/*
-/* $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 $ */
/*
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:
-/* $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),
#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;
#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 <sys/timeout.h>
#include <sys/tree.h>
+#include <sys/queue.h>
+#include <net/radix.h>
+#include <sys/percpu.h>
+
+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
* 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 */
NULL, \
NULL, \
&encdebug, \
- NULL, \
+ NULL, /* ipsecstat */ \
&ipsec_expire_acquire, \
&ipsec_keep_invalid, \
&ipsec_require_pfs, \
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);
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);
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);
-/* $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
#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);
struct cpumem *espcounters;
struct cpumem *ahcounters;
struct cpumem *ipcompcounters;
+struct cpumem *ipseccounters;
char ipsec_def_enc[20];
char ipsec_def_auth[20];
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)
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));
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) {
* everything else.
*/
error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff);
+ if (error)
+ ipsecstat_inc(ipsec_idrops);
return error;
drop:
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;
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 */
__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 *);
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),
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),
buf, sizeof(buf)), ntohl(tdbp->tdb_spi)));
IPSEC_ISTAT(esps_hdrops, ahs_hdrops, ipcomps_hdrops);
- return;
+ return -1;
}
ip6 = mtod(m, struct ip6_hdr *);
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);
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),
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),
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),
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);
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++;
/* 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
}
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();
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)