summaryrefslogtreecommitdiff
path: root/sbin/iked/ikev2.c
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2014-02-14 09:00:04 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2014-02-14 09:00:04 +0000
commitf4e9a9ffa848780f796a2edef5fa8e2d43dcecff (patch)
tree712e5771110f6982e21a8f8bc7369a74c3ddec28 /sbin/iked/ikev2.c
parentbc74558dd64e1cf7d15e7047cddaf618301aa4ea (diff)
initial support for IPComp
still experimental and rekeying needs some work; ok mikeb@
Diffstat (limited to 'sbin/iked/ikev2.c')
-rw-r--r--sbin/iked/ikev2.c173
1 files changed, 172 insertions, 1 deletions
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 2714013900e..8c61528f8a6 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.91 2014/01/24 07:35:55 markus Exp $ */
+/* $OpenBSD: ikev2.c,v 1.92 2014/02/14 09:00:03 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -100,10 +100,14 @@ ssize_t ikev2_add_ts(struct ibuf *, struct ikev2_payload **, ssize_t,
struct iked_sa *, int);
ssize_t ikev2_add_certreq(struct ibuf *, struct ikev2_payload **, ssize_t,
struct ibuf *, u_int8_t);
+ssize_t ikev2_add_ipcompnotify(struct iked *, struct ibuf *,
+ struct ikev2_payload **, ssize_t, struct iked_sa *);
ssize_t ikev2_add_ts_payload(struct ibuf *, u_int, struct iked_sa *);
int ikev2_add_data(struct ibuf *, void *, size_t);
int ikev2_add_buf(struct ibuf *buf, struct ibuf *);
+int ikev2_ipcomp_enable(struct iked_sa *);
+
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, ikev2_dispatch_parent },
{ "ikev1", PROC_IKEV1, ikev2_dispatch_ikev1 },
@@ -1010,6 +1014,11 @@ ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa)
goto done;
}
+ /* compression */
+ if ((pol->pol_flags & IKED_POLICY_IPCOMP) &&
+ (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1)
+ goto done;
+
if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1)
goto done;
@@ -1347,6 +1356,57 @@ ikev2_add_certreq(struct ibuf *e, struct ikev2_payload **pld, ssize_t len,
return (len);
}
+ssize_t
+ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e,
+ struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa)
+{
+ struct iked_childsa csa;
+ struct ikev2_notify *n;
+ u_int8_t *ptr;
+ u_int16_t cpi;
+ u_int32_t spi;
+ u_int8_t transform;
+
+ /* we only support deflate */
+ transform = IKEV2_IPCOMP_DEFLATE;
+
+ bzero(&csa, sizeof(csa));
+ csa.csa_saproto = IKEV2_SAPROTO_IPCOMP;
+ csa.csa_ikesa = sa;
+ csa.csa_local = &sa->sa_peer;
+ csa.csa_peer = &sa->sa_local;
+
+ if (pfkey_sa_init(env->sc_pfkey, &csa, &spi) == -1)
+ return (-1);
+ /*
+ * We get spi == 0 if the kernel does not support IPcomp,
+ * so just return the length of the current payload.
+ */
+ if (spi == 0)
+ return (len);
+ cpi = htobe16((u_int16_t)spi);
+ if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1)
+ return (-1);
+ if ((*pld = ikev2_add_payload(e)) == NULL)
+ return (-1);
+ len = sizeof(*n) + sizeof(cpi) + sizeof(transform);
+ if ((ptr = ibuf_advance(e, len)) == NULL)
+ return (-1);
+ n = (struct ikev2_notify *)ptr;
+ n->n_protoid = 0;
+ n->n_spisize = 0;
+ n->n_type = htobe16(IKEV2_N_IPCOMP_SUPPORTED);
+ ptr += sizeof(*n);
+ memcpy(ptr, &cpi, sizeof(cpi));
+ ptr += sizeof(cpi);
+ memcpy(ptr, &transform, sizeof(transform));
+
+ sa->sa_cpi_in = spi; /* already on host byte order */
+ log_debug("%s: sa_cpi_in 0x%04x", __func__, sa->sa_cpi_in);
+
+ return (len);
+}
+
int
ikev2_next_payload(struct ikev2_payload *pld, size_t length,
u_int8_t nextpayload)
@@ -2075,6 +2135,11 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
goto done;
}
+ /* compression */
+ if (sa->sa_ipcomp &&
+ (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa)) == -1)
+ goto done;
+
if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1)
goto done;
@@ -2692,6 +2757,9 @@ ikev2_ike_sa_alive(struct iked *env, void *arg)
/* check for incoming traffic on any child SA */
TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
+ if (!csa->csa_loaded ||
+ csa->csa_saproto == IKEV2_SAPROTO_IPCOMP)
+ continue;
if (pfkey_sa_last_used(env->sc_pfkey, csa, &last_used) != 0)
continue;
gettimeofday(&tv, NULL);
@@ -3832,11 +3900,114 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
}
int
+ikev2_ipcomp_enable(struct iked_sa *sa)
+{
+ struct iked_childsa *other, *csa = NULL, *csb = NULL;
+ struct iked_flow *flow, *flowa = NULL, *flowb = NULL;
+
+ if ((csa = calloc(1, sizeof(*csa))) == NULL ||
+ (csb = calloc(1, sizeof(*csb))) == NULL ||
+ (flowa = calloc(1, sizeof(*flowa))) == NULL ||
+ (flowb = calloc(1, sizeof(*flowb))) == NULL) {
+ free(csa);
+ free(csb);
+ free(flowa);
+ free(flowb);
+ return (-1);
+ }
+
+ /* switch ESP SAs to transport mode */
+ TAILQ_FOREACH(other, &sa->sa_childsas, csa_entry)
+ if (!other->csa_rekey && !other->csa_loaded &&
+ other->csa_saproto == IKEV2_SAPROTO_ESP)
+ other->csa_transport = 1;
+
+ /* install IPCOMP SAs */
+ csa->csa_ikesa = sa;
+ csa->csa_saproto = IKEV2_SAPROTO_IPCOMP;
+ if (sa->sa_hdr.sh_initiator) {
+ csa->csa_dstid = &sa->sa_rid;
+ csa->csa_srcid = &sa->sa_iid;
+ } else {
+ csa->csa_dstid = &sa->sa_iid;
+ csa->csa_srcid = &sa->sa_rid;
+ }
+ csa->csa_spi.spi_size = 2;
+ csa->csa_spi.spi = sa->sa_cpi_out;
+ csa->csa_peerspi = sa->sa_cpi_in;
+ csa->csa_dir = IPSP_DIRECTION_OUT;
+ csa->csa_local = &sa->sa_local;
+ csa->csa_peer = &sa->sa_peer;
+ csb->csa_allocated = 0;
+
+ memcpy(csb, csa, sizeof(*csb));
+ csb->csa_spi.spi = csa->csa_peerspi;
+ csb->csa_peerspi = csa->csa_spi.spi;
+ csb->csa_dir = IPSP_DIRECTION_IN;
+ csb->csa_local = csa->csa_peer;
+ csb->csa_peer = csa->csa_local;
+ csb->csa_allocated = 1;
+
+ TAILQ_INSERT_TAIL(&sa->sa_childsas, csa, csa_entry);
+ TAILQ_INSERT_TAIL(&sa->sa_childsas, csb, csa_entry);
+
+ csa->csa_peersa = csb;
+ csb->csa_peersa = csa;
+
+ /* redirect flows to IPCOMP */
+ TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
+ if (flow->flow_loaded ||
+ flow->flow_saproto != IKEV2_SAPROTO_ESP)
+ continue;
+ log_debug("%s: flow %p saproto %d -> %d", __func__,
+ flow, flow->flow_saproto, IKEV2_SAPROTO_IPCOMP);
+ flow->flow_saproto = IKEV2_SAPROTO_IPCOMP;
+ }
+
+ /* setup ESP flows for gateways */
+ flowa->flow_dir = IPSP_DIRECTION_OUT;
+ flowa->flow_saproto = IKEV2_SAPROTO_ESP;
+ if (sa->sa_hdr.sh_initiator) {
+ flowa->flow_dstid = &sa->sa_rid;
+ flowa->flow_srcid = &sa->sa_iid;
+ } else {
+ flowa->flow_dstid = &sa->sa_iid;
+ flowa->flow_srcid = &sa->sa_rid;
+ }
+ flowa->flow_local = &sa->sa_local;
+ flowa->flow_peer = &sa->sa_peer;
+ memcpy(&flowa->flow_src, &sa->sa_local, sizeof(sa->sa_local));
+ memcpy(&flowa->flow_dst, &sa->sa_peer, sizeof(sa->sa_peer));
+ flowa->flow_src.addr_mask = flowa->flow_dst.addr_mask =
+ (sa->sa_local.addr_af == AF_INET) ? 32 : 128;
+ flowa->flow_src.addr_port = flowa->flow_dst.addr_port = 0;
+ flowa->flow_ikesa = sa;
+
+ memcpy(flowb, flowa, sizeof(*flowb));
+ flowb->flow_dir = IPSP_DIRECTION_IN;
+ memcpy(&flowb->flow_dst, &flowa->flow_src, sizeof(flowa->flow_src));
+ memcpy(&flowb->flow_src, &flowa->flow_dst, sizeof(flowa->flow_dst));
+
+ TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry);
+ TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry);
+
+ /* make sure IPCOMP CPIs are not reused */
+ sa->sa_ipcomp = 0;
+ sa->sa_cpi_in = sa->sa_cpi_out = 0;
+
+ return (0);
+}
+
+int
ikev2_childsa_enable(struct iked *env, struct iked_sa *sa)
{
struct iked_childsa *csa;
struct iked_flow *flow, *oflow;
+ if (sa->sa_ipcomp && sa->sa_cpi_in && sa->sa_cpi_out &&
+ ikev2_ipcomp_enable(sa) == -1)
+ return (-1);
+
TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
if (csa->csa_rekey || csa->csa_loaded)
continue;