1*f36db9c4Syasuoka /* $OpenBSD: pfkey.c,v 1.85 2024/07/13 12:22:46 yasuoka Exp $ */
245ae9d61Sreyk
345ae9d61Sreyk /*
4fcebd35dSreyk * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
545ae9d61Sreyk * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
645ae9d61Sreyk * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
745ae9d61Sreyk * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
845ae9d61Sreyk *
945ae9d61Sreyk * Permission to use, copy, modify, and distribute this software for any
1045ae9d61Sreyk * purpose with or without fee is hereby granted, provided that the above
1145ae9d61Sreyk * copyright notice and this permission notice appear in all copies.
1245ae9d61Sreyk *
1345ae9d61Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1445ae9d61Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1545ae9d61Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1645ae9d61Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1745ae9d61Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1845ae9d61Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1945ae9d61Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2045ae9d61Sreyk */
2145ae9d61Sreyk
2245ae9d61Sreyk #include <sys/queue.h>
2345ae9d61Sreyk #include <sys/uio.h>
2445ae9d61Sreyk #include <sys/socket.h>
2545ae9d61Sreyk
2645ae9d61Sreyk #include <netinet/in.h>
2745ae9d61Sreyk #include <netinet/ip_ipsp.h>
281c18b693Stobhe #include <net/if.h>
2945ae9d61Sreyk #include <net/pfkeyv2.h>
3045ae9d61Sreyk
3145ae9d61Sreyk #include <err.h>
3245ae9d61Sreyk #include <errno.h>
3345ae9d61Sreyk #include <stdio.h>
34f0de6e10Sderaadt #include <poll.h>
3545ae9d61Sreyk #include <string.h>
3645ae9d61Sreyk #include <stdlib.h>
3745ae9d61Sreyk #include <unistd.h>
3845ae9d61Sreyk #include <event.h>
3945ae9d61Sreyk
4045ae9d61Sreyk #include "iked.h"
4145ae9d61Sreyk #include "ikev2.h"
4245ae9d61Sreyk
4345ae9d61Sreyk #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
441c18b693Stobhe #define IOV_CNT 28
4545ae9d61Sreyk
46d09d3a7dSreyk #define PFKEYV2_CHUNK sizeof(uint64_t)
473f3558dcSmarkus #define PFKEY_REPLY_TIMEOUT 1000
4845ae9d61Sreyk
49c0b327e6Spatrick /* only used internally */
50c0b327e6Spatrick #define IKED_SADB_UPDATE_SA_ADDRESSES 0xff
51c0b327e6Spatrick
52d09d3a7dSreyk static uint32_t sadb_msg_seq = 0;
53d09d3a7dSreyk static unsigned int sadb_decoupled = 0;
5445ae9d61Sreyk
550347364bStobhe static int iked_rdomain = 0;
560347364bStobhe
57d1ebe3d0Smikeb static struct event pfkey_timer_ev;
58d1ebe3d0Smikeb static struct timeval pfkey_timer_tv;
59d1ebe3d0Smikeb
60d1ebe3d0Smikeb struct pfkey_message {
61d1ebe3d0Smikeb SIMPLEQ_ENTRY(pfkey_message)
62d1ebe3d0Smikeb pm_entry;
63d09d3a7dSreyk uint8_t *pm_data;
641c9ff2fdSblambert ssize_t pm_length;
65d1ebe3d0Smikeb };
666e1880a3Smarkus SIMPLEQ_HEAD(, pfkey_message) pfkey_retry, pfkey_postponed =
67d1ebe3d0Smikeb SIMPLEQ_HEAD_INITIALIZER(pfkey_postponed);
68d1ebe3d0Smikeb
6945ae9d61Sreyk struct pfkey_constmap {
70d09d3a7dSreyk uint8_t pfkey_id;
71d09d3a7dSreyk unsigned int pfkey_ikeid;
72d09d3a7dSreyk unsigned int pfkey_fixedkey;
7345ae9d61Sreyk };
7445ae9d61Sreyk
7545ae9d61Sreyk static const struct pfkey_constmap pfkey_encr[] = {
7645ae9d61Sreyk { SADB_EALG_3DESCBC, IKEV2_XFORMENCR_3DES },
7745ae9d61Sreyk { SADB_X_EALG_CAST, IKEV2_XFORMENCR_CAST },
7845ae9d61Sreyk { SADB_X_EALG_BLF, IKEV2_XFORMENCR_BLOWFISH },
7945ae9d61Sreyk { SADB_EALG_NULL, IKEV2_XFORMENCR_NULL },
8045ae9d61Sreyk { SADB_X_EALG_AES, IKEV2_XFORMENCR_AES_CBC },
8145ae9d61Sreyk { SADB_X_EALG_AESCTR, IKEV2_XFORMENCR_AES_CTR },
823fa77592Smikeb { SADB_X_EALG_AESGCM16, IKEV2_XFORMENCR_AES_GCM_16 },
833fa77592Smikeb { SADB_X_EALG_AESGMAC, IKEV2_XFORMENCR_NULL_AES_GMAC },
84b3954071Smikeb { SADB_X_EALG_CHACHA20POLY1305, IKEV2_XFORMENCR_CHACHA20_POLY1305 },
8545ae9d61Sreyk { 0 }
8645ae9d61Sreyk };
8745ae9d61Sreyk
8845ae9d61Sreyk static const struct pfkey_constmap pfkey_integr[] = {
8945ae9d61Sreyk { SADB_AALG_MD5HMAC, IKEV2_XFORMAUTH_HMAC_MD5_96 },
9045ae9d61Sreyk { SADB_AALG_SHA1HMAC, IKEV2_XFORMAUTH_HMAC_SHA1_96 },
9145ae9d61Sreyk { SADB_X_AALG_SHA2_256, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },
9245ae9d61Sreyk { SADB_X_AALG_SHA2_384, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 },
9345ae9d61Sreyk { SADB_X_AALG_SHA2_512, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 },
9445ae9d61Sreyk { 0 }
9545ae9d61Sreyk };
9645ae9d61Sreyk
9745ae9d61Sreyk static const struct pfkey_constmap pfkey_satype[] = {
9845ae9d61Sreyk { SADB_SATYPE_AH, IKEV2_SAPROTO_AH },
9945ae9d61Sreyk { SADB_SATYPE_ESP, IKEV2_SAPROTO_ESP },
10003f6ad09Smarkus { SADB_X_SATYPE_IPCOMP, IKEV2_SAPROTO_IPCOMP },
10145ae9d61Sreyk { 0 }
10245ae9d61Sreyk };
10345ae9d61Sreyk
104d09d3a7dSreyk int pfkey_map(const struct pfkey_constmap *, uint16_t, uint8_t *);
1058f6f6c19Stobhe int pfkey_flow(struct iked *, uint8_t, uint8_t, struct iked_flow *);
1068f6f6c19Stobhe int pfkey_sa(struct iked *, uint8_t, uint8_t, struct iked_childsa *);
1078f6f6c19Stobhe int pfkey_sa_getspi(struct iked *, uint8_t, struct iked_childsa *, uint32_t *);
1088f6f6c19Stobhe int pfkey_sagroup(struct iked *, uint8_t, uint8_t,
10945ae9d61Sreyk struct iked_childsa *, struct iked_childsa *);
110e7c3d382Stobhe int pfkey_write(struct iked *, struct sadb_msg *, struct iovec *, int,
111d09d3a7dSreyk uint8_t **, ssize_t *);
112d09d3a7dSreyk int pfkey_reply(int, uint8_t **, ssize_t *);
1136417b90fSreyk void pfkey_dispatch(int, short, void *);
114*f36db9c4Syasuoka int pfkey_sa_lookup(struct iked *, struct iked_childsa *, uint64_t *,
115*f36db9c4Syasuoka struct iked_sastats *);
1168f6f6c19Stobhe int pfkey_sa_check_exists(struct iked *, struct iked_childsa *);
117*f36db9c4Syasuoka int pfkey_sa_sastats(struct iked *, struct iked_childsa *,
118*f36db9c4Syasuoka struct iked_sastats *);
1194292c78bSreyk
12045ae9d61Sreyk struct sadb_ident *
121d09d3a7dSreyk pfkey_id2ident(struct iked_id *, unsigned int);
122d09d3a7dSreyk void *pfkey_find_ext(uint8_t *, ssize_t, int);
123b0eeedd0Smikeb
124d1ebe3d0Smikeb void pfkey_timer_cb(int, short, void *);
1256e1880a3Smarkus int pfkey_process(struct iked *, struct pfkey_message *);
12645ae9d61Sreyk
12745ae9d61Sreyk int
pfkey_couple(struct iked * env,struct iked_sas * sas,int couple)1288f6f6c19Stobhe pfkey_couple(struct iked *env, struct iked_sas *sas, int couple)
129fc20f985Sreyk {
130fc20f985Sreyk struct iked_sa *sa;
131fc20f985Sreyk struct iked_flow *flow;
132e7fee6f8Stobhe struct iked_childsa *csa, *ipcomp;
133fc20f985Sreyk const char *mode[] = { "coupled", "decoupled" };
134fc20f985Sreyk
135fc20f985Sreyk /* Socket is not ready */
1368f6f6c19Stobhe if (env->sc_pfkey == -1)
137fc20f985Sreyk return (-1);
138fc20f985Sreyk
139af52b2a8Svgross if (sadb_decoupled == !couple)
140fc20f985Sreyk return (0);
141fc20f985Sreyk
142fc20f985Sreyk log_debug("%s: kernel %s -> %s", __func__,
143af52b2a8Svgross mode[sadb_decoupled], mode[!sadb_decoupled]);
144af52b2a8Svgross
145af52b2a8Svgross /* Allow writes to the PF_KEY socket */
146af52b2a8Svgross sadb_decoupled = 0;
147fc20f985Sreyk
148fc20f985Sreyk RB_FOREACH(sa, iked_sas, sas) {
149fc20f985Sreyk TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
150af52b2a8Svgross if (!csa->csa_loaded && couple)
1518f6f6c19Stobhe (void)pfkey_sa_add(env, csa, NULL);
152af52b2a8Svgross else if (csa->csa_loaded && !couple)
1538f6f6c19Stobhe (void)pfkey_sa_delete(env, csa);
154e7fee6f8Stobhe if ((ipcomp = csa->csa_bundled) != NULL) {
155e7fee6f8Stobhe if (!ipcomp->csa_loaded && couple)
1568f6f6c19Stobhe (void)pfkey_sa_add(env, ipcomp, csa);
157e7fee6f8Stobhe else if (ipcomp->csa_loaded && !couple)
1588f6f6c19Stobhe (void)pfkey_sa_delete(env, ipcomp);
159e7fee6f8Stobhe }
160fc20f985Sreyk }
161fc20f985Sreyk TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
162af52b2a8Svgross if (!flow->flow_loaded && couple)
1638f6f6c19Stobhe (void)pfkey_flow_add(env, flow);
164af52b2a8Svgross else if (flow->flow_loaded && !couple)
1658f6f6c19Stobhe (void)pfkey_flow_delete(env, flow);
166fc20f985Sreyk }
167fc20f985Sreyk }
168fc20f985Sreyk
169af52b2a8Svgross sadb_decoupled = !couple;
170af52b2a8Svgross
171fc20f985Sreyk return (0);
172fc20f985Sreyk }
173fc20f985Sreyk
174fc20f985Sreyk int
pfkey_map(const struct pfkey_constmap * map,uint16_t alg,uint8_t * pfkalg)175d09d3a7dSreyk pfkey_map(const struct pfkey_constmap *map, uint16_t alg, uint8_t *pfkalg)
17645ae9d61Sreyk {
17745ae9d61Sreyk int i;
17845ae9d61Sreyk
17945ae9d61Sreyk for (i = 0; map[i].pfkey_id != 0; i++)
18045ae9d61Sreyk if (map[i].pfkey_ikeid == alg) {
18145ae9d61Sreyk *pfkalg = map[i].pfkey_id;
18245ae9d61Sreyk return (0);
18345ae9d61Sreyk }
18445ae9d61Sreyk return (-1);
18545ae9d61Sreyk }
18645ae9d61Sreyk
18745ae9d61Sreyk int
pfkey_flow(struct iked * env,uint8_t satype,uint8_t action,struct iked_flow * flow)1888f6f6c19Stobhe pfkey_flow(struct iked *env, uint8_t satype, uint8_t action, struct iked_flow *flow)
18945ae9d61Sreyk {
19045ae9d61Sreyk struct sadb_msg smsg;
1917cd0af1dSvgross struct iked_addr *flow_src, *flow_dst;
19245ae9d61Sreyk struct sadb_address sa_src, sa_dst, sa_local, sa_peer, sa_smask,
19345ae9d61Sreyk sa_dmask;
19445ae9d61Sreyk struct sadb_protocol sa_flowtype, sa_protocol;
1950347364bStobhe struct sadb_x_rdomain sa_rdomain;
19645ae9d61Sreyk struct sadb_ident *sa_srcid, *sa_dstid;
19745ae9d61Sreyk struct sockaddr_storage ssrc, sdst, slocal, speer, smask, dmask;
19845ae9d61Sreyk struct iovec iov[IOV_CNT];
1994292c78bSreyk int iov_cnt, ret = -1;
200eab256a9Stobhe uint64_t pad = 0;
201eab256a9Stobhe size_t padlen;
20245ae9d61Sreyk
20345ae9d61Sreyk sa_srcid = sa_dstid = NULL;
20445ae9d61Sreyk
2057cd0af1dSvgross flow_src = &flow->flow_src;
2067cd0af1dSvgross flow_dst = &flow->flow_dst;
2077cd0af1dSvgross
2087cd0af1dSvgross if (flow->flow_prenat.addr_af == flow_src->addr_af) {
2097cd0af1dSvgross if (flow->flow_dir == IPSP_DIRECTION_IN)
2107cd0af1dSvgross flow_dst = &flow->flow_prenat;
2117cd0af1dSvgross else
2127cd0af1dSvgross flow_src = &flow->flow_prenat;
2137cd0af1dSvgross }
2147cd0af1dSvgross
21545ae9d61Sreyk bzero(&ssrc, sizeof(ssrc));
21645ae9d61Sreyk bzero(&smask, sizeof(smask));
2177cd0af1dSvgross memcpy(&ssrc, &flow_src->addr, sizeof(ssrc));
2187cd0af1dSvgross memcpy(&smask, &flow_src->addr, sizeof(smask));
2197cd0af1dSvgross socket_af((struct sockaddr *)&ssrc, flow_src->addr_port);
2207cd0af1dSvgross socket_af((struct sockaddr *)&smask, flow_src->addr_port ?
2219b76b366Sjsg 0xffff : 0);
22245ae9d61Sreyk
2237cd0af1dSvgross switch (flow_src->addr_af) {
22445ae9d61Sreyk case AF_INET:
22545ae9d61Sreyk ((struct sockaddr_in *)&smask)->sin_addr.s_addr =
2267cd0af1dSvgross prefixlen2mask(flow_src->addr_net ?
2277cd0af1dSvgross flow_src->addr_mask : 32);
22845ae9d61Sreyk break;
22945ae9d61Sreyk case AF_INET6:
2307cd0af1dSvgross prefixlen2mask6(flow_src->addr_net ?
2317cd0af1dSvgross flow_src->addr_mask : 128,
232d09d3a7dSreyk (uint32_t *)((struct sockaddr_in6 *)
23345ae9d61Sreyk &smask)->sin6_addr.s6_addr);
23445ae9d61Sreyk break;
23545ae9d61Sreyk default:
23645ae9d61Sreyk log_warnx("%s: unsupported address family %d",
2377cd0af1dSvgross __func__, flow_src->addr_af);
23845ae9d61Sreyk return (-1);
23945ae9d61Sreyk }
24045ae9d61Sreyk smask.ss_len = ssrc.ss_len;
24145ae9d61Sreyk
24245ae9d61Sreyk bzero(&sdst, sizeof(sdst));
24345ae9d61Sreyk bzero(&dmask, sizeof(dmask));
2447cd0af1dSvgross memcpy(&sdst, &flow_dst->addr, sizeof(sdst));
2457cd0af1dSvgross memcpy(&dmask, &flow_dst->addr, sizeof(dmask));
2467cd0af1dSvgross socket_af((struct sockaddr *)&sdst, flow_dst->addr_port);
2477cd0af1dSvgross socket_af((struct sockaddr *)&dmask, flow_dst->addr_port ?
248a38650c7Smikeb 0xffff : 0);
24945ae9d61Sreyk
2507cd0af1dSvgross switch (flow_dst->addr_af) {
25145ae9d61Sreyk case AF_INET:
25245ae9d61Sreyk ((struct sockaddr_in *)&dmask)->sin_addr.s_addr =
2537cd0af1dSvgross prefixlen2mask(flow_dst->addr_net ?
2547cd0af1dSvgross flow_dst->addr_mask : 32);
25545ae9d61Sreyk break;
25645ae9d61Sreyk case AF_INET6:
2577cd0af1dSvgross prefixlen2mask6(flow_dst->addr_net ?
2587cd0af1dSvgross flow_dst->addr_mask : 128,
259d09d3a7dSreyk (uint32_t *)((struct sockaddr_in6 *)
26045ae9d61Sreyk &dmask)->sin6_addr.s6_addr);
26145ae9d61Sreyk break;
26245ae9d61Sreyk default:
26345ae9d61Sreyk log_warnx("%s: unsupported address family %d",
2647cd0af1dSvgross __func__, flow_dst->addr_af);
26545ae9d61Sreyk return (-1);
26645ae9d61Sreyk }
26745ae9d61Sreyk dmask.ss_len = sdst.ss_len;
26845ae9d61Sreyk
26945ae9d61Sreyk bzero(&slocal, sizeof(slocal));
27045ae9d61Sreyk bzero(&speer, sizeof(speer));
2715342a326Sreyk if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
27245ae9d61Sreyk memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal));
27345ae9d61Sreyk socket_af((struct sockaddr *)&slocal, 0);
27445ae9d61Sreyk
27545ae9d61Sreyk memcpy(&speer, &flow->flow_peer->addr, sizeof(speer));
27645ae9d61Sreyk socket_af((struct sockaddr *)&speer, 0);
27745ae9d61Sreyk }
27845ae9d61Sreyk
27945ae9d61Sreyk bzero(&smsg, sizeof(smsg));
28045ae9d61Sreyk smsg.sadb_msg_version = PF_KEY_V2;
281b0eeedd0Smikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
28245ae9d61Sreyk smsg.sadb_msg_pid = getpid();
28345ae9d61Sreyk smsg.sadb_msg_len = sizeof(smsg) / 8;
28445ae9d61Sreyk smsg.sadb_msg_type = action;
28545ae9d61Sreyk smsg.sadb_msg_satype = satype;
28645ae9d61Sreyk
28745ae9d61Sreyk bzero(&sa_flowtype, sizeof(sa_flowtype));
28845ae9d61Sreyk sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
28945ae9d61Sreyk sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
29045ae9d61Sreyk sa_flowtype.sadb_protocol_direction = flow->flow_dir;
2910d71d9e6Stobhe sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE;
29245ae9d61Sreyk
29345ae9d61Sreyk bzero(&sa_protocol, sizeof(sa_protocol));
29445ae9d61Sreyk sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
29545ae9d61Sreyk sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
29645ae9d61Sreyk sa_protocol.sadb_protocol_direction = 0;
29745ae9d61Sreyk sa_protocol.sadb_protocol_proto = flow->flow_ipproto;
29845ae9d61Sreyk
29945ae9d61Sreyk bzero(&sa_src, sizeof(sa_src));
30045ae9d61Sreyk sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
30145ae9d61Sreyk sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
30245ae9d61Sreyk
30345ae9d61Sreyk bzero(&sa_smask, sizeof(sa_smask));
30445ae9d61Sreyk sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
30545ae9d61Sreyk sa_smask.sadb_address_len =
30645ae9d61Sreyk (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
30745ae9d61Sreyk
30845ae9d61Sreyk bzero(&sa_dst, sizeof(sa_dst));
30945ae9d61Sreyk sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
31045ae9d61Sreyk sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
31145ae9d61Sreyk
31245ae9d61Sreyk bzero(&sa_dmask, sizeof(sa_dmask));
31345ae9d61Sreyk sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
31445ae9d61Sreyk sa_dmask.sadb_address_len =
31545ae9d61Sreyk (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
31645ae9d61Sreyk
3175342a326Sreyk if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
31845ae9d61Sreyk /* local address */
31945ae9d61Sreyk bzero(&sa_local, sizeof(sa_local));
32045ae9d61Sreyk sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
32145ae9d61Sreyk sa_local.sadb_address_len =
32245ae9d61Sreyk (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
32345ae9d61Sreyk
32445ae9d61Sreyk /* peer address */
32545ae9d61Sreyk bzero(&sa_peer, sizeof(sa_peer));
32645ae9d61Sreyk sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
32745ae9d61Sreyk sa_peer.sadb_address_len =
32845ae9d61Sreyk (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
32945ae9d61Sreyk
33045ae9d61Sreyk /* local id */
331834f9deeSmarkus sa_srcid = pfkey_id2ident(IKESA_SRCID(flow->flow_ikesa),
33245ae9d61Sreyk SADB_EXT_IDENTITY_SRC);
33345ae9d61Sreyk
33445ae9d61Sreyk /* peer id */
335834f9deeSmarkus sa_dstid = pfkey_id2ident(IKESA_DSTID(flow->flow_ikesa),
33645ae9d61Sreyk SADB_EXT_IDENTITY_DST);
33745ae9d61Sreyk }
33845ae9d61Sreyk
3390347364bStobhe if (flow->flow_rdomain >= 0) {
3400347364bStobhe /* install flow in specific rdomain */
3410347364bStobhe bzero(&sa_rdomain, sizeof(sa_rdomain));
3420347364bStobhe sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
3430347364bStobhe sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
3440347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = flow->flow_rdomain;
3450347364bStobhe }
3460347364bStobhe
347eab256a9Stobhe #define PAD(len) \
348eab256a9Stobhe padlen = ROUNDUP((len)) - (len); \
349eab256a9Stobhe if (padlen) { \
350eab256a9Stobhe iov[iov_cnt].iov_base = &pad; \
351eab256a9Stobhe iov[iov_cnt].iov_len = padlen; \
352eab256a9Stobhe iov_cnt++; \
353eab256a9Stobhe }
354eab256a9Stobhe
35545ae9d61Sreyk iov_cnt = 0;
35645ae9d61Sreyk
35745ae9d61Sreyk /* header */
35845ae9d61Sreyk iov[iov_cnt].iov_base = &smsg;
35945ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(smsg);
36045ae9d61Sreyk iov_cnt++;
36145ae9d61Sreyk
36245ae9d61Sreyk /* add flow type */
36345ae9d61Sreyk iov[iov_cnt].iov_base = &sa_flowtype;
36445ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_flowtype);
36545ae9d61Sreyk smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
36645ae9d61Sreyk iov_cnt++;
36745ae9d61Sreyk
3685342a326Sreyk if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
36945ae9d61Sreyk /* remote peer */
37045ae9d61Sreyk iov[iov_cnt].iov_base = &sa_peer;
37145ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_peer);
37245ae9d61Sreyk iov_cnt++;
37345ae9d61Sreyk iov[iov_cnt].iov_base = &speer;
374eab256a9Stobhe iov[iov_cnt].iov_len = speer.ss_len;
37545ae9d61Sreyk smsg.sadb_msg_len += sa_peer.sadb_address_len;
37645ae9d61Sreyk iov_cnt++;
377eab256a9Stobhe PAD(speer.ss_len);
37845ae9d61Sreyk }
37945ae9d61Sreyk
38045ae9d61Sreyk /* src addr */
38145ae9d61Sreyk iov[iov_cnt].iov_base = &sa_src;
38245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_src);
38345ae9d61Sreyk iov_cnt++;
38445ae9d61Sreyk iov[iov_cnt].iov_base = &ssrc;
385eab256a9Stobhe iov[iov_cnt].iov_len = ssrc.ss_len;
38645ae9d61Sreyk smsg.sadb_msg_len += sa_src.sadb_address_len;
38745ae9d61Sreyk iov_cnt++;
388eab256a9Stobhe PAD(ssrc.ss_len);
38945ae9d61Sreyk
39045ae9d61Sreyk /* src mask */
39145ae9d61Sreyk iov[iov_cnt].iov_base = &sa_smask;
39245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_smask);
39345ae9d61Sreyk iov_cnt++;
39445ae9d61Sreyk iov[iov_cnt].iov_base = &smask;
395eab256a9Stobhe iov[iov_cnt].iov_len = smask.ss_len;
39645ae9d61Sreyk smsg.sadb_msg_len += sa_smask.sadb_address_len;
39745ae9d61Sreyk iov_cnt++;
398eab256a9Stobhe PAD(smask.ss_len);
39945ae9d61Sreyk
40045ae9d61Sreyk /* dest addr */
40145ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dst;
40245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dst);
40345ae9d61Sreyk iov_cnt++;
40445ae9d61Sreyk iov[iov_cnt].iov_base = &sdst;
405eab256a9Stobhe iov[iov_cnt].iov_len = sdst.ss_len;
40645ae9d61Sreyk smsg.sadb_msg_len += sa_dst.sadb_address_len;
40745ae9d61Sreyk iov_cnt++;
408eab256a9Stobhe PAD(sdst.ss_len);
40945ae9d61Sreyk
41045ae9d61Sreyk /* dst mask */
41145ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dmask;
41245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dmask);
41345ae9d61Sreyk iov_cnt++;
41445ae9d61Sreyk iov[iov_cnt].iov_base = &dmask;
415eab256a9Stobhe iov[iov_cnt].iov_len = dmask.ss_len;
41645ae9d61Sreyk smsg.sadb_msg_len += sa_dmask.sadb_address_len;
41745ae9d61Sreyk iov_cnt++;
418eab256a9Stobhe PAD(dmask.ss_len);
41945ae9d61Sreyk
42045ae9d61Sreyk /* add protocol */
42145ae9d61Sreyk iov[iov_cnt].iov_base = &sa_protocol;
42245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_protocol);
42345ae9d61Sreyk smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
42445ae9d61Sreyk iov_cnt++;
42545ae9d61Sreyk
42645ae9d61Sreyk if (sa_srcid) {
42745ae9d61Sreyk /* src identity */
42845ae9d61Sreyk iov[iov_cnt].iov_base = sa_srcid;
42945ae9d61Sreyk iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
43045ae9d61Sreyk smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
43145ae9d61Sreyk iov_cnt++;
43245ae9d61Sreyk }
43345ae9d61Sreyk if (sa_dstid) {
43445ae9d61Sreyk /* dst identity */
43545ae9d61Sreyk iov[iov_cnt].iov_base = sa_dstid;
43645ae9d61Sreyk iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
43745ae9d61Sreyk smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
43845ae9d61Sreyk iov_cnt++;
43945ae9d61Sreyk }
44045ae9d61Sreyk
4410347364bStobhe if (flow->flow_rdomain >= 0) {
4420347364bStobhe iov[iov_cnt].iov_base = &sa_rdomain;
4430347364bStobhe iov[iov_cnt].iov_len = sizeof(sa_rdomain);
4440347364bStobhe smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
4450347364bStobhe iov_cnt++;
4460347364bStobhe }
447eab256a9Stobhe #undef PAD
4480347364bStobhe
449e7c3d382Stobhe ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL);
45045ae9d61Sreyk
45145ae9d61Sreyk free(sa_srcid);
45245ae9d61Sreyk free(sa_dstid);
45345ae9d61Sreyk
45445ae9d61Sreyk return (ret);
45545ae9d61Sreyk }
45645ae9d61Sreyk
45745ae9d61Sreyk int
pfkey_sa(struct iked * env,uint8_t satype,uint8_t action,struct iked_childsa * sa)4588f6f6c19Stobhe pfkey_sa(struct iked *env, uint8_t satype, uint8_t action, struct iked_childsa *sa)
45945ae9d61Sreyk {
4601c18b693Stobhe char iface[IF_NAMESIZE];
46145ae9d61Sreyk struct sadb_msg smsg;
46245ae9d61Sreyk struct sadb_sa sadb;
463c0b327e6Spatrick struct sadb_address sa_src, sa_dst, sa_pxy;
46445ae9d61Sreyk struct sadb_key sa_authkey, sa_enckey;
465b0eeedd0Smikeb struct sadb_lifetime sa_ltime_hard, sa_ltime_soft;
46645ae9d61Sreyk struct sadb_x_udpencap udpencap;
46745ae9d61Sreyk struct sadb_x_tag sa_tag;
4681c18b693Stobhe struct sadb_x_iface sa_iface;
4690d1109d5Sreyk char *tag = NULL;
470dd7958f8Sreyk struct sadb_x_tap sa_tap;
4710347364bStobhe struct sadb_x_rdomain sa_rdomain;
472c0b327e6Spatrick struct sockaddr_storage ssrc, sdst, spxy;
47345ae9d61Sreyk struct sadb_ident *sa_srcid, *sa_dstid;
474b0eeedd0Smikeb struct iked_lifetime *lt;
475dd7958f8Sreyk struct iked_policy *pol;
476c0b327e6Spatrick struct iked_addr *dst;
47745ae9d61Sreyk struct iovec iov[IOV_CNT];
4781c18b693Stobhe const char *errstr = NULL;
4791c18b693Stobhe uint32_t ifminor;
480d09d3a7dSreyk uint32_t jitter;
4814292c78bSreyk int iov_cnt;
482489f3a3dSmpi int ret, dotap = 0;
483eab256a9Stobhe uint64_t pad = 0;
484eab256a9Stobhe size_t padlen;
48545ae9d61Sreyk
48645ae9d61Sreyk sa_srcid = sa_dstid = NULL;
48745ae9d61Sreyk
488dd7958f8Sreyk if (sa->csa_ikesa == NULL || sa->csa_ikesa->sa_policy == NULL) {
489dd7958f8Sreyk log_warn("%s: invalid SA and policy", __func__);
490dd7958f8Sreyk return (-1);
491dd7958f8Sreyk }
492dd7958f8Sreyk pol = sa->csa_ikesa->sa_policy;
493b0eeedd0Smikeb lt = &pol->pol_lifetime;
494dd7958f8Sreyk
49545ae9d61Sreyk bzero(&ssrc, sizeof(ssrc));
49645ae9d61Sreyk memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
49745ae9d61Sreyk if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
49845ae9d61Sreyk log_warn("%s: invalid address", __func__);
49945ae9d61Sreyk return (-1);
50045ae9d61Sreyk }
50145ae9d61Sreyk
502c0b327e6Spatrick dst = (action == IKED_SADB_UPDATE_SA_ADDRESSES &&
503c0b327e6Spatrick sa->csa_dir == IPSP_DIRECTION_OUT) ?
504c0b327e6Spatrick &sa->csa_ikesa->sa_peer_loaded :
505c0b327e6Spatrick sa->csa_peer;
50645ae9d61Sreyk bzero(&sdst, sizeof(sdst));
507c0b327e6Spatrick memcpy(&sdst, &dst->addr, sizeof(sdst));
50845ae9d61Sreyk if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
50945ae9d61Sreyk log_warn("%s: invalid address", __func__);
51045ae9d61Sreyk return (-1);
51145ae9d61Sreyk }
51245ae9d61Sreyk
513c0b327e6Spatrick bzero(&spxy, sizeof(spxy));
514c0b327e6Spatrick if (dst != sa->csa_peer) {
515c0b327e6Spatrick memcpy(&spxy, &sa->csa_peer->addr, sizeof(spxy));
516c0b327e6Spatrick if (socket_af((struct sockaddr *)&spxy, 0) == -1) {
517c0b327e6Spatrick log_warn("%s: invalid address", __func__);
518c0b327e6Spatrick return (-1);
519c0b327e6Spatrick }
520c0b327e6Spatrick }
521c0b327e6Spatrick
52245ae9d61Sreyk bzero(&smsg, sizeof(smsg));
52345ae9d61Sreyk smsg.sadb_msg_version = PF_KEY_V2;
524b0eeedd0Smikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
52545ae9d61Sreyk smsg.sadb_msg_pid = getpid();
52645ae9d61Sreyk smsg.sadb_msg_len = sizeof(smsg) / 8;
52745ae9d61Sreyk smsg.sadb_msg_type = action;
52845ae9d61Sreyk smsg.sadb_msg_satype = satype;
52945ae9d61Sreyk
53045ae9d61Sreyk bzero(&sadb, sizeof(sadb));
53145ae9d61Sreyk sadb.sadb_sa_len = sizeof(sadb) / 8;
53245ae9d61Sreyk sadb.sadb_sa_exttype = SADB_EXT_SA;
53345ae9d61Sreyk sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
53445ae9d61Sreyk sadb.sadb_sa_state = SADB_SASTATE_MATURE;
535d7243a54Smikeb sadb.sadb_sa_replay = 64;
53645ae9d61Sreyk
53703f6ad09Smarkus if (!sa->csa_transport)
53845ae9d61Sreyk sadb.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
53945ae9d61Sreyk
540d7243a54Smikeb if (sa->csa_esn)
541d7243a54Smikeb sadb.sadb_sa_flags |= SADB_X_SAFLAGS_ESN;
542d7243a54Smikeb
54345ae9d61Sreyk bzero(&sa_src, sizeof(sa_src));
54445ae9d61Sreyk sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
54545ae9d61Sreyk sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
54645ae9d61Sreyk
54745ae9d61Sreyk bzero(&sa_dst, sizeof(sa_dst));
54845ae9d61Sreyk sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
54945ae9d61Sreyk sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
55045ae9d61Sreyk
551c0b327e6Spatrick bzero(&sa_pxy, sizeof(sa_pxy));
552c0b327e6Spatrick sa_pxy.sadb_address_len = (sizeof(sa_pxy) + ROUNDUP(spxy.ss_len)) / 8;
553c0b327e6Spatrick sa_pxy.sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
554c0b327e6Spatrick
55545ae9d61Sreyk bzero(&sa_authkey, sizeof(sa_authkey));
55645ae9d61Sreyk bzero(&sa_enckey, sizeof(sa_enckey));
55745ae9d61Sreyk bzero(&udpencap, sizeof udpencap);
558b0eeedd0Smikeb bzero(&sa_ltime_hard, sizeof(sa_ltime_hard));
559b0eeedd0Smikeb bzero(&sa_ltime_soft, sizeof(sa_ltime_soft));
5601c18b693Stobhe bzero(&sa_iface, sizeof(sa_iface));
56145ae9d61Sreyk
5620347364bStobhe if (pol->pol_rdomain >= 0) {
5630347364bStobhe bzero(&sa_rdomain, sizeof(sa_rdomain));
5640347364bStobhe sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
5650347364bStobhe sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
5660347364bStobhe if (satype == SADB_X_SATYPE_IPCOMP) {
5670347364bStobhe /* IPCOMP SAs are always in the pol_rdomain */
5680347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
5690347364bStobhe sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
5700347364bStobhe } else if (sa->csa_dir == IPSP_DIRECTION_OUT) {
5710347364bStobhe /* switch rdomain on encrypt/decrypt */
5720347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
5730347364bStobhe sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
5740347364bStobhe } else {
5750347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = iked_rdomain;
5760347364bStobhe sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
5770347364bStobhe }
5780347364bStobhe }
5790347364bStobhe
58045ae9d61Sreyk if (action == SADB_DELETE)
58145ae9d61Sreyk goto send;
58245ae9d61Sreyk
583e3b1b16bSreyk if (satype == SADB_SATYPE_ESP &&
584e3b1b16bSreyk sa->csa_ikesa->sa_udpencap && sa->csa_ikesa->sa_natt) {
585e3b1b16bSreyk sadb.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
586e3b1b16bSreyk udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
587e3b1b16bSreyk udpencap.sadb_x_udpencap_len = sizeof(udpencap) / 8;
588e3b1b16bSreyk udpencap.sadb_x_udpencap_port =
589e3b1b16bSreyk sa->csa_ikesa->sa_peer.addr_port;
590e3b1b16bSreyk
591e3b1b16bSreyk log_debug("%s: udpencap port %d", __func__,
592e3b1b16bSreyk ntohs(udpencap.sadb_x_udpencap_port));
593e3b1b16bSreyk }
594e3b1b16bSreyk
595c0b327e6Spatrick if (action == IKED_SADB_UPDATE_SA_ADDRESSES) {
596c0b327e6Spatrick smsg.sadb_msg_type = SADB_UPDATE;
597c0b327e6Spatrick goto send;
598c0b327e6Spatrick }
599c0b327e6Spatrick
600b0eeedd0Smikeb if ((action == SADB_ADD || action == SADB_UPDATE) &&
601b0eeedd0Smikeb !sa->csa_persistent && (lt->lt_bytes || lt->lt_seconds)) {
602b0eeedd0Smikeb sa_ltime_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
603b0eeedd0Smikeb sa_ltime_hard.sadb_lifetime_len = sizeof(sa_ltime_hard) / 8;
604b0eeedd0Smikeb sa_ltime_hard.sadb_lifetime_bytes = lt->lt_bytes;
605b0eeedd0Smikeb sa_ltime_hard.sadb_lifetime_addtime = lt->lt_seconds;
606b0eeedd0Smikeb
6075b3564b9Sreyk /* double the lifetime for ipcomp; disable byte lifetime */
6081a394ec9Spatrick if (satype == SADB_X_SATYPE_IPCOMP) {
60965f3034eSmarkus sa_ltime_hard.sadb_lifetime_addtime *= 2;
6101a394ec9Spatrick sa_ltime_hard.sadb_lifetime_bytes = 0;
6111a394ec9Spatrick }
61265f3034eSmarkus
613b0eeedd0Smikeb sa_ltime_soft.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
614b0eeedd0Smikeb sa_ltime_soft.sadb_lifetime_len = sizeof(sa_ltime_soft) / 8;
615b0eeedd0Smikeb /* set randomly to 85-95% */
616b0eeedd0Smikeb jitter = 850 + arc4random_uniform(100);
617b0eeedd0Smikeb sa_ltime_soft.sadb_lifetime_bytes =
61865f3034eSmarkus (sa_ltime_hard.sadb_lifetime_bytes * jitter) / 1000;
619b0eeedd0Smikeb sa_ltime_soft.sadb_lifetime_addtime =
62065f3034eSmarkus (sa_ltime_hard.sadb_lifetime_addtime * jitter) / 1000;
621b0eeedd0Smikeb }
622b0eeedd0Smikeb
62345ae9d61Sreyk /* XXX handle NULL encryption or NULL auth or combined encr/auth */
62445ae9d61Sreyk if (action == SADB_ADD &&
62545ae9d61Sreyk !ibuf_length(sa->csa_integrkey) && !ibuf_length(sa->csa_encrkey) &&
62645ae9d61Sreyk satype != SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) {
62745ae9d61Sreyk log_warnx("%s: no key specified", __func__);
62845ae9d61Sreyk return (-1);
62945ae9d61Sreyk }
63045ae9d61Sreyk
63137ce25f4Smarkus if (sa->csa_integrid)
63245ae9d61Sreyk if (pfkey_map(pfkey_integr,
63337ce25f4Smarkus sa->csa_integrid, &sadb.sadb_sa_auth) == -1) {
63445ae9d61Sreyk log_warnx("%s: unsupported integrity algorithm %s",
63537ce25f4Smarkus __func__, print_map(sa->csa_integrid,
63645ae9d61Sreyk ikev2_xformauth_map));
63745ae9d61Sreyk return (-1);
63845ae9d61Sreyk }
63945ae9d61Sreyk
64037ce25f4Smarkus if (sa->csa_encrid)
64145ae9d61Sreyk if (pfkey_map(pfkey_encr,
64237ce25f4Smarkus sa->csa_encrid, &sadb.sadb_sa_encrypt) == -1) {
64345ae9d61Sreyk log_warnx("%s: unsupported encryption algorithm %s",
64437ce25f4Smarkus __func__, print_map(sa->csa_encrid,
64545ae9d61Sreyk ikev2_xformencr_map));
64645ae9d61Sreyk return (-1);
64745ae9d61Sreyk }
64845ae9d61Sreyk
64945ae9d61Sreyk if (ibuf_length(sa->csa_integrkey)) {
65045ae9d61Sreyk sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
651eab256a9Stobhe ROUNDUP(ibuf_size(sa->csa_integrkey))) / 8;
65245ae9d61Sreyk sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
65345ae9d61Sreyk sa_authkey.sadb_key_bits =
65445ae9d61Sreyk 8 * ibuf_size(sa->csa_integrkey);
65545ae9d61Sreyk }
65645ae9d61Sreyk
65745ae9d61Sreyk if (ibuf_length(sa->csa_encrkey)) {
65845ae9d61Sreyk sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
659eab256a9Stobhe ROUNDUP(ibuf_size(sa->csa_encrkey))) / 8;
66045ae9d61Sreyk sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
66145ae9d61Sreyk sa_enckey.sadb_key_bits =
66245ae9d61Sreyk 8 * ibuf_size(sa->csa_encrkey);
66345ae9d61Sreyk }
66445ae9d61Sreyk
66503f6ad09Smarkus /* we only support deflate */
66603f6ad09Smarkus if (satype == SADB_X_SATYPE_IPCOMP)
66703f6ad09Smarkus sadb.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
66803f6ad09Smarkus
669834f9deeSmarkus /* Note that we need to swap the IDs for incoming SAs (SADB_UPDATE) */
670834f9deeSmarkus if (action != SADB_UPDATE) {
671834f9deeSmarkus sa_srcid = pfkey_id2ident(
672834f9deeSmarkus IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC);
673834f9deeSmarkus sa_dstid = pfkey_id2ident(
674834f9deeSmarkus IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST);
675834f9deeSmarkus } else {
676834f9deeSmarkus sa_srcid = pfkey_id2ident(
677834f9deeSmarkus IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC);
678834f9deeSmarkus sa_dstid = pfkey_id2ident(
679834f9deeSmarkus IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST);
680834f9deeSmarkus }
68145ae9d61Sreyk
68245ae9d61Sreyk tag = sa->csa_ikesa->sa_tag;
68345ae9d61Sreyk if (tag != NULL && *tag != '\0') {
68445ae9d61Sreyk bzero(&sa_tag, sizeof(sa_tag));
68545ae9d61Sreyk sa_tag.sadb_x_tag_exttype = SADB_X_EXT_TAG;
68645ae9d61Sreyk sa_tag.sadb_x_tag_len =
68745ae9d61Sreyk (ROUNDUP(strlen(tag) + 1) + sizeof(sa_tag)) / 8;
68845ae9d61Sreyk sa_tag.sadb_x_tag_taglen = strlen(tag) + 1;
68945ae9d61Sreyk } else
69045ae9d61Sreyk tag = NULL;
69145ae9d61Sreyk
692dd7958f8Sreyk if (pol->pol_tap != 0) {
693489f3a3dSmpi dotap = 1;
694dd7958f8Sreyk bzero(&sa_tap, sizeof(sa_tap));
695dd7958f8Sreyk sa_tap.sadb_x_tap_exttype = SADB_X_EXT_TAP;
696dd7958f8Sreyk sa_tap.sadb_x_tap_len = sizeof(sa_tap) / 8;
697dd7958f8Sreyk sa_tap.sadb_x_tap_unit = pol->pol_tap;
698dd7958f8Sreyk }
699dd7958f8Sreyk
7001c18b693Stobhe if (pol->pol_flags & IKED_POLICY_ROUTING) {
7011c18b693Stobhe sa_iface.sadb_x_iface_exttype = SADB_X_EXT_IFACE;
7021c18b693Stobhe sa_iface.sadb_x_iface_len = sizeof(sa_iface) / 8;
7033cd1d0b5Stobhe if (if_indextoname(pol->pol_iface, iface) == 0) {
7043cd1d0b5Stobhe log_warn("%s: unsupported interface %d",
7053cd1d0b5Stobhe __func__, pol->pol_iface);
7061c18b693Stobhe return (-1);
7071c18b693Stobhe }
7081c18b693Stobhe ifminor = strtonum(iface + strlen("sec"), 0, UINT_MAX, &errstr);
7091c18b693Stobhe if (errstr != NULL) {
7101c18b693Stobhe log_warnx("%s: unsupported interface %s",
7111c18b693Stobhe __func__, iface);
7121c18b693Stobhe return (-1);
7131c18b693Stobhe }
7141c18b693Stobhe sa_iface.sadb_x_iface_unit = ifminor;
7151c18b693Stobhe sa_iface.sadb_x_iface_direction = sa->csa_dir;
7161c18b693Stobhe }
7171c18b693Stobhe
71845ae9d61Sreyk send:
719eab256a9Stobhe
720eab256a9Stobhe #define PAD(len) \
721eab256a9Stobhe padlen = ROUNDUP((len)) - (len); \
722eab256a9Stobhe if (padlen) { \
723eab256a9Stobhe iov[iov_cnt].iov_base = &pad; \
724eab256a9Stobhe iov[iov_cnt].iov_len = padlen; \
725eab256a9Stobhe iov_cnt++; \
726eab256a9Stobhe }
727eab256a9Stobhe
72845ae9d61Sreyk iov_cnt = 0;
72945ae9d61Sreyk
73045ae9d61Sreyk /* header */
73145ae9d61Sreyk iov[iov_cnt].iov_base = &smsg;
73245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(smsg);
73345ae9d61Sreyk iov_cnt++;
73445ae9d61Sreyk
73545ae9d61Sreyk /* sa */
73645ae9d61Sreyk iov[iov_cnt].iov_base = &sadb;
73745ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sadb);
73845ae9d61Sreyk smsg.sadb_msg_len += sadb.sadb_sa_len;
73945ae9d61Sreyk iov_cnt++;
74045ae9d61Sreyk
74145ae9d61Sreyk /* src addr */
74245ae9d61Sreyk iov[iov_cnt].iov_base = &sa_src;
74345ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_src);
74445ae9d61Sreyk iov_cnt++;
74545ae9d61Sreyk iov[iov_cnt].iov_base = &ssrc;
746eab256a9Stobhe iov[iov_cnt].iov_len = ssrc.ss_len;
74745ae9d61Sreyk smsg.sadb_msg_len += sa_src.sadb_address_len;
74845ae9d61Sreyk iov_cnt++;
749eab256a9Stobhe PAD(ssrc.ss_len);
75045ae9d61Sreyk
75145ae9d61Sreyk /* dst addr */
75245ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dst;
75345ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dst);
75445ae9d61Sreyk iov_cnt++;
75545ae9d61Sreyk iov[iov_cnt].iov_base = &sdst;
756eab256a9Stobhe iov[iov_cnt].iov_len = sdst.ss_len;
75745ae9d61Sreyk smsg.sadb_msg_len += sa_dst.sadb_address_len;
75845ae9d61Sreyk iov_cnt++;
759eab256a9Stobhe PAD(sdst.ss_len);
76045ae9d61Sreyk
761c0b327e6Spatrick if (spxy.ss_len) {
762c0b327e6Spatrick /* pxy addr */
763c0b327e6Spatrick iov[iov_cnt].iov_base = &sa_pxy;
764c0b327e6Spatrick iov[iov_cnt].iov_len = sizeof(sa_pxy);
765c0b327e6Spatrick iov_cnt++;
766c0b327e6Spatrick iov[iov_cnt].iov_base = &spxy;
767eab256a9Stobhe iov[iov_cnt].iov_len = spxy.ss_len;
768c0b327e6Spatrick smsg.sadb_msg_len += sa_pxy.sadb_address_len;
769c0b327e6Spatrick iov_cnt++;
770eab256a9Stobhe PAD(spxy.ss_len);
771c0b327e6Spatrick }
772c0b327e6Spatrick
773b0eeedd0Smikeb if (sa_ltime_soft.sadb_lifetime_len) {
774b0eeedd0Smikeb /* soft lifetime */
775b0eeedd0Smikeb iov[iov_cnt].iov_base = &sa_ltime_soft;
776b0eeedd0Smikeb iov[iov_cnt].iov_len = sizeof(sa_ltime_soft);
777b0eeedd0Smikeb smsg.sadb_msg_len += sa_ltime_soft.sadb_lifetime_len;
778b0eeedd0Smikeb iov_cnt++;
779b0eeedd0Smikeb }
780b0eeedd0Smikeb
781b0eeedd0Smikeb if (sa_ltime_hard.sadb_lifetime_len) {
782b0eeedd0Smikeb /* hard lifetime */
783b0eeedd0Smikeb iov[iov_cnt].iov_base = &sa_ltime_hard;
784b0eeedd0Smikeb iov[iov_cnt].iov_len = sizeof(sa_ltime_hard);
785b0eeedd0Smikeb smsg.sadb_msg_len += sa_ltime_hard.sadb_lifetime_len;
786b0eeedd0Smikeb iov_cnt++;
787b0eeedd0Smikeb }
788b0eeedd0Smikeb
78945ae9d61Sreyk if (udpencap.sadb_x_udpencap_len) {
79045ae9d61Sreyk iov[iov_cnt].iov_base = &udpencap;
79145ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(udpencap);
79245ae9d61Sreyk smsg.sadb_msg_len += udpencap.sadb_x_udpencap_len;
79345ae9d61Sreyk iov_cnt++;
79445ae9d61Sreyk }
79545ae9d61Sreyk
79645ae9d61Sreyk if (sa_enckey.sadb_key_len) {
79745ae9d61Sreyk /* encryption key */
79845ae9d61Sreyk iov[iov_cnt].iov_base = &sa_enckey;
79945ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_enckey);
80045ae9d61Sreyk iov_cnt++;
80145ae9d61Sreyk iov[iov_cnt].iov_base = ibuf_data(sa->csa_encrkey);
802eab256a9Stobhe iov[iov_cnt].iov_len = ibuf_size(sa->csa_encrkey);
80345ae9d61Sreyk smsg.sadb_msg_len += sa_enckey.sadb_key_len;
80445ae9d61Sreyk iov_cnt++;
805eab256a9Stobhe PAD(ibuf_size(sa->csa_encrkey));
80645ae9d61Sreyk }
80745ae9d61Sreyk if (sa_authkey.sadb_key_len) {
80845ae9d61Sreyk /* authentication key */
80945ae9d61Sreyk iov[iov_cnt].iov_base = &sa_authkey;
81045ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_authkey);
81145ae9d61Sreyk iov_cnt++;
81245ae9d61Sreyk iov[iov_cnt].iov_base = ibuf_data(sa->csa_integrkey);
813eab256a9Stobhe iov[iov_cnt].iov_len = ibuf_size(sa->csa_integrkey);
81445ae9d61Sreyk smsg.sadb_msg_len += sa_authkey.sadb_key_len;
81545ae9d61Sreyk iov_cnt++;
816eab256a9Stobhe PAD(ibuf_size(sa->csa_integrkey));
81745ae9d61Sreyk }
81845ae9d61Sreyk
81945ae9d61Sreyk if (sa_srcid) {
82045ae9d61Sreyk /* src identity */
82145ae9d61Sreyk iov[iov_cnt].iov_base = sa_srcid;
82245ae9d61Sreyk iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
82345ae9d61Sreyk smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
82445ae9d61Sreyk iov_cnt++;
82545ae9d61Sreyk }
82645ae9d61Sreyk if (sa_dstid) {
82745ae9d61Sreyk /* dst identity */
82845ae9d61Sreyk iov[iov_cnt].iov_base = sa_dstid;
82945ae9d61Sreyk iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
83045ae9d61Sreyk smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
83145ae9d61Sreyk iov_cnt++;
83245ae9d61Sreyk }
83345ae9d61Sreyk
83445ae9d61Sreyk if (tag != NULL) {
83545ae9d61Sreyk /* tag identity */
83645ae9d61Sreyk iov[iov_cnt].iov_base = &sa_tag;
83745ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_tag);
83845ae9d61Sreyk iov_cnt++;
83945ae9d61Sreyk iov[iov_cnt].iov_base = tag;
840eab256a9Stobhe iov[iov_cnt].iov_len = strlen(tag) + 1;
84145ae9d61Sreyk smsg.sadb_msg_len += sa_tag.sadb_x_tag_len;
84245ae9d61Sreyk iov_cnt++;
843eab256a9Stobhe PAD(strlen(tag) + 1);
84445ae9d61Sreyk }
84545ae9d61Sreyk
8461c18b693Stobhe if (sa_iface.sadb_x_iface_len) {
8471c18b693Stobhe iov[iov_cnt].iov_base = &sa_iface;
8481c18b693Stobhe iov[iov_cnt].iov_len = sa_iface.sadb_x_iface_len * 8;
8491c18b693Stobhe smsg.sadb_msg_len += sa_iface.sadb_x_iface_len;
8501c18b693Stobhe iov_cnt++;
8511c18b693Stobhe }
8521c18b693Stobhe
853489f3a3dSmpi if (dotap != 0) {
854dd7958f8Sreyk /* enc(4) device tap unit */
855dd7958f8Sreyk iov[iov_cnt].iov_base = &sa_tap;
856dd7958f8Sreyk iov[iov_cnt].iov_len = sizeof(sa_tap);
857dd7958f8Sreyk smsg.sadb_msg_len += sa_tap.sadb_x_tap_len;
858dd7958f8Sreyk iov_cnt++;
859dd7958f8Sreyk }
860dd7958f8Sreyk
8610347364bStobhe if (pol->pol_rdomain >= 0) {
8620347364bStobhe iov[iov_cnt].iov_base = &sa_rdomain;
8630347364bStobhe iov[iov_cnt].iov_len = sizeof(sa_rdomain);
8640347364bStobhe smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
8650347364bStobhe iov_cnt++;
8660347364bStobhe }
867eab256a9Stobhe #undef PAD
8680347364bStobhe
869e7c3d382Stobhe ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL);
870160b250fSreyk
871160b250fSreyk free(sa_srcid);
872160b250fSreyk free(sa_dstid);
873160b250fSreyk
874160b250fSreyk return ret;
87545ae9d61Sreyk }
87645ae9d61Sreyk
87745ae9d61Sreyk int
pfkey_sa_lookup(struct iked * env,struct iked_childsa * sa,uint64_t * last_used,struct iked_sastats * stats)878*f36db9c4Syasuoka pfkey_sa_lookup(struct iked *env, struct iked_childsa *sa, uint64_t *last_used,
879*f36db9c4Syasuoka struct iked_sastats *stats)
880131966b1Smarkus {
8810347364bStobhe struct iked_policy *pol = sa->csa_ikesa->sa_policy;
882131966b1Smarkus struct sadb_msg *msg, smsg;
883131966b1Smarkus struct sadb_address sa_src, sa_dst;
884131966b1Smarkus struct sadb_sa sadb;
8850347364bStobhe struct sadb_x_rdomain sa_rdomain;
886131966b1Smarkus struct sadb_lifetime *sa_life;
887*f36db9c4Syasuoka struct sadb_x_counter *sa_counter;
888131966b1Smarkus struct sockaddr_storage ssrc, sdst;
889131966b1Smarkus struct iovec iov[IOV_CNT];
890eab256a9Stobhe uint64_t pad = 0;
891eab256a9Stobhe size_t padlen;
892d09d3a7dSreyk uint8_t *data;
893131966b1Smarkus ssize_t n;
8940347364bStobhe int iov_cnt, ret = -1, rdomain;
895d09d3a7dSreyk uint8_t satype;
896131966b1Smarkus
8978389fa3fStobhe if (last_used)
898131966b1Smarkus *last_used = 0;
899131966b1Smarkus
900131966b1Smarkus if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
901131966b1Smarkus return (-1);
902131966b1Smarkus
903131966b1Smarkus bzero(&ssrc, sizeof(ssrc));
904131966b1Smarkus memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
905131966b1Smarkus if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
906131966b1Smarkus log_warn("%s: invalid address", __func__);
907131966b1Smarkus return (-1);
908131966b1Smarkus }
909131966b1Smarkus
910131966b1Smarkus bzero(&sdst, sizeof(sdst));
911131966b1Smarkus memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
912131966b1Smarkus if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
913131966b1Smarkus log_warn("%s: invalid address", __func__);
914131966b1Smarkus return (-1);
915131966b1Smarkus }
916131966b1Smarkus
917131966b1Smarkus bzero(&smsg, sizeof(smsg));
918131966b1Smarkus smsg.sadb_msg_version = PF_KEY_V2;
919131966b1Smarkus smsg.sadb_msg_seq = ++sadb_msg_seq;
920131966b1Smarkus smsg.sadb_msg_pid = getpid();
921131966b1Smarkus smsg.sadb_msg_len = sizeof(smsg) / 8;
922131966b1Smarkus smsg.sadb_msg_type = SADB_GET;
923131966b1Smarkus smsg.sadb_msg_satype = satype;
924131966b1Smarkus
925131966b1Smarkus bzero(&sadb, sizeof(sadb));
926131966b1Smarkus sadb.sadb_sa_len = sizeof(sadb) / 8;
927131966b1Smarkus sadb.sadb_sa_exttype = SADB_EXT_SA;
928131966b1Smarkus sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
929131966b1Smarkus sadb.sadb_sa_state = SADB_SASTATE_MATURE;
930131966b1Smarkus sadb.sadb_sa_replay = 64;
931131966b1Smarkus
9320347364bStobhe if (pol->pol_rdomain >= 0) {
9330347364bStobhe rdomain = (sa->csa_dir == IPSP_DIRECTION_IN) ?
9340347364bStobhe iked_rdomain : pol->pol_rdomain;
9350347364bStobhe bzero(&sa_rdomain, sizeof(sa_rdomain));
9360347364bStobhe sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
9370347364bStobhe sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
9380347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = rdomain;
9390347364bStobhe }
9400347364bStobhe
941131966b1Smarkus bzero(&sa_src, sizeof(sa_src));
942131966b1Smarkus sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
943131966b1Smarkus sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
944131966b1Smarkus
945131966b1Smarkus bzero(&sa_dst, sizeof(sa_dst));
946131966b1Smarkus sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
947131966b1Smarkus sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
948131966b1Smarkus
949131966b1Smarkus iov_cnt = 0;
950131966b1Smarkus
951eab256a9Stobhe #define PAD(len) \
952eab256a9Stobhe padlen = ROUNDUP((len)) - (len); \
953eab256a9Stobhe if (padlen) { \
954eab256a9Stobhe iov[iov_cnt].iov_base = &pad; \
955eab256a9Stobhe iov[iov_cnt].iov_len = padlen; \
956eab256a9Stobhe iov_cnt++; \
957eab256a9Stobhe }
958eab256a9Stobhe
959131966b1Smarkus /* header */
960131966b1Smarkus iov[iov_cnt].iov_base = &smsg;
961131966b1Smarkus iov[iov_cnt].iov_len = sizeof(smsg);
962131966b1Smarkus iov_cnt++;
963131966b1Smarkus
964131966b1Smarkus /* sa */
965131966b1Smarkus iov[iov_cnt].iov_base = &sadb;
966131966b1Smarkus iov[iov_cnt].iov_len = sizeof(sadb);
967131966b1Smarkus smsg.sadb_msg_len += sadb.sadb_sa_len;
968131966b1Smarkus iov_cnt++;
969131966b1Smarkus
970131966b1Smarkus /* src addr */
971131966b1Smarkus iov[iov_cnt].iov_base = &sa_src;
972131966b1Smarkus iov[iov_cnt].iov_len = sizeof(sa_src);
973131966b1Smarkus iov_cnt++;
974131966b1Smarkus iov[iov_cnt].iov_base = &ssrc;
975eab256a9Stobhe iov[iov_cnt].iov_len = ssrc.ss_len;
976131966b1Smarkus smsg.sadb_msg_len += sa_src.sadb_address_len;
977131966b1Smarkus iov_cnt++;
978eab256a9Stobhe PAD(ssrc.ss_len);
979131966b1Smarkus
980131966b1Smarkus /* dst addr */
981131966b1Smarkus iov[iov_cnt].iov_base = &sa_dst;
982131966b1Smarkus iov[iov_cnt].iov_len = sizeof(sa_dst);
983131966b1Smarkus iov_cnt++;
984131966b1Smarkus iov[iov_cnt].iov_base = &sdst;
985eab256a9Stobhe iov[iov_cnt].iov_len = sdst.ss_len;
986131966b1Smarkus smsg.sadb_msg_len += sa_dst.sadb_address_len;
987131966b1Smarkus iov_cnt++;
988eab256a9Stobhe PAD(sdst.ss_len);
989131966b1Smarkus
9900347364bStobhe if (pol->pol_rdomain >= 0) {
9910347364bStobhe iov[iov_cnt].iov_base = &sa_rdomain;
9920347364bStobhe iov[iov_cnt].iov_len = sizeof(sa_rdomain);
9930347364bStobhe smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
9940347364bStobhe iov_cnt++;
9950347364bStobhe }
9960347364bStobhe
997e7c3d382Stobhe if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0)
998131966b1Smarkus return (-1);
999131966b1Smarkus
1000131966b1Smarkus msg = (struct sadb_msg *)data;
1001131966b1Smarkus if (msg->sadb_msg_errno != 0) {
1002131966b1Smarkus errno = msg->sadb_msg_errno;
1003131966b1Smarkus ret = -1;
100433e4f7e3Stobhe if (errno == ESRCH)
100533e4f7e3Stobhe log_debug("%s: not found", __func__);
100633e4f7e3Stobhe else
1007131966b1Smarkus log_warn("%s: message", __func__);
1008131966b1Smarkus goto done;
1009131966b1Smarkus }
10108389fa3fStobhe if (last_used) {
10118389fa3fStobhe if ((sa_life = pfkey_find_ext(data, n,
10128389fa3fStobhe SADB_X_EXT_LIFETIME_LASTUSE)) == NULL) {
1013bb108424Smarkus /* has never been used */
1014131966b1Smarkus ret = -1;
1015131966b1Smarkus goto done;
1016131966b1Smarkus }
1017131966b1Smarkus *last_used = sa_life->sadb_lifetime_usetime;
1018131966b1Smarkus log_debug("%s: last_used %llu", __func__, *last_used);
10198389fa3fStobhe }
1020*f36db9c4Syasuoka if (stats) {
1021*f36db9c4Syasuoka if ((sa_counter = pfkey_find_ext(data, n,
1022*f36db9c4Syasuoka SADB_X_EXT_COUNTER)) == NULL) {
1023*f36db9c4Syasuoka /* has never been used */
1024*f36db9c4Syasuoka ret = -1;
1025*f36db9c4Syasuoka goto done;
1026*f36db9c4Syasuoka }
1027*f36db9c4Syasuoka stats->sas_ibytes = sa_counter->sadb_x_counter_ibytes;
1028*f36db9c4Syasuoka stats->sas_obytes = sa_counter->sadb_x_counter_obytes;
1029*f36db9c4Syasuoka stats->sas_ipackets = sa_counter->sadb_x_counter_ipackets;
1030*f36db9c4Syasuoka stats->sas_opackets = sa_counter->sadb_x_counter_opackets;
1031*f36db9c4Syasuoka stats->sas_idrops = sa_counter->sadb_x_counter_idrops;
1032*f36db9c4Syasuoka stats->sas_odrops = sa_counter->sadb_x_counter_odrops;
1033*f36db9c4Syasuoka }
1034131966b1Smarkus
1035eab256a9Stobhe #undef PAD
1036131966b1Smarkus done:
10371e82d711Sderaadt freezero(data, n);
1038131966b1Smarkus return (ret);
1039131966b1Smarkus }
1040131966b1Smarkus
1041131966b1Smarkus int
pfkey_sa_last_used(struct iked * env,struct iked_childsa * sa,uint64_t * last_used)10428f6f6c19Stobhe pfkey_sa_last_used(struct iked *env, struct iked_childsa *sa, uint64_t *last_used)
10438389fa3fStobhe {
1044*f36db9c4Syasuoka return pfkey_sa_lookup(env, sa, last_used, NULL);
10458389fa3fStobhe }
10468389fa3fStobhe
10478389fa3fStobhe int
pfkey_sa_check_exists(struct iked * env,struct iked_childsa * sa)10488f6f6c19Stobhe pfkey_sa_check_exists(struct iked *env, struct iked_childsa *sa)
10498389fa3fStobhe {
1050*f36db9c4Syasuoka return pfkey_sa_lookup(env, sa, NULL, NULL);
1051*f36db9c4Syasuoka }
1052*f36db9c4Syasuoka
1053*f36db9c4Syasuoka int
pfkey_sa_sastats(struct iked * env,struct iked_childsa * sa,struct iked_sastats * stats)1054*f36db9c4Syasuoka pfkey_sa_sastats(struct iked *env, struct iked_childsa *sa,
1055*f36db9c4Syasuoka struct iked_sastats *stats)
1056*f36db9c4Syasuoka {
1057*f36db9c4Syasuoka return pfkey_sa_lookup(env, sa, NULL, stats);
10588389fa3fStobhe }
10598389fa3fStobhe
10608389fa3fStobhe int
pfkey_sa_getspi(struct iked * env,uint8_t satype,struct iked_childsa * sa,uint32_t * spip)10618f6f6c19Stobhe pfkey_sa_getspi(struct iked *env, uint8_t satype, struct iked_childsa *sa,
1062d09d3a7dSreyk uint32_t *spip)
106345ae9d61Sreyk {
106445ae9d61Sreyk struct sadb_msg *msg, smsg;
106545ae9d61Sreyk struct sadb_address sa_src, sa_dst;
106645ae9d61Sreyk struct sadb_sa *sa_ext;
106745ae9d61Sreyk struct sadb_spirange sa_spirange;
106845ae9d61Sreyk struct sockaddr_storage ssrc, sdst;
106945ae9d61Sreyk struct iovec iov[IOV_CNT];
1070eab256a9Stobhe uint64_t pad = 0;
1071eab256a9Stobhe size_t padlen;
1072d09d3a7dSreyk uint8_t *data;
107345ae9d61Sreyk ssize_t n;
1074b0eeedd0Smikeb int iov_cnt, ret = -1;
107545ae9d61Sreyk
107645ae9d61Sreyk bzero(&ssrc, sizeof(ssrc));
107745ae9d61Sreyk memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
107845ae9d61Sreyk if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
107945ae9d61Sreyk log_warn("%s: invalid address", __func__);
108045ae9d61Sreyk return (-1);
108145ae9d61Sreyk }
108245ae9d61Sreyk
108345ae9d61Sreyk bzero(&sdst, sizeof(sdst));
108445ae9d61Sreyk memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
108545ae9d61Sreyk if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
108645ae9d61Sreyk log_warn("%s: invalid address", __func__);
108745ae9d61Sreyk return (-1);
108845ae9d61Sreyk }
108945ae9d61Sreyk
109045ae9d61Sreyk bzero(&smsg, sizeof(smsg));
109145ae9d61Sreyk smsg.sadb_msg_version = PF_KEY_V2;
1092b0eeedd0Smikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
109345ae9d61Sreyk smsg.sadb_msg_pid = getpid();
109445ae9d61Sreyk smsg.sadb_msg_len = sizeof(smsg) / 8;
109545ae9d61Sreyk smsg.sadb_msg_type = SADB_GETSPI;
109645ae9d61Sreyk smsg.sadb_msg_satype = satype;
109745ae9d61Sreyk
109845ae9d61Sreyk bzero(&sa_spirange, sizeof(sa_spirange));
109945ae9d61Sreyk sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
110045ae9d61Sreyk sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
110145ae9d61Sreyk sa_spirange.sadb_spirange_min = 0x100;
110203f6ad09Smarkus sa_spirange.sadb_spirange_max = (satype == SADB_X_SATYPE_IPCOMP) ?
110303f6ad09Smarkus (CPI_PRIVATE_MIN - 1) : 0xffffffff;
110445ae9d61Sreyk sa_spirange.sadb_spirange_reserved = 0;
110545ae9d61Sreyk
110645ae9d61Sreyk bzero(&sa_src, sizeof(sa_src));
110745ae9d61Sreyk sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
110845ae9d61Sreyk sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
110945ae9d61Sreyk
111045ae9d61Sreyk bzero(&sa_dst, sizeof(sa_dst));
111145ae9d61Sreyk sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
111245ae9d61Sreyk sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
111345ae9d61Sreyk
1114eab256a9Stobhe #define PAD(len) \
1115eab256a9Stobhe padlen = ROUNDUP((len)) - (len); \
1116eab256a9Stobhe if (padlen) { \
1117eab256a9Stobhe iov[iov_cnt].iov_base = &pad; \
1118eab256a9Stobhe iov[iov_cnt].iov_len = padlen; \
1119eab256a9Stobhe iov_cnt++; \
1120eab256a9Stobhe }
1121eab256a9Stobhe
112245ae9d61Sreyk iov_cnt = 0;
112345ae9d61Sreyk
112445ae9d61Sreyk /* header */
112545ae9d61Sreyk iov[iov_cnt].iov_base = &smsg;
112645ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(smsg);
112745ae9d61Sreyk iov_cnt++;
112845ae9d61Sreyk
112945ae9d61Sreyk /* SPI range */
113045ae9d61Sreyk iov[iov_cnt].iov_base = &sa_spirange;
113145ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_spirange);
113245ae9d61Sreyk smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
113345ae9d61Sreyk iov_cnt++;
113445ae9d61Sreyk
113545ae9d61Sreyk /* src addr */
113645ae9d61Sreyk iov[iov_cnt].iov_base = &sa_src;
113745ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_src);
113845ae9d61Sreyk iov_cnt++;
113945ae9d61Sreyk iov[iov_cnt].iov_base = &ssrc;
1140eab256a9Stobhe iov[iov_cnt].iov_len = ssrc.ss_len;
114145ae9d61Sreyk smsg.sadb_msg_len += sa_src.sadb_address_len;
114245ae9d61Sreyk iov_cnt++;
1143eab256a9Stobhe PAD(ssrc.ss_len);
114445ae9d61Sreyk
114545ae9d61Sreyk /* dst addr */
114645ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dst;
114745ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dst);
114845ae9d61Sreyk iov_cnt++;
114945ae9d61Sreyk iov[iov_cnt].iov_base = &sdst;
1150eab256a9Stobhe iov[iov_cnt].iov_len = sdst.ss_len;
115145ae9d61Sreyk smsg.sadb_msg_len += sa_dst.sadb_address_len;
115245ae9d61Sreyk iov_cnt++;
1153eab256a9Stobhe PAD(sdst.ss_len);
115445ae9d61Sreyk
115545ae9d61Sreyk *spip = 0;
115645ae9d61Sreyk
1157e7c3d382Stobhe if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0)
115845ae9d61Sreyk return (-1);
115945ae9d61Sreyk
116045ae9d61Sreyk msg = (struct sadb_msg *)data;
1161b0eeedd0Smikeb if (msg->sadb_msg_errno != 0) {
1162b0eeedd0Smikeb errno = msg->sadb_msg_errno;
1163b0eeedd0Smikeb log_warn("%s: message", __func__);
1164b0eeedd0Smikeb goto done;
1165b0eeedd0Smikeb }
1166b0eeedd0Smikeb if ((sa_ext = pfkey_find_ext(data, n, SADB_EXT_SA)) == NULL) {
1167fc01d564Stobhe log_debug("%s: erroneous reply", __func__);
1168b0eeedd0Smikeb goto done;
1169b0eeedd0Smikeb }
1170b0eeedd0Smikeb
117145ae9d61Sreyk *spip = ntohl(sa_ext->sadb_sa_spi);
117245ae9d61Sreyk log_debug("%s: spi 0x%08x", __func__, *spip);
117345ae9d61Sreyk
1174eab256a9Stobhe #undef PAD
1175eab256a9Stobhe
1176b0eeedd0Smikeb done:
11771e82d711Sderaadt freezero(data, n);
117845ae9d61Sreyk return (ret);
117945ae9d61Sreyk }
118045ae9d61Sreyk
118145ae9d61Sreyk int
pfkey_sagroup(struct iked * env,uint8_t satype1,uint8_t action,struct iked_childsa * sa1,struct iked_childsa * sa2)11828f6f6c19Stobhe pfkey_sagroup(struct iked *env, uint8_t satype1, uint8_t action,
118345ae9d61Sreyk struct iked_childsa *sa1, struct iked_childsa *sa2)
118445ae9d61Sreyk {
118545ae9d61Sreyk struct sadb_msg smsg;
118645ae9d61Sreyk struct sadb_sa sadb1, sadb2;
118745ae9d61Sreyk struct sadb_address sa_dst1, sa_dst2;
118845ae9d61Sreyk struct sockaddr_storage sdst1, sdst2;
118945ae9d61Sreyk struct sadb_protocol sa_proto;
11900347364bStobhe struct sadb_x_rdomain sa_rdomain;
11910347364bStobhe struct iked_policy *pol;
119245ae9d61Sreyk struct iovec iov[IOV_CNT];
1193eab256a9Stobhe uint64_t pad = 0;
1194eab256a9Stobhe size_t padlen;
11954292c78bSreyk int iov_cnt;
11960347364bStobhe int group_rdomain;
1197d09d3a7dSreyk uint8_t satype2;
119845ae9d61Sreyk
119945ae9d61Sreyk if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1)
120045ae9d61Sreyk return (-1);
120145ae9d61Sreyk
120245ae9d61Sreyk bzero(&sdst1, sizeof(sdst1));
120345ae9d61Sreyk memcpy(&sdst1, &sa1->csa_peer->addr, sizeof(sdst1));
120445ae9d61Sreyk if (socket_af((struct sockaddr *)&sdst1, 0) == -1) {
120545ae9d61Sreyk log_warnx("%s: unsupported address family %d",
120645ae9d61Sreyk __func__, sdst1.ss_family);
120745ae9d61Sreyk return (-1);
120845ae9d61Sreyk }
120945ae9d61Sreyk
121045ae9d61Sreyk bzero(&sdst2, sizeof(sdst2));
121145ae9d61Sreyk memcpy(&sdst2, &sa2->csa_peer->addr, sizeof(sdst2));
121245ae9d61Sreyk if (socket_af((struct sockaddr *)&sdst2, 0) == -1) {
121345ae9d61Sreyk log_warnx("%s: unsupported address family %d",
121445ae9d61Sreyk __func__, sdst2.ss_family);
121545ae9d61Sreyk return (-1);
121645ae9d61Sreyk }
121745ae9d61Sreyk
121845ae9d61Sreyk bzero(&smsg, sizeof(smsg));
121945ae9d61Sreyk smsg.sadb_msg_version = PF_KEY_V2;
1220b0eeedd0Smikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
122145ae9d61Sreyk smsg.sadb_msg_pid = getpid();
122245ae9d61Sreyk smsg.sadb_msg_len = sizeof(smsg) / 8;
122345ae9d61Sreyk smsg.sadb_msg_type = action;
122445ae9d61Sreyk smsg.sadb_msg_satype = satype1;
122545ae9d61Sreyk
122645ae9d61Sreyk bzero(&sadb1, sizeof(sadb1));
122745ae9d61Sreyk sadb1.sadb_sa_len = sizeof(sadb1) / 8;
122845ae9d61Sreyk sadb1.sadb_sa_exttype = SADB_EXT_SA;
122945ae9d61Sreyk sadb1.sadb_sa_spi = htonl(sa1->csa_spi.spi);
123045ae9d61Sreyk sadb1.sadb_sa_state = SADB_SASTATE_MATURE;
123145ae9d61Sreyk
123245ae9d61Sreyk bzero(&sadb2, sizeof(sadb2));
123345ae9d61Sreyk sadb2.sadb_sa_len = sizeof(sadb2) / 8;
123445ae9d61Sreyk sadb2.sadb_sa_exttype = SADB_X_EXT_SA2;
123545ae9d61Sreyk sadb2.sadb_sa_spi = htonl(sa2->csa_spi.spi);
123645ae9d61Sreyk sadb2.sadb_sa_state = SADB_SASTATE_MATURE;
12370347364bStobhe
12380347364bStobhe /* Incoming SA1 (IPCOMP) and SA2 (ESP) are in different/other rdomain */
12390347364bStobhe group_rdomain =
12400347364bStobhe (pol = sa1->csa_ikesa->sa_policy) != NULL &&
12410347364bStobhe pol->pol_rdomain >= 0 &&
12420347364bStobhe satype1 == SADB_X_SATYPE_IPCOMP &&
12430347364bStobhe satype2 == SADB_SATYPE_ESP;
12440347364bStobhe if (group_rdomain) {
12450347364bStobhe bzero(&sa_rdomain, sizeof(sa_rdomain));
12460347364bStobhe sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
12470347364bStobhe sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
12480347364bStobhe if (sa1->csa_dir == IPSP_DIRECTION_IN) {
12490347364bStobhe /* only ESP SA is iked's rdomain */
12500347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
12510347364bStobhe sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
12520347364bStobhe } else {
12530347364bStobhe /* both SAs are in pol_rdomain */
12540347364bStobhe sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
12550347364bStobhe sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
12560347364bStobhe }
12570347364bStobhe }
12580347364bStobhe
125945ae9d61Sreyk iov_cnt = 0;
126045ae9d61Sreyk
126145ae9d61Sreyk bzero(&sa_dst1, sizeof(sa_dst1));
126245ae9d61Sreyk sa_dst1.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
126345ae9d61Sreyk sa_dst1.sadb_address_len =
126445ae9d61Sreyk (sizeof(sa_dst1) + ROUNDUP(sdst1.ss_len)) / 8;
126545ae9d61Sreyk
126645ae9d61Sreyk bzero(&sa_dst2, sizeof(sa_dst2));
126745ae9d61Sreyk sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
126845ae9d61Sreyk sa_dst2.sadb_address_len =
126945ae9d61Sreyk (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
127045ae9d61Sreyk
127145ae9d61Sreyk bzero(&sa_proto, sizeof(sa_proto));
1272d44c51a1Sbluhm sa_proto.sadb_protocol_exttype = SADB_X_EXT_SATYPE2;
127345ae9d61Sreyk sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
127445ae9d61Sreyk sa_proto.sadb_protocol_direction = 0;
127545ae9d61Sreyk sa_proto.sadb_protocol_proto = satype2;
127645ae9d61Sreyk
1277eab256a9Stobhe #define PAD(len) \
1278eab256a9Stobhe padlen = ROUNDUP((len)) - (len); \
1279eab256a9Stobhe if (padlen) { \
1280eab256a9Stobhe iov[iov_cnt].iov_base = &pad; \
1281eab256a9Stobhe iov[iov_cnt].iov_len = padlen; \
1282eab256a9Stobhe iov_cnt++; \
1283eab256a9Stobhe }
1284eab256a9Stobhe
128545ae9d61Sreyk /* header */
128645ae9d61Sreyk iov[iov_cnt].iov_base = &smsg;
128745ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(smsg);
128845ae9d61Sreyk iov_cnt++;
128945ae9d61Sreyk
129045ae9d61Sreyk /* sa */
129145ae9d61Sreyk iov[iov_cnt].iov_base = &sadb1;
129245ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sadb1);
129345ae9d61Sreyk smsg.sadb_msg_len += sadb1.sadb_sa_len;
129445ae9d61Sreyk iov_cnt++;
129545ae9d61Sreyk
129645ae9d61Sreyk /* dst addr */
129745ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dst1;
129845ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dst1);
129945ae9d61Sreyk iov_cnt++;
130045ae9d61Sreyk iov[iov_cnt].iov_base = &sdst1;
1301eab256a9Stobhe iov[iov_cnt].iov_len = sdst1.ss_len;
130245ae9d61Sreyk smsg.sadb_msg_len += sa_dst1.sadb_address_len;
130345ae9d61Sreyk iov_cnt++;
1304eab256a9Stobhe PAD(sdst1.ss_len);
130545ae9d61Sreyk
130645ae9d61Sreyk /* second sa */
130745ae9d61Sreyk iov[iov_cnt].iov_base = &sadb2;
130845ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sadb2);
130945ae9d61Sreyk smsg.sadb_msg_len += sadb2.sadb_sa_len;
131045ae9d61Sreyk iov_cnt++;
131145ae9d61Sreyk
131245ae9d61Sreyk /* second dst addr */
131345ae9d61Sreyk iov[iov_cnt].iov_base = &sa_dst2;
131445ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_dst2);
131545ae9d61Sreyk iov_cnt++;
131645ae9d61Sreyk iov[iov_cnt].iov_base = &sdst2;
1317eab256a9Stobhe iov[iov_cnt].iov_len = sdst2.ss_len;
131845ae9d61Sreyk smsg.sadb_msg_len += sa_dst2.sadb_address_len;
131945ae9d61Sreyk iov_cnt++;
1320eab256a9Stobhe PAD(sdst2.ss_len);
132145ae9d61Sreyk
132245ae9d61Sreyk /* SA type */
132345ae9d61Sreyk iov[iov_cnt].iov_base = &sa_proto;
132445ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(sa_proto);
132545ae9d61Sreyk smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
132645ae9d61Sreyk iov_cnt++;
132745ae9d61Sreyk
13280347364bStobhe /* SA1 and SA2 are from different rdomains */
13290347364bStobhe if (group_rdomain) {
13300347364bStobhe iov[iov_cnt].iov_base = &sa_rdomain;
13310347364bStobhe iov[iov_cnt].iov_len = sizeof(sa_rdomain);
13320347364bStobhe smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
13330347364bStobhe iov_cnt++;
13340347364bStobhe }
13350347364bStobhe
1336eab256a9Stobhe #undef PAD
1337eab256a9Stobhe
1338e7c3d382Stobhe return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL));
13394292c78bSreyk }
13404292c78bSreyk
13414292c78bSreyk int
pfkey_write(struct iked * env,struct sadb_msg * smsg,struct iovec * iov,int iov_cnt,uint8_t ** datap,ssize_t * lenp)1342e7c3d382Stobhe pfkey_write(struct iked *env, struct sadb_msg *smsg, struct iovec *iov, int iov_cnt,
1343d09d3a7dSreyk uint8_t **datap, ssize_t *lenp)
13444292c78bSreyk {
13454292c78bSreyk ssize_t n, len = smsg->sadb_msg_len * 8;
1346e7c3d382Stobhe int ret = -1;
13474292c78bSreyk
1348fc20f985Sreyk if (sadb_decoupled) {
1349fc20f985Sreyk switch (smsg->sadb_msg_type) {
1350fc20f985Sreyk case SADB_GETSPI:
1351fc20f985Sreyk /* we need to get a new SPI from the kernel */
1352fc20f985Sreyk break;
1353fc20f985Sreyk default:
1354fc20f985Sreyk if (datap || lenp) {
1355fc20f985Sreyk log_warnx("%s: pfkey not coupled", __func__);
1356fc20f985Sreyk return (-1);
1357fc20f985Sreyk }
1358fc20f985Sreyk /* ignore request */
1359fc20f985Sreyk return (0);
1360fc20f985Sreyk }
1361fc20f985Sreyk }
1362fc20f985Sreyk
1363e7c3d382Stobhe /* Delete event to poll() in pfkey_reply() */
1364e7c3d382Stobhe event_del(&env->sc_pfkeyev);
1365e7c3d382Stobhe
1366e7c3d382Stobhe if ((n = writev(env->sc_pfkey, iov, iov_cnt)) == -1) {
13679ae8a051Stobhe log_warn("%s: writev failed: type %u len %zd",
13689ae8a051Stobhe __func__, smsg->sadb_msg_type, len);
1369e7c3d382Stobhe goto done;
137045ae9d61Sreyk } else if (n != len) {
13714292c78bSreyk log_warn("%s: short write", __func__);
1372e7c3d382Stobhe goto done;
137345ae9d61Sreyk }
137445ae9d61Sreyk
1375e7c3d382Stobhe ret = pfkey_reply(env->sc_pfkey, datap, lenp);
1376e7c3d382Stobhe done:
1377e7c3d382Stobhe event_add(&env->sc_pfkeyev, NULL);
1378e7c3d382Stobhe return (ret);
137945ae9d61Sreyk }
138045ae9d61Sreyk
13818389fa3fStobhe /* wait for pfkey response and returns 0 for ok, -1 for error, -2 for timeout */
138245ae9d61Sreyk int
pfkey_reply(int fd,uint8_t ** datap,ssize_t * lenp)13838f6f6c19Stobhe pfkey_reply(int fd, uint8_t **datap, ssize_t *lenp)
138445ae9d61Sreyk {
1385d1ebe3d0Smikeb struct pfkey_message *pm;
138645ae9d61Sreyk struct sadb_msg hdr;
138745ae9d61Sreyk ssize_t len;
1388d09d3a7dSreyk uint8_t *data;
1389f0de6e10Sderaadt struct pollfd pfd[1];
13903f3558dcSmarkus int n;
139145ae9d61Sreyk
13928f6f6c19Stobhe pfd[0].fd = fd;
1393f0de6e10Sderaadt pfd[0].events = POLLIN;
1394f0de6e10Sderaadt
1395b0eeedd0Smikeb for (;;) {
13963f3558dcSmarkus /*
13973f3558dcSmarkus * We should actually expect the reply to get lost
13983f3558dcSmarkus * as PF_KEY is an unreliable service per the specs.
13993f3558dcSmarkus * Currently we do this by setting a short timeout,
14003f3558dcSmarkus * and if it is not readable in that time, we fail
14013f3558dcSmarkus * the read.
14023f3558dcSmarkus */
1403f0de6e10Sderaadt n = poll(pfd, 1, PFKEY_REPLY_TIMEOUT / 1000);
14043f3558dcSmarkus if (n == -1) {
1405f0de6e10Sderaadt log_warn("%s: poll() failed", __func__);
14063f3558dcSmarkus return (-1);
14073f3558dcSmarkus }
14083f3558dcSmarkus if (n == 0) {
14093f3558dcSmarkus log_warnx("%s: no reply from PF_KEY", __func__);
14108389fa3fStobhe return (-2); /* retry */
14113f3558dcSmarkus }
14123f3558dcSmarkus
14138f6f6c19Stobhe if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
1414b0eeedd0Smikeb log_warn("%s: short recv", __func__);
14154292c78bSreyk return (-1);
141645ae9d61Sreyk }
1417b0eeedd0Smikeb
1418b0eeedd0Smikeb if (hdr.sadb_msg_version != PF_KEY_V2) {
1419b0eeedd0Smikeb log_warnx("%s: wrong pfkey version", __func__);
1420b0eeedd0Smikeb return (-1);
1421b0eeedd0Smikeb }
1422b0eeedd0Smikeb
1423ce03dfbeSdoug if ((data = reallocarray(NULL, hdr.sadb_msg_len,
1424ce03dfbeSdoug PFKEYV2_CHUNK)) == NULL) {
14254292c78bSreyk log_warn("%s: malloc", __func__);
14264292c78bSreyk return (-1);
14274292c78bSreyk }
1428ce03dfbeSdoug len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
1429ce03dfbeSdoug
14308f6f6c19Stobhe if (read(fd, data, len) != len) {
14314292c78bSreyk log_warnx("%s: short read", __func__);
143245ae9d61Sreyk free(data);
14334292c78bSreyk return (-1);
143445ae9d61Sreyk }
1435d1ebe3d0Smikeb
1436d1ebe3d0Smikeb /* XXX: Only one message can be outstanding. */
1437d1ebe3d0Smikeb if (hdr.sadb_msg_seq == sadb_msg_seq &&
1438d09d3a7dSreyk hdr.sadb_msg_pid == (uint32_t)getpid())
1439d1ebe3d0Smikeb break;
1440d1ebe3d0Smikeb
1441596c707eSmarkus /* ignore messages for other processes */
1442596c707eSmarkus if (hdr.sadb_msg_pid != 0 &&
1443d09d3a7dSreyk hdr.sadb_msg_pid != (uint32_t)getpid()) {
144474214e59Smarkus free(data);
1445596c707eSmarkus continue;
144674214e59Smarkus }
1447596c707eSmarkus
1448d1ebe3d0Smikeb /* not the reply, enqueue */
1449d1ebe3d0Smikeb if ((pm = malloc(sizeof(*pm))) == NULL) {
1450596c707eSmarkus log_warn("%s: malloc", __func__);
1451d1ebe3d0Smikeb free(data);
1452d1ebe3d0Smikeb return (-1);
1453d1ebe3d0Smikeb }
1454d1ebe3d0Smikeb pm->pm_data = data;
14551c9ff2fdSblambert pm->pm_length = len;
1456d1ebe3d0Smikeb SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry);
1457d1ebe3d0Smikeb evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
1458d1ebe3d0Smikeb }
1459d1ebe3d0Smikeb
146045ae9d61Sreyk if (datap) {
146145ae9d61Sreyk *datap = data;
146245ae9d61Sreyk if (lenp)
146345ae9d61Sreyk *lenp = len;
1464d1ebe3d0Smikeb } else
146545ae9d61Sreyk free(data);
1466d1ebe3d0Smikeb
146745ae9d61Sreyk if (datap == NULL && hdr.sadb_msg_errno != 0) {
146845ae9d61Sreyk errno = hdr.sadb_msg_errno;
146945ae9d61Sreyk if (errno != EEXIST) {
147033e4f7e3Stobhe if (errno == ESRCH)
147133e4f7e3Stobhe log_debug("%s: not found", __func__);
147233e4f7e3Stobhe else
14734292c78bSreyk log_warn("%s: message", __func__);
14744292c78bSreyk return (-1);
147545ae9d61Sreyk }
147645ae9d61Sreyk }
14774292c78bSreyk return (0);
147845ae9d61Sreyk }
147945ae9d61Sreyk
148045ae9d61Sreyk int
pfkey_flow_add(struct iked * env,struct iked_flow * flow)14818f6f6c19Stobhe pfkey_flow_add(struct iked *env, struct iked_flow *flow)
148245ae9d61Sreyk {
1483d09d3a7dSreyk uint8_t satype;
148445ae9d61Sreyk
148545ae9d61Sreyk if (flow->flow_loaded)
148645ae9d61Sreyk return (0);
148745ae9d61Sreyk
148845ae9d61Sreyk if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
148945ae9d61Sreyk return (-1);
149045ae9d61Sreyk
14918f6f6c19Stobhe if (pfkey_flow(env, satype, SADB_X_ADDFLOW, flow) == -1)
149245ae9d61Sreyk return (-1);
149345ae9d61Sreyk
149445ae9d61Sreyk flow->flow_loaded = 1;
14955342a326Sreyk
149645ae9d61Sreyk return (0);
149745ae9d61Sreyk }
149845ae9d61Sreyk
149945ae9d61Sreyk int
pfkey_flow_delete(struct iked * env,struct iked_flow * flow)15008f6f6c19Stobhe pfkey_flow_delete(struct iked *env, struct iked_flow *flow)
150145ae9d61Sreyk {
1502d09d3a7dSreyk uint8_t satype;
150345ae9d61Sreyk
150445ae9d61Sreyk if (!flow->flow_loaded)
150545ae9d61Sreyk return (0);
150645ae9d61Sreyk
150745ae9d61Sreyk if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
150845ae9d61Sreyk return (-1);
150945ae9d61Sreyk
15108f6f6c19Stobhe if (pfkey_flow(env, satype, SADB_X_DELFLOW, flow) == -1)
151145ae9d61Sreyk return (-1);
151245ae9d61Sreyk
151345ae9d61Sreyk flow->flow_loaded = 0;
15145342a326Sreyk
151545ae9d61Sreyk return (0);
151645ae9d61Sreyk }
151745ae9d61Sreyk
151845ae9d61Sreyk int
pfkey_sa_init(struct iked * env,struct iked_childsa * sa,uint32_t * spi)15198f6f6c19Stobhe pfkey_sa_init(struct iked *env, struct iked_childsa *sa, uint32_t *spi)
152045ae9d61Sreyk {
1521d09d3a7dSreyk uint8_t satype;
152245ae9d61Sreyk
152345ae9d61Sreyk if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
152445ae9d61Sreyk return (-1);
152545ae9d61Sreyk
15268f6f6c19Stobhe if (pfkey_sa_getspi(env, satype, sa, spi) == -1)
152745ae9d61Sreyk return (-1);
152845ae9d61Sreyk
152945ae9d61Sreyk log_debug("%s: new spi 0x%08x", __func__, *spi);
153045ae9d61Sreyk
153145ae9d61Sreyk return (0);
153245ae9d61Sreyk }
153345ae9d61Sreyk
153445ae9d61Sreyk int
pfkey_sa_add(struct iked * env,struct iked_childsa * sa,struct iked_childsa * last)15358f6f6c19Stobhe pfkey_sa_add(struct iked *env, struct iked_childsa *sa, struct iked_childsa *last)
153645ae9d61Sreyk {
1537d09d3a7dSreyk uint8_t satype;
1538d09d3a7dSreyk unsigned int cmd;
15398389fa3fStobhe int rval;
154045ae9d61Sreyk
154145ae9d61Sreyk if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
154245ae9d61Sreyk return (-1);
154345ae9d61Sreyk
1544d4614e98Sreyk if (sa->csa_allocated || sa->csa_loaded)
154545ae9d61Sreyk cmd = SADB_UPDATE;
154645ae9d61Sreyk else
154745ae9d61Sreyk cmd = SADB_ADD;
154845ae9d61Sreyk
154945ae9d61Sreyk log_debug("%s: %s spi %s", __func__, cmd == SADB_ADD ? "add": "update",
155045ae9d61Sreyk print_spi(sa->csa_spi.spi, 4));
155145ae9d61Sreyk
15528f6f6c19Stobhe rval = pfkey_sa(env, satype, cmd, sa);
15538389fa3fStobhe if (rval != 0) {
1554af52b2a8Svgross if (cmd == SADB_ADD) {
15558389fa3fStobhe if (rval == -2) {
15568389fa3fStobhe /* timeout: check for existence */
15578f6f6c19Stobhe if (pfkey_sa_check_exists(env, sa) == 0) {
15588389fa3fStobhe log_debug("%s: SA exists after timeout",
15598389fa3fStobhe __func__);
15608389fa3fStobhe goto loaded;
15618389fa3fStobhe }
15628389fa3fStobhe }
15638f6f6c19Stobhe (void)pfkey_sa_delete(env, sa);
156445ae9d61Sreyk return (-1);
156545ae9d61Sreyk }
1566af52b2a8Svgross if (sa->csa_allocated && !sa->csa_loaded && errno == ESRCH) {
1567af52b2a8Svgross /* Needed for recoupling local SAs */
1568af52b2a8Svgross log_debug("%s: SADB_UPDATE on local SA returned ESRCH,"
1569af52b2a8Svgross " trying SADB_ADD", __func__);
15708f6f6c19Stobhe if (pfkey_sa(env, satype, SADB_ADD, sa) == -1)
1571af52b2a8Svgross return (-1);
1572af52b2a8Svgross } else {
1573af52b2a8Svgross return (-1);
1574af52b2a8Svgross }
1575af52b2a8Svgross }
157645ae9d61Sreyk
15778389fa3fStobhe loaded:
1578e7fee6f8Stobhe if (last != NULL) {
15798f6f6c19Stobhe if (pfkey_sagroup(env, satype,
158045ae9d61Sreyk SADB_X_GRPSPIS, sa, last) == -1) {
15818f6f6c19Stobhe (void)pfkey_sa_delete(env, sa);
158245ae9d61Sreyk return (-1);
158345ae9d61Sreyk }
158445ae9d61Sreyk }
158545ae9d61Sreyk
158645ae9d61Sreyk sa->csa_loaded = 1;
158745ae9d61Sreyk return (0);
158845ae9d61Sreyk }
158945ae9d61Sreyk
159045ae9d61Sreyk int
pfkey_sa_update_addresses(struct iked * env,struct iked_childsa * sa)15918f6f6c19Stobhe pfkey_sa_update_addresses(struct iked *env, struct iked_childsa *sa)
1592c0b327e6Spatrick {
1593c0b327e6Spatrick uint8_t satype;
1594c0b327e6Spatrick
1595c0b327e6Spatrick if (!sa->csa_ikesa)
1596c0b327e6Spatrick return (-1);
1597c0b327e6Spatrick /* check if peer has changed */
1598c0b327e6Spatrick if (sa->csa_ikesa->sa_peer_loaded.addr.ss_family == AF_UNSPEC ||
1599c0b327e6Spatrick memcmp(&sa->csa_ikesa->sa_peer_loaded, &sa->csa_ikesa->sa_peer,
1600c0b327e6Spatrick sizeof(sa->csa_ikesa->sa_peer_loaded)) == 0)
1601c0b327e6Spatrick return (0);
1602c0b327e6Spatrick if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
1603c0b327e6Spatrick return (-1);
1604c0b327e6Spatrick log_debug("%s: spi %s", __func__, print_spi(sa->csa_spi.spi, 4));
16058f6f6c19Stobhe return pfkey_sa(env, satype, IKED_SADB_UPDATE_SA_ADDRESSES, sa);
1606c0b327e6Spatrick }
1607c0b327e6Spatrick
1608c0b327e6Spatrick int
pfkey_sa_delete(struct iked * env,struct iked_childsa * sa)16098f6f6c19Stobhe pfkey_sa_delete(struct iked *env, struct iked_childsa *sa)
161045ae9d61Sreyk {
1611d09d3a7dSreyk uint8_t satype;
1612*f36db9c4Syasuoka struct iked_sastats sas;
161345ae9d61Sreyk
161445ae9d61Sreyk if (!sa->csa_loaded || sa->csa_spi.spi == 0)
161545ae9d61Sreyk return (0);
161645ae9d61Sreyk
161745ae9d61Sreyk if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
161845ae9d61Sreyk return (-1);
161945ae9d61Sreyk
1620*f36db9c4Syasuoka /* preserve the statistics */
1621*f36db9c4Syasuoka memset(&sas, 0, sizeof(sas));
1622*f36db9c4Syasuoka pfkey_sa_sastats(env, sa, &sas);
1623*f36db9c4Syasuoka
16248f6f6c19Stobhe if (pfkey_sa(env, satype, SADB_DELETE, sa) == -1 &&
16258f6f6c19Stobhe pfkey_sa_check_exists(env, sa) == 0)
162645ae9d61Sreyk return (-1);
162745ae9d61Sreyk
162845ae9d61Sreyk sa->csa_loaded = 0;
1629*f36db9c4Syasuoka
1630*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_ipackets += sas.sas_ipackets;
1631*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_opackets += sas.sas_opackets;
1632*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_ibytes += sas.sas_ibytes;
1633*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_obytes += sas.sas_obytes;
1634*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_idrops += sas.sas_idrops;
1635*f36db9c4Syasuoka sa->csa_ikesa->sa_stats.sas_odrops += sas.sas_odrops;
1636*f36db9c4Syasuoka
163745ae9d61Sreyk return (0);
163845ae9d61Sreyk }
163945ae9d61Sreyk
164045ae9d61Sreyk int
pfkey_flush(struct iked * env)1641e7c3d382Stobhe pfkey_flush(struct iked *env)
164245ae9d61Sreyk {
164345ae9d61Sreyk struct sadb_msg smsg;
164445ae9d61Sreyk struct iovec iov[IOV_CNT];
16454292c78bSreyk int iov_cnt;
164645ae9d61Sreyk
164745ae9d61Sreyk bzero(&smsg, sizeof(smsg));
164845ae9d61Sreyk smsg.sadb_msg_version = PF_KEY_V2;
1649b0eeedd0Smikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
165045ae9d61Sreyk smsg.sadb_msg_pid = getpid();
165145ae9d61Sreyk smsg.sadb_msg_len = sizeof(smsg) / 8;
165245ae9d61Sreyk smsg.sadb_msg_type = SADB_FLUSH;
165345ae9d61Sreyk smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
165445ae9d61Sreyk
165545ae9d61Sreyk iov_cnt = 0;
165645ae9d61Sreyk
165745ae9d61Sreyk iov[iov_cnt].iov_base = &smsg;
165845ae9d61Sreyk iov[iov_cnt].iov_len = sizeof(smsg);
165945ae9d61Sreyk iov_cnt++;
166045ae9d61Sreyk
1661e7c3d382Stobhe return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL));
166245ae9d61Sreyk }
166345ae9d61Sreyk
166445ae9d61Sreyk struct sadb_ident *
pfkey_id2ident(struct iked_id * id,unsigned int exttype)1665d09d3a7dSreyk pfkey_id2ident(struct iked_id *id, unsigned int exttype)
166645ae9d61Sreyk {
166745ae9d61Sreyk char idstr[IKED_ID_SIZE];
1668d09d3a7dSreyk unsigned int type;
166945ae9d61Sreyk size_t len;
167045ae9d61Sreyk struct sadb_ident *sa_id;
167145ae9d61Sreyk
167245ae9d61Sreyk switch (id->id_type) {
167345ae9d61Sreyk case IKEV2_ID_FQDN:
167445ae9d61Sreyk type = SADB_IDENTTYPE_FQDN;
167545ae9d61Sreyk break;
1676435ab53fSreyk case IKEV2_ID_UFQDN:
167745ae9d61Sreyk type = SADB_IDENTTYPE_USERFQDN;
167845ae9d61Sreyk break;
1679435ab53fSreyk case IKEV2_ID_IPV4:
1680435ab53fSreyk case IKEV2_ID_IPV6:
168145ae9d61Sreyk type = SADB_IDENTTYPE_PREFIX;
168245ae9d61Sreyk break;
1683435ab53fSreyk case IKEV2_ID_ASN1_DN:
1684d8131d03Sphessler type = SADB_IDENTTYPE_ASN1_DN;
1685d8131d03Sphessler break;
1686435ab53fSreyk case IKEV2_ID_ASN1_GN:
168745ae9d61Sreyk case IKEV2_ID_KEY_ID:
168845ae9d61Sreyk case IKEV2_ID_NONE:
168945ae9d61Sreyk default:
169045ae9d61Sreyk /* XXX not implemented/supported by PFKEY */
169145ae9d61Sreyk return (NULL);
169245ae9d61Sreyk }
169345ae9d61Sreyk
169445ae9d61Sreyk bzero(&idstr, sizeof(idstr));
169545ae9d61Sreyk
169608246d98Sreyk if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1)
169745ae9d61Sreyk return (NULL);
169845ae9d61Sreyk
169945ae9d61Sreyk len = ROUNDUP(strlen(idstr) + 1) + sizeof(*sa_id);
170045ae9d61Sreyk if ((sa_id = calloc(1, len)) == NULL)
170145ae9d61Sreyk return (NULL);
170245ae9d61Sreyk
170345ae9d61Sreyk strlcpy((char *)(sa_id + 1), idstr, ROUNDUP(strlen(idstr) + 1));
170445ae9d61Sreyk sa_id->sadb_ident_type = type;
170545ae9d61Sreyk sa_id->sadb_ident_len = len / 8;
170645ae9d61Sreyk sa_id->sadb_ident_exttype = exttype;
170745ae9d61Sreyk
170845ae9d61Sreyk return (sa_id);
170945ae9d61Sreyk }
171045ae9d61Sreyk
171145ae9d61Sreyk int
pfkey_socket(struct iked * env)17128f6f6c19Stobhe pfkey_socket(struct iked *env)
17136417b90fSreyk {
17146417b90fSreyk int fd;
17156417b90fSreyk
1716f2f2a684Sreyk if (privsep_process != PROC_PARENT)
17176417b90fSreyk fatal("pfkey_socket: called from unprivileged process");
17186417b90fSreyk
17196417b90fSreyk if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
17206417b90fSreyk fatal("pfkey_socket: failed to open PF_KEY socket");
17216417b90fSreyk
17226417b90fSreyk return (fd);
17236417b90fSreyk }
17246417b90fSreyk
17256417b90fSreyk void
pfkey_init(struct iked * env,int fd)17266417b90fSreyk pfkey_init(struct iked *env, int fd)
172745ae9d61Sreyk {
17280927abadSmikeb struct sadb_msg smsg;
17290927abadSmikeb struct iovec iov;
173045ae9d61Sreyk
17310347364bStobhe iked_rdomain = getrtable();
17320347364bStobhe
17330aa0bcefSmarkus /* Set up a timer to process messages deferred by the pfkey_reply */
17340aa0bcefSmarkus pfkey_timer_tv.tv_sec = 1;
17350aa0bcefSmarkus pfkey_timer_tv.tv_usec = 0;
17360aa0bcefSmarkus evtimer_set(&pfkey_timer_ev, pfkey_timer_cb, env);
17370aa0bcefSmarkus
17386417b90fSreyk /* Register the pfkey socket event handler */
17396417b90fSreyk env->sc_pfkey = fd;
17406417b90fSreyk event_set(&env->sc_pfkeyev, env->sc_pfkey,
17416417b90fSreyk EV_READ|EV_PERSIST, pfkey_dispatch, env);
17426417b90fSreyk event_add(&env->sc_pfkeyev, NULL);
174345ae9d61Sreyk
1744e7c3d382Stobhe pfkey_flush(env);
1745e7c3d382Stobhe
17460927abadSmikeb /* Register it to get ESP and AH acquires from the kernel */
17470927abadSmikeb bzero(&smsg, sizeof(smsg));
17480927abadSmikeb smsg.sadb_msg_version = PF_KEY_V2;
17490927abadSmikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
17500927abadSmikeb smsg.sadb_msg_pid = getpid();
17510927abadSmikeb smsg.sadb_msg_len = sizeof(smsg) / 8;
17520927abadSmikeb smsg.sadb_msg_type = SADB_REGISTER;
17530927abadSmikeb smsg.sadb_msg_satype = SADB_SATYPE_ESP;
17540927abadSmikeb
17550927abadSmikeb iov.iov_base = &smsg;
17560927abadSmikeb iov.iov_len = sizeof(smsg);
17570927abadSmikeb
1758e7c3d382Stobhe if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL))
17590927abadSmikeb fatal("pfkey_init: failed to set up ESP acquires");
17600927abadSmikeb
17610927abadSmikeb bzero(&smsg, sizeof(smsg));
17620927abadSmikeb smsg.sadb_msg_version = PF_KEY_V2;
17630927abadSmikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
17640927abadSmikeb smsg.sadb_msg_pid = getpid();
17650927abadSmikeb smsg.sadb_msg_len = sizeof(smsg) / 8;
17660927abadSmikeb smsg.sadb_msg_type = SADB_REGISTER;
17670927abadSmikeb smsg.sadb_msg_satype = SADB_SATYPE_AH;
17680927abadSmikeb
17690927abadSmikeb iov.iov_base = &smsg;
17700927abadSmikeb iov.iov_len = sizeof(smsg);
17710927abadSmikeb
1772e7c3d382Stobhe if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL))
17730927abadSmikeb fatal("pfkey_init: failed to set up AH acquires");
177445ae9d61Sreyk }
1775b0eeedd0Smikeb
1776b0eeedd0Smikeb void *
pfkey_find_ext(uint8_t * data,ssize_t len,int type)1777d09d3a7dSreyk pfkey_find_ext(uint8_t *data, ssize_t len, int type)
1778b0eeedd0Smikeb {
1779b0eeedd0Smikeb struct sadb_ext *ext = (struct sadb_ext *)(data +
1780b0eeedd0Smikeb sizeof(struct sadb_msg));
1781b0eeedd0Smikeb
1782d09d3a7dSreyk while (ext && ((uint8_t *)ext - data < len)) {
1783b0eeedd0Smikeb if (ext->sadb_ext_type == type)
1784b0eeedd0Smikeb return (ext);
1785d09d3a7dSreyk ext = (struct sadb_ext *)((uint8_t *)ext +
1786b0eeedd0Smikeb ext->sadb_ext_len * PFKEYV2_CHUNK);
1787b0eeedd0Smikeb }
1788b0eeedd0Smikeb
1789b0eeedd0Smikeb return (NULL);
1790b0eeedd0Smikeb }
1791b0eeedd0Smikeb
1792b0eeedd0Smikeb void
pfkey_dispatch(int fd,short event,void * arg)17938f6f6c19Stobhe pfkey_dispatch(int fd, short event, void *arg)
1794b0eeedd0Smikeb {
1795b0eeedd0Smikeb struct iked *env = (struct iked *)arg;
17966e1880a3Smarkus struct pfkey_message pm, *pmp;
1797b0eeedd0Smikeb struct sadb_msg hdr;
1798b0eeedd0Smikeb ssize_t len;
1799d09d3a7dSreyk uint8_t *data;
1800b0eeedd0Smikeb
18018f6f6c19Stobhe if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
1802b0eeedd0Smikeb log_warn("%s: short recv", __func__);
1803b0eeedd0Smikeb return;
1804b0eeedd0Smikeb }
1805b0eeedd0Smikeb
1806b0eeedd0Smikeb if (hdr.sadb_msg_version != PF_KEY_V2) {
1807b0eeedd0Smikeb log_warnx("%s: wrong pfkey version", __func__);
1808b0eeedd0Smikeb return;
1809b0eeedd0Smikeb }
1810b0eeedd0Smikeb
1811ce03dfbeSdoug if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEYV2_CHUNK))
1812ce03dfbeSdoug == NULL) {
1813b0eeedd0Smikeb log_warn("%s: malloc", __func__);
1814b0eeedd0Smikeb return;
1815b0eeedd0Smikeb }
1816ce03dfbeSdoug len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
1817ce03dfbeSdoug
18188f6f6c19Stobhe if (read(fd, data, len) != len) {
1819b0eeedd0Smikeb log_warn("%s: short read", __func__);
1820b0eeedd0Smikeb free(data);
1821b0eeedd0Smikeb return;
1822b0eeedd0Smikeb }
1823b0eeedd0Smikeb
182490ef7001Smarkus /* Try postponed requests first, so we do in-order processing */
182590ef7001Smarkus if (!SIMPLEQ_EMPTY(&pfkey_postponed))
182690ef7001Smarkus pfkey_timer_cb(0, 0, env);
182790ef7001Smarkus
1828d1ebe3d0Smikeb pm.pm_data = data;
18291c9ff2fdSblambert pm.pm_length = len;
1830d1ebe3d0Smikeb
18316e1880a3Smarkus if (pfkey_process(env, &pm) == -1 &&
18326e1880a3Smarkus (pmp = calloc(1, sizeof(*pmp))) != NULL) {
18336e1880a3Smarkus pmp->pm_data = data;
18346e1880a3Smarkus pmp->pm_length = len;
18356e1880a3Smarkus log_debug("%s: pfkey_process is busy, retry later", __func__);
18366e1880a3Smarkus SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pmp, pm_entry);
18376e1880a3Smarkus evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
18386e1880a3Smarkus } else {
1839d1ebe3d0Smikeb free(data);
1840d1ebe3d0Smikeb }
18416e1880a3Smarkus }
1842d1ebe3d0Smikeb
1843d1ebe3d0Smikeb void
pfkey_timer_cb(int unused,short event,void * arg)1844d1ebe3d0Smikeb pfkey_timer_cb(int unused, short event, void *arg)
1845d1ebe3d0Smikeb {
1846d1ebe3d0Smikeb struct iked *env = arg;
1847d1ebe3d0Smikeb struct pfkey_message *pm;
1848d1ebe3d0Smikeb
18496e1880a3Smarkus SIMPLEQ_INIT(&pfkey_retry);
1850d1ebe3d0Smikeb while (!SIMPLEQ_EMPTY(&pfkey_postponed)) {
1851d1ebe3d0Smikeb pm = SIMPLEQ_FIRST(&pfkey_postponed);
1852d1ebe3d0Smikeb SIMPLEQ_REMOVE_HEAD(&pfkey_postponed, pm_entry);
18536e1880a3Smarkus if (pfkey_process(env, pm) == -1) {
18546e1880a3Smarkus log_debug("%s: pfkey_process is busy, retry later",
18556e1880a3Smarkus __func__);
18566e1880a3Smarkus SIMPLEQ_INSERT_TAIL(&pfkey_retry, pm, pm_entry);
18576e1880a3Smarkus } else {
1858d1ebe3d0Smikeb free(pm->pm_data);
1859d1ebe3d0Smikeb free(pm);
1860d1ebe3d0Smikeb }
1861d1ebe3d0Smikeb }
186290ef7001Smarkus /* move from retry to postponed */
1863b434f93eSbket SIMPLEQ_CONCAT(&pfkey_postponed, &pfkey_retry);
18646e1880a3Smarkus if (!SIMPLEQ_EMPTY(&pfkey_postponed))
18656e1880a3Smarkus evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
18666e1880a3Smarkus }
1867d1ebe3d0Smikeb
18686e1880a3Smarkus /*
18696e1880a3Smarkus * pfkey_process returns 0 if the message has been processed and -1 if
187064405961Smmcc * the system is busy and the message should be passed again, later.
18716e1880a3Smarkus */
18726e1880a3Smarkus int
pfkey_process(struct iked * env,struct pfkey_message * pm)1873d1ebe3d0Smikeb pfkey_process(struct iked *env, struct pfkey_message *pm)
1874d1ebe3d0Smikeb {
1875d1ebe3d0Smikeb struct iked_spi spi;
1876d1ebe3d0Smikeb struct sadb_sa *sa;
18770927abadSmikeb struct sadb_lifetime *sa_ltime;
18780d1109d5Sreyk struct sadb_msg *hdr;
18790d1109d5Sreyk struct sadb_msg smsg;
18800d1109d5Sreyk struct iked_addr peer;
18810d1109d5Sreyk struct iked_flow flow;
18820d1109d5Sreyk struct sadb_address *sa_addr;
18830927abadSmikeb struct sadb_protocol *sa_proto;
18840927abadSmikeb struct sadb_x_policy sa_pol;
188547d6a31cSmarkus struct sockaddr *ssrc, *sdst, *smask, *dmask, *speer;
18860927abadSmikeb struct iovec iov[IOV_CNT];
18879e93d211Stobhe int ret = 0, iov_cnt;
1888d09d3a7dSreyk uint8_t *reply;
18890d1109d5Sreyk ssize_t rlen;
18900927abadSmikeb const char *errmsg = NULL;
1891d09d3a7dSreyk uint8_t *data = pm->pm_data;
18921c9ff2fdSblambert ssize_t len = pm->pm_length;
189347d6a31cSmarkus size_t slen;
1894d1ebe3d0Smikeb
1895d1ebe3d0Smikeb if (!env || !data || !len)
18966e1880a3Smarkus return (0);
1897d1ebe3d0Smikeb
1898d1ebe3d0Smikeb hdr = (struct sadb_msg *)data;
1899d1ebe3d0Smikeb
1900d1ebe3d0Smikeb switch (hdr->sadb_msg_type) {
19010927abadSmikeb case SADB_ACQUIRE:
19020927abadSmikeb bzero(&flow, sizeof(flow));
19030927abadSmikeb bzero(&peer, sizeof(peer));
19040927abadSmikeb
19050927abadSmikeb if ((sa_addr = pfkey_find_ext(data, len,
19060927abadSmikeb SADB_EXT_ADDRESS_DST)) == NULL) {
19070927abadSmikeb log_debug("%s: no peer address", __func__);
19086e1880a3Smarkus return (0);
1909b0eeedd0Smikeb }
191047d6a31cSmarkus speer = (struct sockaddr *)(sa_addr + 1);
191147d6a31cSmarkus peer.addr_af = speer->sa_family;
19120927abadSmikeb peer.addr_port = htons(socket_getport(speer));
191347d6a31cSmarkus if ((slen = speer->sa_len) > sizeof(peer.addr)) {
191447d6a31cSmarkus log_debug("%s: invalid peer address len", __func__);
19156e1880a3Smarkus return (0);
191647d6a31cSmarkus }
191747d6a31cSmarkus memcpy(&peer.addr, speer, slen);
19180927abadSmikeb if (socket_af((struct sockaddr *)&peer.addr,
19190927abadSmikeb peer.addr_port) == -1) {
19200927abadSmikeb log_debug("%s: invalid address", __func__);
19216e1880a3Smarkus return (0);
19220927abadSmikeb }
19230927abadSmikeb flow.flow_peer = &peer;
19240927abadSmikeb
19250927abadSmikeb log_debug("%s: acquire request (peer %s)", __func__,
192614e2a040Stb print_addr(speer));
19270927abadSmikeb
19280927abadSmikeb /* get the matching flow */
19290927abadSmikeb bzero(&smsg, sizeof(smsg));
19300927abadSmikeb smsg.sadb_msg_version = PF_KEY_V2;
19310927abadSmikeb smsg.sadb_msg_seq = ++sadb_msg_seq;
19320927abadSmikeb smsg.sadb_msg_pid = getpid();
19330927abadSmikeb smsg.sadb_msg_len = sizeof(smsg) / 8;
19340927abadSmikeb smsg.sadb_msg_type = SADB_X_ASKPOLICY;
19350927abadSmikeb
19360927abadSmikeb iov_cnt = 0;
19370927abadSmikeb
19380927abadSmikeb iov[iov_cnt].iov_base = &smsg;
19390927abadSmikeb iov[iov_cnt].iov_len = sizeof(smsg);
19400927abadSmikeb iov_cnt++;
19410927abadSmikeb
19420927abadSmikeb bzero(&sa_pol, sizeof(sa_pol));
19430927abadSmikeb sa_pol.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
19440927abadSmikeb sa_pol.sadb_x_policy_len = sizeof(sa_pol) / 8;
19450927abadSmikeb sa_pol.sadb_x_policy_seq = hdr->sadb_msg_seq;
19460927abadSmikeb
19470927abadSmikeb iov[iov_cnt].iov_base = &sa_pol;
19480927abadSmikeb iov[iov_cnt].iov_len = sizeof(sa_pol);
19490927abadSmikeb smsg.sadb_msg_len += sizeof(sa_pol) / 8;
19500927abadSmikeb iov_cnt++;
19510927abadSmikeb
1952e7c3d382Stobhe if (pfkey_write(env, &smsg, iov, iov_cnt, &reply, &rlen)) {
19530927abadSmikeb log_warnx("%s: failed to get a policy", __func__);
19546e1880a3Smarkus return (0);
19550927abadSmikeb }
19560927abadSmikeb
19570927abadSmikeb if ((sa_addr = pfkey_find_ext(reply, rlen,
19580927abadSmikeb SADB_X_EXT_SRC_FLOW)) == NULL) {
19590927abadSmikeb errmsg = "flow source address";
19600927abadSmikeb goto out;
19610927abadSmikeb }
196247d6a31cSmarkus ssrc = (struct sockaddr *)(sa_addr + 1);
196347d6a31cSmarkus flow.flow_src.addr_af = ssrc->sa_family;
19640927abadSmikeb flow.flow_src.addr_port = htons(socket_getport(ssrc));
196547d6a31cSmarkus if ((slen = ssrc->sa_len) > sizeof(flow.flow_src.addr)) {
196647d6a31cSmarkus log_debug("%s: invalid src address len", __func__);
19671c738e03Stobhe goto out;
196847d6a31cSmarkus }
196947d6a31cSmarkus memcpy(&flow.flow_src.addr, ssrc, slen);
19700927abadSmikeb if (socket_af((struct sockaddr *)&flow.flow_src.addr,
19710927abadSmikeb flow.flow_src.addr_port) == -1) {
19720927abadSmikeb log_debug("%s: invalid address", __func__);
19731c738e03Stobhe goto out;
19740927abadSmikeb }
19750927abadSmikeb
19760927abadSmikeb if ((sa_addr = pfkey_find_ext(reply, rlen,
19770927abadSmikeb SADB_X_EXT_DST_FLOW)) == NULL) {
19780927abadSmikeb errmsg = "flow destination address";
19790927abadSmikeb goto out;
19800927abadSmikeb }
198147d6a31cSmarkus sdst = (struct sockaddr *)(sa_addr + 1);
198247d6a31cSmarkus flow.flow_dst.addr_af = sdst->sa_family;
19830927abadSmikeb flow.flow_dst.addr_port = htons(socket_getport(sdst));
198447d6a31cSmarkus if ((slen = sdst->sa_len) > sizeof(flow.flow_dst.addr)) {
198547d6a31cSmarkus log_debug("%s: invalid dst address len", __func__);
19861c738e03Stobhe goto out;
198747d6a31cSmarkus }
198847d6a31cSmarkus memcpy(&flow.flow_dst.addr, sdst, slen);
19890927abadSmikeb if (socket_af((struct sockaddr *)&flow.flow_dst.addr,
19900927abadSmikeb flow.flow_dst.addr_port) == -1) {
19910927abadSmikeb log_debug("%s: invalid address", __func__);
19921c738e03Stobhe goto out;
19930927abadSmikeb }
19940927abadSmikeb
19950927abadSmikeb if ((sa_addr = pfkey_find_ext(reply, rlen,
19960927abadSmikeb SADB_X_EXT_SRC_MASK)) == NULL) {
19970927abadSmikeb errmsg = "flow source mask";
19980927abadSmikeb goto out;
19990927abadSmikeb }
200047d6a31cSmarkus smask = (struct sockaddr *)(sa_addr + 1);
200147d6a31cSmarkus switch (smask->sa_family) {
20020927abadSmikeb case AF_INET:
20030927abadSmikeb flow.flow_src.addr_mask =
20040927abadSmikeb mask2prefixlen((struct sockaddr *)smask);
20050927abadSmikeb if (flow.flow_src.addr_mask != 32)
20060927abadSmikeb flow.flow_src.addr_net = 1;
20070927abadSmikeb break;
20080927abadSmikeb case AF_INET6:
20090927abadSmikeb flow.flow_src.addr_mask =
20100927abadSmikeb mask2prefixlen6((struct sockaddr *)smask);
20110927abadSmikeb if (flow.flow_src.addr_mask != 128)
20120927abadSmikeb flow.flow_src.addr_net = 1;
20130927abadSmikeb break;
20140927abadSmikeb default:
20150927abadSmikeb log_debug("%s: bad address family", __func__);
20161c738e03Stobhe goto out;
20170927abadSmikeb }
20180927abadSmikeb
20190927abadSmikeb if ((sa_addr = pfkey_find_ext(reply, rlen,
20200927abadSmikeb SADB_X_EXT_DST_MASK)) == NULL) {
20210927abadSmikeb errmsg = "flow destination mask";
20220927abadSmikeb goto out;
20230927abadSmikeb }
202447d6a31cSmarkus dmask = (struct sockaddr *)(sa_addr + 1);
202547d6a31cSmarkus switch (dmask->sa_family) {
20260927abadSmikeb case AF_INET:
20270927abadSmikeb flow.flow_dst.addr_mask =
20280927abadSmikeb mask2prefixlen((struct sockaddr *)dmask);
20290927abadSmikeb if (flow.flow_src.addr_mask != 32)
20300927abadSmikeb flow.flow_src.addr_net = 1;
20310927abadSmikeb break;
20320927abadSmikeb case AF_INET6:
20330927abadSmikeb flow.flow_dst.addr_mask =
20340927abadSmikeb mask2prefixlen6((struct sockaddr *)dmask);
20350927abadSmikeb if (flow.flow_src.addr_mask != 128)
20360927abadSmikeb flow.flow_src.addr_net = 1;
20370927abadSmikeb break;
20380927abadSmikeb default:
20390927abadSmikeb log_debug("%s: bad address family", __func__);
20401c738e03Stobhe goto out;
20410927abadSmikeb }
20420927abadSmikeb
20436946103dStobhe switch (hdr->sadb_msg_satype) {
20446946103dStobhe case SADB_SATYPE_AH:
20456946103dStobhe flow.flow_saproto = IKEV2_SAPROTO_AH;
20466946103dStobhe break;
20476946103dStobhe case SADB_SATYPE_ESP:
20486946103dStobhe flow.flow_saproto = IKEV2_SAPROTO_ESP;
20496946103dStobhe break;
20506946103dStobhe case SADB_X_SATYPE_IPCOMP:
20516946103dStobhe flow.flow_saproto = IKEV2_SAPROTO_IPCOMP;
20526946103dStobhe break;
20536946103dStobhe }
20546946103dStobhe
20550927abadSmikeb if ((sa_proto = pfkey_find_ext(reply, rlen,
20560927abadSmikeb SADB_X_EXT_FLOW_TYPE)) == NULL) {
20570927abadSmikeb errmsg = "flow protocol";
20580927abadSmikeb goto out;
20590927abadSmikeb }
20600927abadSmikeb flow.flow_dir = sa_proto->sadb_protocol_direction;
20610347364bStobhe flow.flow_rdomain = -1; /* XXX get from kernel */
20620927abadSmikeb
20630927abadSmikeb log_debug("%s: flow %s from %s/%s to %s/%s via %s", __func__,
20640927abadSmikeb flow.flow_dir == IPSP_DIRECTION_IN ? "in" : "out",
206514e2a040Stb print_addr(ssrc), print_addr(smask),
206614e2a040Stb print_addr(sdst), print_addr(dmask),
206714e2a040Stb print_addr(speer));
20680927abadSmikeb
2069ff6390f7Stobhe ret = ikev2_child_sa_acquire(env, &flow);
20700927abadSmikeb
20710927abadSmikeb out:
20720927abadSmikeb if (errmsg)
20730927abadSmikeb log_warnx("%s: %s wasn't found", __func__, errmsg);
20740927abadSmikeb free(reply);
20750927abadSmikeb break;
20760927abadSmikeb
20770927abadSmikeb case SADB_EXPIRE:
20780927abadSmikeb if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) {
20790927abadSmikeb log_warnx("%s: SA extension wasn't found", __func__);
20806e1880a3Smarkus return (0);
20810927abadSmikeb }
20820927abadSmikeb if ((sa_ltime = pfkey_find_ext(data, len,
2083b0eeedd0Smikeb SADB_EXT_LIFETIME_SOFT)) == NULL &&
20840927abadSmikeb (sa_ltime = pfkey_find_ext(data, len,
2085b0eeedd0Smikeb SADB_EXT_LIFETIME_HARD)) == NULL) {
20860927abadSmikeb log_warnx("%s: lifetime extension wasn't found",
2087b0eeedd0Smikeb __func__);
20886e1880a3Smarkus return (0);
2089b0eeedd0Smikeb }
2090b0eeedd0Smikeb spi.spi = ntohl(sa->sadb_sa_spi);
2091b0eeedd0Smikeb spi.spi_size = 4;
2092d1ebe3d0Smikeb switch (hdr->sadb_msg_satype) {
2093b0eeedd0Smikeb case SADB_SATYPE_AH:
2094b0eeedd0Smikeb spi.spi_protoid = IKEV2_SAPROTO_AH;
2095b0eeedd0Smikeb break;
2096b0eeedd0Smikeb case SADB_SATYPE_ESP:
2097b0eeedd0Smikeb spi.spi_protoid = IKEV2_SAPROTO_ESP;
2098b0eeedd0Smikeb break;
209965f3034eSmarkus case SADB_X_SATYPE_IPCOMP:
210065f3034eSmarkus spi.spi_size = 2;
210165f3034eSmarkus spi.spi_protoid = IKEV2_SAPROTO_IPCOMP;
210265f3034eSmarkus break;
2103b0eeedd0Smikeb default:
210490ef7001Smarkus log_warnx("%s: unsupported SA type %d spi %s",
2105d1ebe3d0Smikeb __func__, hdr->sadb_msg_satype,
2106b0eeedd0Smikeb print_spi(spi.spi, spi.spi_size));
21076e1880a3Smarkus return (0);
2108b0eeedd0Smikeb }
2109b0eeedd0Smikeb
2110b0eeedd0Smikeb log_debug("%s: SA %s is expired, pending %s", __func__,
2111b0eeedd0Smikeb print_spi(spi.spi, spi.spi_size),
21120927abadSmikeb sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ?
2113b0eeedd0Smikeb "rekeying" : "deletion");
2114b0eeedd0Smikeb
21150927abadSmikeb if (sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT)
2116ff6390f7Stobhe ret = ikev2_child_sa_rekey(env, &spi);
2117b0eeedd0Smikeb else
2118ff6390f7Stobhe ret = ikev2_child_sa_drop(env, &spi);
2119b0eeedd0Smikeb break;
2120b0eeedd0Smikeb }
21216e1880a3Smarkus return (ret);
2122b0eeedd0Smikeb }
2123