From: tobhe Date: Sat, 13 Feb 2021 16:14:12 +0000 (+0000) Subject: Add dynamic address configuration for roadwarrior clients. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=264f8b22eb1a85595e95e1f72bb5931416faf5f5;p=openbsd Add dynamic address configuration for roadwarrior clients. The new 'iface' config option can be used to specify an interface for the virtual addresses received from the peer. Routes are automatically added based on the configured flows. Input from sthen@ and claudio@ ok patrick@ --- diff --git a/sbin/iked/Makefile b/sbin/iked/Makefile index 6b8e3013aec..387b74677ad 100644 --- a/sbin/iked/Makefile +++ b/sbin/iked/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.17 2017/07/19 12:50:32 espie Exp $ +# $OpenBSD: Makefile,v 1.18 2021/02/13 16:14:12 tobhe Exp $ PROG= iked SRCS= ca.c chap_ms.c config.c control.c crypto.c dh.c \ eap.c iked.c ikev2.c ikev2_msg.c ikev2_pld.c \ log.c ocsp.c pfkey.c policy.c proc.c timer.c util.c \ - imsg_util.c smult_curve25519_ref.c + imsg_util.c smult_curve25519_ref.c vroute.c SRCS+= eap_map.c ikev2_map.c SRCS+= parse.y MAN= iked.conf.5 iked.8 diff --git a/sbin/iked/config.c b/sbin/iked/config.c index 26c63f5855c..849916f3ec0 100644 --- a/sbin/iked/config.c +++ b/sbin/iked/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.76 2021/02/08 16:13:58 tobhe Exp $ */ +/* $OpenBSD: config.c,v 1.77 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -117,6 +117,7 @@ config_free_sa(struct iked *env, struct iked_sa *sa) config_free_fragments(&sa->sa_fragments); config_free_proposals(&sa->sa_proposals, 0); config_free_childsas(env, &sa->sa_childsas, NULL, NULL); + sa_configure_iface(env, sa, 0); sa_free_flows(env, &sa->sa_flows); if (sa->sa_addrpool) { diff --git a/sbin/iked/iked.c b/sbin/iked/iked.c index b4a13353974..5fe91b518dd 100644 --- a/sbin/iked/iked.c +++ b/sbin/iked/iked.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.c,v 1.53 2021/02/08 16:13:58 tobhe Exp $ */ +/* $OpenBSD: iked.c,v 1.54 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -199,6 +199,8 @@ main(int argc, char *argv[]) proc_listen(ps, procs, nitems(procs)); + vroute_init(env); + if (parent_configure(env) == -1) fatalx("configuration failed"); @@ -266,9 +268,10 @@ parent_configure(struct iked *env) * dns - for reload and ocsp connect. * inet - for ocsp connect. * route - for using interfaces in iked.conf (SIOCGIFGMEMB) + * wroute - for adding and removing addresses (SIOCAIFGMEMB) * sendfd - for ocsp sockets. */ - if (pledge("stdio rpath proc dns inet route sendfd", NULL) == -1) + if (pledge("stdio rpath proc dns inet route wroute sendfd", NULL) == -1) fatal("pledge"); config_setstatic(env); @@ -454,6 +457,14 @@ parent_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) struct iked *env = p->p_ps->ps_env; switch (imsg->hdr.type) { + case IMSG_IF_ADDADDR: + case IMSG_IF_DELADDR: + return (vroute_getaddr(env, imsg)); + case IMSG_VROUTE_ADD: + case IMSG_VROUTE_DEL: + return (vroute_getroute(env, imsg)); + case IMSG_VROUTE_CLONE: + return (vroute_getcloneroute(env, imsg)); case IMSG_CTL_EXIT: parent_shutdown(env); default: diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5 index 695f0efb618..529c6aaa37c 100644 --- a/sbin/iked/iked.conf.5 +++ b/sbin/iked/iked.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: iked.conf.5,v 1.83 2021/01/24 19:10:19 tobhe Exp $ +.\" $OpenBSD: iked.conf.5,v 1.84 2021/02/13 16:14:12 tobhe Exp $ .\" .\" Copyright (c) 2010 - 2014 Reyk Floeter .\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved. @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 24 2021 $ +.Dd $Mdocdate: February 13 2021 $ .Dt IKED.CONF 5 .Os .Sh NAME @@ -659,6 +659,9 @@ included. .It Ic access-server Ar address The address of an internal remote access server. .El +.It Ic iface Ar interface +Configure requested addresses and routes on the specified +.Ar interface . .It Ic tag Ar string Add a .Xr pf 4 diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h index e748ee6020e..45658af733d 100644 --- a/sbin/iked/iked.h +++ b/sbin/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.184 2021/02/04 20:38:26 tobhe Exp $ */ +/* $OpenBSD: iked.h,v 1.185 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -153,6 +153,7 @@ struct iked_flow { unsigned int flow_dir; /* in/out */ int flow_rdomain; struct iked_addr flow_prenat; + int flow_fixed; unsigned int flow_loaded; /* pfkey done */ @@ -236,6 +237,7 @@ struct iked_lifetime { struct iked_policy { unsigned int pol_id; char pol_name[IKED_ID_SIZE]; + unsigned int pol_iface; #define IKED_SKIP_FLAGS 0 #define IKED_SKIP_AF 1 @@ -751,6 +753,7 @@ struct iked { struct event sc_pfkeyev; uint8_t sc_certreqtype; struct ibuf *sc_certreq; + void *sc_vroute; struct iked_socket *sc_sock4[2]; struct iked_socket *sc_sock6[2]; @@ -864,6 +867,7 @@ struct iked_sa * struct iked_policy *); void sa_free(struct iked *, struct iked_sa *); void sa_free_flows(struct iked *, struct iked_saflows *); +int sa_configure_iface(struct iked *, struct iked_sa *, int); int sa_address(struct iked_sa *, struct iked_addr *, struct sockaddr *); void childsa_free(struct iked_childsa *); struct iked_childsa * @@ -937,6 +941,18 @@ int dsa_update(struct iked_dsa *, const void *, size_t); ssize_t dsa_sign_final(struct iked_dsa *, void *, size_t); ssize_t dsa_verify_final(struct iked_dsa *, void *, size_t); +/* vroute.c */ +void vroute_init(struct iked *); +int vroute_getaddr(struct iked *, struct imsg *); +int vroute_setaddroute(struct iked *, uint8_t, struct sockaddr *, + uint8_t, struct sockaddr *); +int vroute_setcloneroute(struct iked *, uint8_t, struct sockaddr *, + uint8_t, struct sockaddr *); +int vroute_setdelroute(struct iked *, uint8_t, struct sockaddr *, + uint8_t, struct sockaddr *); +int vroute_getroute(struct iked *, struct imsg *); +int vroute_getcloneroute(struct iked *, struct imsg *); + /* ikev2.c */ pid_t ikev2(struct privsep *, struct privsep_proc *); void ikev2_recv(struct iked *, struct iked_message *); diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c index 979d1295189..9a9bc781725 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.306 2021/02/11 22:02:41 tobhe Exp $ */ +/* $OpenBSD: ikev2.c,v 1.307 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -1616,6 +1616,7 @@ ikev2_init_done(struct iked *env, struct iked_sa *sa) ikev2_enable_timer(env, sa); ikev2_log_established(sa); ikev2_record_dstid(env, sa); + sa_configure_iface(env, sa, 1); } if (ret) diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y index e9a57c97667..60dd3f3ea03 100644 --- a/sbin/iked/parse.y +++ b/sbin/iked/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.127 2021/02/09 21:35:48 tobhe Exp $ */ +/* $OpenBSD: parse.y,v 1.128 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -408,7 +408,7 @@ int create_ike(char *, int, uint8_t, uint8_t, char *, char *, uint32_t, struct iked_lifetime *, struct iked_auth *, struct ipsec_filters *, - struct ipsec_addr_wrap *); + struct ipsec_addr_wrap *, char *); int create_user(const char *, const char *); int get_id_type(char *); uint8_t x2i(unsigned char *); @@ -471,7 +471,7 @@ typedef struct { %token STICKYADDRESS NOSTICKYADDRESS %token TOLERATE MAXAGE DYNAMIC %token CERTPARTIALCHAIN -%token REQUEST +%token REQUEST IFACE %token STRING %token NUMBER %type string @@ -494,7 +494,7 @@ typedef struct { %type ike_sas child_sas %type lifetime %type byte_spec time_spec ikelifetime -%type name +%type name iface %type cfg ikecfg ikecfgvals %type transform_esn %% @@ -584,10 +584,10 @@ user : USER STRING STRING { ikev2rule : IKEV2 name ikeflags satype af proto rdomain hosts_list peers ike_sas child_sas ids ikelifetime lifetime ikeauth ikecfg - filters { + iface filters { if (create_ike($2, $5, $6, $7, $8, &$9, $10, $11, $4, $3, $12.srcid, $12.dstid, $13, &$14, &$15, - $17, $16) == -1) { + $18, $16, $17) == -1) { yyerror("create_ike failed"); YYERROR; } @@ -1238,6 +1238,13 @@ filter : TAG STRING } ; +iface : { + $$ = NULL; + } + | IFACE STRING { + $$ = $2; + } + string : string STRING { if (asprintf(&$$, "%s %s", $1, $2) == -1) @@ -1363,6 +1370,7 @@ lookup(char *s) { "fragmentation", FRAGMENTATION }, { "from", FROM }, { "group", GROUP }, + { "iface", IFACE }, { "ike", IKEV1 }, { "ikelifetime", IKELIFETIME }, { "ikesa", IKESA }, @@ -2451,6 +2459,7 @@ print_policy(struct iked_policy *pol) struct iked_cfg *cfg; unsigned int i, j; const struct ipsec_xf *xfs = NULL; + char iface[IF_NAMESIZE]; print_verbose("ikev2"); @@ -2623,6 +2632,9 @@ print_policy(struct iked_policy *pol) if (pol->pol_tag[0] != '\0') print_verbose(" tag \"%s\"", pol->pol_tag); + if (pol->pol_iface != 0 && if_indextoname(pol->pol_iface, iface) != NULL) + print_verbose(" iface %s", iface); + if (pol->pol_tap != 0) print_verbose(" tap \"enc%u\"", pol->pol_tap); @@ -2677,7 +2689,7 @@ create_ike(char *name, int af, uint8_t ipproto, uint8_t flags, char *srcid, char *dstid, uint32_t ikelifetime, struct iked_lifetime *lt, struct iked_auth *authtype, struct ipsec_filters *filter, - struct ipsec_addr_wrap *ikecfg) + struct ipsec_addr_wrap *ikecfg, char *iface) { char idstr[IKED_ID_SIZE]; struct ipsec_addr_wrap *ipa, *ipb; @@ -2715,6 +2727,14 @@ create_ike(char *name, int af, uint8_t ipproto, "policy%d", policy_id); } + if (iface != NULL) { + pol.pol_iface = if_nametoindex(iface); + if (pol.pol_iface == 0) { + yyerror("invalid iface"); + return (-1); + } + } + if (srcid) { pol.pol_localid.id_type = get_id_type(srcid); pol.pol_localid.id_length = strlen(srcid); diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c index 82199824257..189dd822596 100644 --- a/sbin/iked/policy.c +++ b/sbin/iked/policy.c @@ -1,6 +1,7 @@ -/* $OpenBSD: policy.c,v 1.77 2021/02/12 19:30:34 tobhe Exp $ */ +/* $OpenBSD: policy.c,v 1.78 2021/02/13 16:14:12 tobhe Exp $ */ /* + * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2001 Daniel Hartmeier * @@ -22,6 +23,8 @@ #include #include +#include + #include #include #include @@ -667,6 +670,121 @@ sa_address(struct iked_sa *sa, struct iked_addr *addr, struct sockaddr *peer) return (0); } +int +sa_configure_iface(struct iked *env, struct iked_sa *sa, int add) +{ + struct iked_flow *saflow; + struct iovec iov[4]; + int iovcnt; + struct sockaddr *caddr; + struct sockaddr_in *addr; + struct sockaddr_in mask; + struct sockaddr_in6 *addr6; + struct sockaddr_in6 mask6; + int rdomain; + + if (sa->sa_policy->pol_iface == 0) + return (0); + + if (sa->sa_cp_addr) { + iovcnt = 0; + addr = (struct sockaddr_in *)&sa->sa_cp_addr->addr; + iov[0].iov_base = addr; + iov[0].iov_len = sizeof(*addr); + iovcnt++; + + bzero(&mask, sizeof(mask)); + mask.sin_addr.s_addr = + prefixlen2mask(sa->sa_cp_addr->addr_mask ? + sa->sa_cp_addr->addr_mask : 32); + mask.sin_family = AF_INET; + mask.sin_len = sizeof(mask); + iov[1].iov_base = &mask; + iov[1].iov_len = sizeof(mask); + iovcnt++; + + iov[2].iov_base = &sa->sa_policy->pol_iface; + iov[2].iov_len = sizeof(sa->sa_policy->pol_iface); + iovcnt++; + + if(proc_composev(&env->sc_ps, PROC_PARENT, + add ? IMSG_IF_ADDADDR : IMSG_IF_DELADDR, + iov, iovcnt)) + return (-1); + } + if (sa->sa_cp_addr6) { + iovcnt = 0; + addr6 = (struct sockaddr_in6 *)&sa->sa_cp_addr6->addr; + iov[0].iov_base = addr6; + iov[0].iov_len = sizeof(*addr6); + iovcnt++; + + bzero(&mask6, sizeof(mask6)); + prefixlen2mask6(sa->sa_cp_addr6->addr_mask ? + sa->sa_cp_addr6->addr_mask : 128, + (uint32_t *)&mask6.sin6_addr.s6_addr); + mask6.sin6_family = AF_INET6; + mask6.sin6_len = sizeof(mask6); + iov[1].iov_base = &mask6; + iov[1].iov_len = sizeof(mask6); + iovcnt++; + + iov[2].iov_base = &sa->sa_policy->pol_iface; + iov[2].iov_len = sizeof(sa->sa_policy->pol_iface); + iovcnt++; + + if(proc_composev(&env->sc_ps, PROC_PARENT, + add ? IMSG_IF_ADDADDR : IMSG_IF_DELADDR, + iov, iovcnt)) + return (-1); + } + + if (add) { + /* Add direct route to peer */ + if (vroute_setcloneroute(env, getrtable(), + (struct sockaddr *)&sa->sa_peer.addr, 0, NULL)) + return (-1); + } else { + if (vroute_setdelroute(env, getrtable(), + (struct sockaddr *)&sa->sa_peer.addr, + 0, NULL)) + return (-1); + } + + TAILQ_FOREACH(saflow, &sa->sa_flows, flow_entry) { + rdomain = saflow->flow_rdomain == -1 ? + getrtable() : saflow->flow_rdomain; + + switch(saflow->flow_src.addr_af) { + case AF_INET: + caddr = (struct sockaddr *)&sa->sa_cp_addr->addr; + break; + case AF_INET6: + caddr = (struct sockaddr *)&sa->sa_cp_addr6->addr; + break; + default: + return (-1); + } + if (sockaddr_cmp((struct sockaddr *)&saflow->flow_src.addr, + caddr, -1) != 0) + continue; + + if (add) { + if (vroute_setaddroute(env, rdomain, + (struct sockaddr *)&saflow->flow_dst.addr, + saflow->flow_dst.addr_mask, caddr)) + return (-1); + } else { + if (vroute_setdelroute(env, rdomain, + (struct sockaddr *)&saflow->flow_dst.addr, + saflow->flow_dst.addr_mask, caddr)) + return (-1); + } + } + + return (0); +} + void childsa_free(struct iked_childsa *csa) { diff --git a/sbin/iked/types.h b/sbin/iked/types.h index 61dcfcb971d..ad599bf85ed 100644 --- a/sbin/iked/types.h +++ b/sbin/iked/types.h @@ -1,4 +1,4 @@ -/* $OpenBSD: types.h,v 1.41 2021/02/08 16:13:58 tobhe Exp $ */ +/* $OpenBSD: types.h,v 1.42 2021/02/13 16:14:12 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -115,6 +115,11 @@ enum imsg_type { IMSG_CERTVALID, IMSG_CERTINVALID, IMSG_CERT_PARTIAL_CHAIN, + IMSG_IF_ADDADDR, + IMSG_IF_DELADDR, + IMSG_VROUTE_ADD, + IMSG_VROUTE_DEL, + IMSG_VROUTE_CLONE, IMSG_OCSP_FD, IMSG_OCSP_CFG, IMSG_AUTH, diff --git a/sbin/iked/vroute.c b/sbin/iked/vroute.c new file mode 100644 index 00000000000..ceec85c214d --- /dev/null +++ b/sbin/iked/vroute.c @@ -0,0 +1,558 @@ +/* $OpenBSD: vroute.c,v 1.1 2021/02/13 16:14:12 tobhe Exp $ */ + +/* + * Copyright (c) 2021 Tobias Heider + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IKED_VROUTE_PRIO 6 + +#define ROUNDUP(a) \ + (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) + +int vroute_setroute(struct iked *, uint8_t, struct sockaddr *, uint8_t, + struct sockaddr *, int); +int vroute_doroute(struct iked *, int, int, int, uint8_t, struct sockaddr *, + struct sockaddr *, struct sockaddr *); +int vroute_doaddr(struct iked *, char *, struct sockaddr *, struct sockaddr *, int); + +struct iked_vroute_sc { + int ivr_iosock; + int ivr_iosock6; + int ivr_rtsock; + int ivr_rtseq; + pid_t ivr_pid; +}; + +struct vroute_msg { + struct rt_msghdr vm_rtm; + uint8_t vm_space[512]; +}; + +int vroute_process(struct iked *, int msglen, struct vroute_msg *, + struct sockaddr *, struct sockaddr *, struct sockaddr *); + +void +vroute_init(struct iked *env) +{ + struct iked_vroute_sc *ivr; + + ivr = calloc(1, sizeof(*ivr)); + if (ivr == NULL) + fatal("%s: calloc.", __func__); + + if ((ivr->ivr_iosock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + fatal("%s: failed to create ioctl socket", __func__); + + if ((ivr->ivr_iosock6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) + fatal("%s: failed to create ioctl socket", __func__); + + if ((ivr->ivr_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC)) == -1) + fatal("%s: failed to create routing socket", __func__); + + ivr->ivr_pid = getpid(); + + env->sc_vroute = ivr; +} + +int +vroute_getaddr(struct iked *env, struct imsg *imsg) +{ + char ifname[IF_NAMESIZE]; + struct sockaddr *addr, *mask; + uint8_t *ptr; + size_t left; + int af; + unsigned int ifidx; + + ptr = imsg->data; + left = IMSG_DATA_SIZE(imsg); + + if (left < sizeof(*addr)) + fatalx("bad length imsg received"); + + addr = (struct sockaddr *) ptr; + af = addr->sa_family; + + if (left < addr->sa_len) + fatalx("bad length imsg received"); + ptr += addr->sa_len; + left -= addr->sa_len; + + mask = (struct sockaddr *) ptr; + if (mask->sa_family != af) + return (-1); + + if (left < mask->sa_len) + fatalx("bad length imsg received"); + ptr += mask->sa_len; + left -= mask->sa_len; + + if (left != sizeof(ifidx)) + fatalx("bad length imsg received"); + memcpy(&ifidx, ptr, sizeof(ifidx)); + ptr += sizeof(ifidx); + left -= sizeof(ifidx); + + if_indextoname(ifidx, ifname); + + return (vroute_doaddr(env, ifname, addr, mask, + imsg->hdr.type == IMSG_IF_ADDADDR)); +} + +int +vroute_setaddroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, + uint8_t mask, struct sockaddr *ifa) +{ + return (vroute_setroute(env, rdomain, dst, mask, ifa, + IMSG_VROUTE_ADD)); +} + +int +vroute_setcloneroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, + uint8_t mask, struct sockaddr *addr) +{ + return (vroute_setroute(env, rdomain, dst, mask, addr, + IMSG_VROUTE_CLONE)); +} + +int +vroute_setdelroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, + uint8_t mask, struct sockaddr *addr) +{ + return (vroute_setroute(env, rdomain, dst, mask, addr, + IMSG_VROUTE_DEL)); +} + +int +vroute_setroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, + uint8_t mask, struct sockaddr *addr, int type) +{ + struct sockaddr_storage sa; + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + struct iovec iov[5]; + int iovcnt = 0; + uint8_t af; + + if (addr && dst->sa_family != addr->sa_family) + return (-1); + af = dst->sa_family; + + iov[iovcnt].iov_base = ⁡ + iov[iovcnt].iov_len = sizeof(af); + iovcnt++; + + iov[iovcnt].iov_base = &rdomain; + iov[iovcnt].iov_len = sizeof(rdomain); + iovcnt++; + + iov[iovcnt].iov_base = dst; + iov[iovcnt].iov_len = dst->sa_len; + iovcnt++; + + if (type != IMSG_VROUTE_CLONE && addr) { + bzero(&sa, sizeof(sa)); + switch(af) { + case AF_INET: + in = (struct sockaddr_in *)&sa; + in->sin_addr.s_addr = prefixlen2mask(mask); + in->sin_family = af; + in->sin_len = sizeof(*in); + iov[iovcnt].iov_base = in; + iov[iovcnt].iov_len = sizeof(*in); + iovcnt++; + break; + case AF_INET6: + in6 = (struct sockaddr_in6 *)&sa; + prefixlen2mask6(mask, + (uint32_t *)in6->sin6_addr.s6_addr); + in6->sin6_family = af; + in6->sin6_len = sizeof(*in6); + iov[iovcnt].iov_base = in6; + iov[iovcnt].iov_len = sizeof(*in6); + iovcnt++; + break; + } + + iov[iovcnt].iov_base = addr; + iov[iovcnt].iov_len = addr->sa_len; + iovcnt++; + } + + return (proc_composev(&env->sc_ps, PROC_PARENT, type, iov, iovcnt)); +} + +int +vroute_getroute(struct iked *env, struct imsg *imsg) +{ + struct sockaddr *dest, *mask = NULL, *addr = NULL; + uint8_t *ptr; + size_t left; + int addrs = 0; + int type, flags; + uint8_t af, rdomain; + + ptr = (uint8_t *)imsg->data; + left = IMSG_DATA_SIZE(imsg); + + if (left < sizeof(af)) + return (-1); + af = *ptr; + ptr += sizeof(af); + left -= sizeof(af); + + if (left < sizeof(rdomain)) + return (-1); + rdomain = *ptr; + ptr += sizeof(rdomain); + left -= sizeof(rdomain); + + if (left < sizeof(struct sockaddr)) + return (-1); + dest = (struct sockaddr *)ptr; + if (left < dest->sa_len) + return (-1); + socket_setport(dest, 0); + ptr += dest->sa_len; + left -= dest->sa_len; + addrs |= RTA_DST; + + flags = RTF_UP | RTF_STATIC; + if (left != 0) { + if (left < sizeof(struct sockaddr)) + return (-1); + mask = (struct sockaddr *)ptr; + if (left < mask->sa_len) + return (-1); + socket_setport(mask, 0); + ptr += mask->sa_len; + left -= mask->sa_len; + addrs |= RTA_NETMASK; + + if (left < sizeof(struct sockaddr)) + return (-1); + addr = (struct sockaddr *)ptr; + if (left < addr->sa_len) + return (-1); + socket_setport(addr, 0); + ptr += addr->sa_len; + left -= addr->sa_len; + addrs |= RTA_GATEWAY; + } else { + flags |= RTF_HOST; + } + + switch(imsg->hdr.type) { + case IMSG_VROUTE_ADD: + type = RTM_ADD; + break; + case IMSG_VROUTE_DEL: + type = RTM_DELETE; + break; + } + + return (vroute_doroute(env, flags, addrs, rdomain, type, + dest, mask, addr)); +} + +int +vroute_getcloneroute(struct iked *env, struct imsg *imsg) +{ + struct sockaddr *dst; + struct sockaddr_storage dest; + struct sockaddr_storage mask; + struct sockaddr_storage addr; + uint8_t *ptr; + size_t left; + uint8_t af, rdomain; + int flags; + int addrs; + + ptr = (uint8_t *)imsg->data; + left = IMSG_DATA_SIZE(imsg); + + if (left < sizeof(af)) + return (-1); + af = *ptr; + ptr += sizeof(af); + left -= sizeof(af); + + if (left < sizeof(rdomain)) + return (-1); + rdomain = *ptr; + ptr += sizeof(rdomain); + left -= sizeof(rdomain); + + bzero(&dest, sizeof(dest)); + bzero(&mask, sizeof(mask)); + bzero(&addr, sizeof(addr)); + + switch(af) { + case AF_INET: + if (left < sizeof(struct sockaddr_in)) + return (-1); + dst = (struct sockaddr *)ptr;; + memcpy(&dest, ptr, sizeof(struct sockaddr_in));; + ptr += sizeof(struct sockaddr_in); + left -= sizeof(struct sockaddr_in); + break; + case AF_INET6: + if (left < sizeof(struct sockaddr_in6)) + return (-1); + dst = (struct sockaddr *)ptr;; + memcpy(&dest, ptr, sizeof(struct sockaddr_in6));; + ptr += sizeof(struct sockaddr_in6); + left -= sizeof(struct sockaddr_in6); + break; + default: + return (-1); + } + + /* Get route to peer */ + flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC; + if (vroute_doroute(env, flags, RTA_DST, rdomain, RTM_GET, + (struct sockaddr *)&dest, (struct sockaddr *)&mask, + (struct sockaddr *)&addr)) + return (-1); + + /* Set explicit route to peer with gateway addr*/ + addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + return (vroute_doroute(env, flags, addrs, rdomain, RTM_ADD, + dst, (struct sockaddr *)&mask, (struct sockaddr *)&addr)); +} + +int +vroute_doroute(struct iked *env, int flags, int addrs, int rdomain, uint8_t type, + struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr) +{ + struct vroute_msg m_rtmsg; + struct iovec iov[7]; + struct iked_vroute_sc *ivr = env->sc_vroute; + ssize_t len; + int iovcnt = 0; + int i; + long pad = 0; + size_t padlen; + + bzero(&m_rtmsg, sizeof(m_rtmsg)); +#define rtm m_rtmsg.vm_rtm + rtm.rtm_version = RTM_VERSION; + rtm.rtm_tableid = rdomain; + rtm.rtm_type = type; + rtm.rtm_seq = ++ivr->ivr_rtseq; + if (type != RTM_GET) + rtm.rtm_priority = IKED_VROUTE_PRIO; + rtm.rtm_flags = flags; + rtm.rtm_addrs = addrs; + + iov[iovcnt].iov_base = &rtm; + iov[iovcnt].iov_len = sizeof(rtm); + iovcnt++; + + if (rtm.rtm_addrs & RTA_DST) { + iov[iovcnt].iov_base = dest; + iov[iovcnt].iov_len = dest->sa_len; + iovcnt++; + padlen = ROUNDUP(dest->sa_len) - dest->sa_len; + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt].iov_len = padlen; + iovcnt++; + } + } + + if (rtm.rtm_addrs & RTA_GATEWAY) { + iov[iovcnt].iov_base = addr; + iov[iovcnt].iov_len = addr->sa_len; + iovcnt++; + padlen = ROUNDUP(addr->sa_len) - addr->sa_len; + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt].iov_len = padlen; + iovcnt++; + } + } + + if (rtm.rtm_addrs & RTA_NETMASK) { + iov[iovcnt].iov_base = mask; + iov[iovcnt].iov_len = mask->sa_len; + iovcnt++; + padlen = ROUNDUP(mask->sa_len) - mask->sa_len; + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt].iov_len = padlen; + iovcnt++; + } + } + + for (i = 0; i < iovcnt; i++) + rtm.rtm_msglen += iov[i].iov_len; + + log_debug("%s: len: %u type: %s rdomain: %d flags %x addrs %x", __func__, rtm.rtm_msglen, + type == RTM_ADD ? "RTM_ADD" : type == RTM_DELETE ? "RTM_DELETE" : + type == RTM_GET ? "RTM_GET" : "unknown", rdomain, flags, addrs); + + if (writev(ivr->ivr_rtsock, iov, iovcnt) == -1) { + if ((type == RTM_ADD && errno != EEXIST) || + (type == RTM_DELETE && errno != ESRCH)) { + log_warn("%s: write %d", __func__, rtm.rtm_errno); + return (-1); + } + } + + if (type == RTM_GET) { + do { + len = read(ivr->ivr_rtsock, &m_rtmsg, sizeof(m_rtmsg)); + } while(len > 0 && (rtm.rtm_version != RTM_VERSION || + rtm.rtm_seq != ivr->ivr_rtseq || rtm.rtm_pid != ivr->ivr_pid)); + return (vroute_process(env, len, &m_rtmsg, dest, mask, addr)); + } +#undef rtm + + return (0); +} + +int +vroute_process(struct iked *env, int msglen, struct vroute_msg *m_rtmsg, + struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr) +{ + struct sockaddr *sa; + char *cp; + int i; + +#define rtm m_rtmsg->vm_rtm + if (rtm.rtm_version != RTM_VERSION) { + warnx("routing message version %u not understood", + rtm.rtm_version); + return (-1); + } + if (rtm.rtm_msglen > msglen) { + warnx("message length mismatch, in packet %u, returned %d", + rtm.rtm_msglen, msglen); + return (-1); + } + if (rtm.rtm_errno) { + warnx("RTM_GET: %s (errno %d)", + strerror(rtm.rtm_errno), rtm.rtm_errno); + return (-1); + } + cp = m_rtmsg->vm_space; + if(rtm.rtm_addrs) { + for (i = 1; i; i <<= 1) { + if (i & rtm.rtm_addrs) { + sa = (struct sockaddr *)cp; + switch(i) { + case RTA_DST: + memcpy(dest, cp, sa->sa_len); + break; + case RTA_NETMASK: + memcpy(mask, cp, sa->sa_len); + break; + case RTA_GATEWAY: + memcpy(addr, cp, sa->sa_len); + break; + } + cp += ROUNDUP(sa->sa_len); + } + } + } +#undef rtm + return (0); +} + +int +vroute_doaddr(struct iked *env, char *ifname, struct sockaddr *addr, + struct sockaddr *mask, int add) +{ + struct iked_vroute_sc *ivr = env->sc_vroute; + struct ifaliasreq req; + struct in6_aliasreq req6; + unsigned long ioreq; + int af; + char addr_buf[NI_MAXHOST]; + char mask_buf[NI_MAXHOST]; + + af = addr->sa_family; + switch (af) { + case AF_INET: + bzero(&req, sizeof(req)); + strncpy(req.ifra_name, ifname, sizeof(req.ifra_name)); + memcpy(&req.ifra_addr, addr, sizeof(req.ifra_addr)); + if (add) + memcpy(&req.ifra_mask, mask, sizeof(req.ifra_addr)); + + inet_ntop(af, &((struct sockaddr_in *)addr)->sin_addr, + addr_buf, sizeof(addr_buf)); + inet_ntop(af, &((struct sockaddr_in *)mask)->sin_addr, + mask_buf, sizeof(mask_buf)); + log_debug("%s: %s inet %s netmask %s", __func__, + add ? "add" : "del",addr_buf, mask_buf); + + ioreq = add ? SIOCAIFADDR : SIOCDIFADDR; + if (ioctl(ivr->ivr_iosock, ioreq, &req) == -1) { + log_warn("%s: req: %lu", __func__, ioreq); + return (-1); + } + break; + case AF_INET6: + bzero(&req6, sizeof(req6)); + strncpy(req6.ifra_name, ifname, sizeof(req6.ifra_name)); + req6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + req6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + + memcpy(&req6.ifra_addr, addr, sizeof(req6.ifra_addr)); + if (add) + memcpy(&req6.ifra_prefixmask, mask, + sizeof(req6.ifra_prefixmask)); + + + inet_ntop(af, &((struct sockaddr_in6 *)addr)->sin6_addr, + addr_buf, sizeof(addr_buf)); + inet_ntop(af, &((struct sockaddr_in6 *)mask)->sin6_addr, + mask_buf, sizeof(mask_buf)); + log_debug("%s: %s inet6 %s netmask %s", __func__, + add ? "add" : "del",addr_buf, mask_buf); + + ioreq = add ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6; + if (ioctl(ivr->ivr_iosock6, ioreq, &req6) == -1) { + log_warn("%s: req: %lu", __func__, ioreq); + return (-1); + } + break; + default: + return (-1); + } + + return (0); +}