diff options
author | Marco Pfatschbacher <mpf@cvs.openbsd.org> | 2009-01-20 14:36:20 +0000 |
---|---|---|
committer | Marco Pfatschbacher <mpf@cvs.openbsd.org> | 2009-01-20 14:36:20 +0000 |
commit | 66729e2a843c8e9110bf23a513cfbf95d3e04087 (patch) | |
tree | b485048be383611ea2acfc86d5d97f56ae3705da | |
parent | 4c0a50691c50459754a492efc215ccabc8f9e4e1 (diff) |
Add support to isakmpd(8) and ipsecctl(8) to install SA's with a
different source network than we have negotiated with a peer.
This enables us to do nat/binat on the enc(4) interface.
Very useful to work around rfc 1918 collisions.
Manpage and testing by Mitja Muzenic. Thanks!
OK hshoexer@, markus@. "I like it" todd@
-rw-r--r-- | sbin/ipsecctl/ike.c | 131 | ||||
-rw-r--r-- | sbin/ipsecctl/ipsec.conf.5 | 48 | ||||
-rw-r--r-- | sbin/ipsecctl/ipsecctl.c | 4 | ||||
-rw-r--r-- | sbin/ipsecctl/ipsecctl.h | 4 | ||||
-rw-r--r-- | sbin/ipsecctl/parse.y | 33 | ||||
-rw-r--r-- | sbin/isakmpd/ipsec.c | 22 |
6 files changed, 167 insertions, 75 deletions
diff --git a/sbin/ipsecctl/ike.c b/sbin/ipsecctl/ike.c index 0569c409a79..f2ceb549f8a 100644 --- a/sbin/ipsecctl/ike.c +++ b/sbin/ipsecctl/ike.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ike.c,v 1.64 2008/07/01 15:00:53 bluhm Exp $ */ +/* $OpenBSD: ike.c,v 1.65 2009/01/20 14:36:19 mpf Exp $ */ /* * Copyright (c) 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org> * @@ -136,6 +136,8 @@ ike_section_ipsec(struct ipsec_rule *r, FILE *fd) fprintf(fd, SET "[%s]:Configuration=phase2-%s force\n", r->p2name, r->p2name); fprintf(fd, SET "[%s]:Local-ID=%s force\n", r->p2name, r->p2lid); + if (r->p2nid) + fprintf(fd, SET "[%s]:NAT-ID=%s force\n", r->p2name, r->p2nid); fprintf(fd, SET "[%s]:Remote-ID=%s force\n", r->p2name, r->p2rid); if (r->tag) @@ -418,48 +420,57 @@ ike_section_p1(struct ipsec_rule *r, FILE *fd) } static void -ike_section_p2ids(struct ipsec_rule *r, FILE *fd) +ike_section_p2ids_net(struct ipsec_addr *iamask, sa_family_t af, char *name, + char *p2xid, FILE *fd) { char mask[NI_MAXHOST], *network, *p; struct sockaddr_storage sas; struct sockaddr *sa = (struct sockaddr *)&sas; - struct ipsec_addr_wrap *src = r->src; - struct ipsec_addr_wrap *dst = r->dst; - if (src->netaddress) { - bzero(&sas, sizeof(struct sockaddr_storage)); - bzero(mask, sizeof(mask)); - sa->sa_family = src->af; - switch (src->af) { - case AF_INET: - sa->sa_len = sizeof(struct sockaddr_in); - bcopy(&src->mask.ipa, - &((struct sockaddr_in *)(sa))->sin_addr, - sizeof(struct in6_addr)); - break; - case AF_INET6: - sa->sa_len = sizeof(struct sockaddr_in6); - bcopy(&src->mask.ipa, - &((struct sockaddr_in6 *)sa)->sin6_addr, - sizeof(struct in6_addr)); - break; - } - if (getnameinfo(sa, sa->sa_len, mask, sizeof(mask), NULL, 0, - NI_NUMERICHOST)) - errx(1, "could not get a numeric mask"); + bzero(&sas, sizeof(struct sockaddr_storage)); + bzero(mask, sizeof(mask)); + sa->sa_family = af; + switch (af) { + case AF_INET: + sa->sa_len = sizeof(struct sockaddr_in); + bcopy(&iamask->ipa, + &((struct sockaddr_in *)(sa))->sin_addr, + sizeof(struct in6_addr)); + break; + case AF_INET6: + sa->sa_len = sizeof(struct sockaddr_in6); + bcopy(&iamask->ipa, + &((struct sockaddr_in6 *)(sa))->sin6_addr, + sizeof(struct in6_addr)); + break; + } + if (getnameinfo(sa, sa->sa_len, mask, sizeof(mask), NULL, 0, + NI_NUMERICHOST)) + errx(1, "could not get a numeric mask"); - if ((network = strdup(src->name)) == NULL) - err(1, "ike_section_p2ids: strdup"); - if ((p = strrchr(network, '/')) != NULL) - *p = '\0'; + if ((network = strdup(name)) == NULL) + err(1, "ike_section_p2ids: strdup"); + if ((p = strrchr(network, '/')) != NULL) + *p = '\0'; - fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR_SUBNET force\n", - r->p2lid, ((src->af == AF_INET) ? 4 : 6)); - fprintf(fd, SET "[%s]:Network=%s force\n", r->p2lid, - network); - fprintf(fd, SET "[%s]:Netmask=%s force\n", r->p2lid, mask); + fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR_SUBNET force\n", + p2xid, ((af == AF_INET) ? 4 : 6)); + fprintf(fd, SET "[%s]:Network=%s force\n", p2xid, network); + fprintf(fd, SET "[%s]:Netmask=%s force\n", p2xid, mask); + + free(network); +} + +static void +ike_section_p2ids(struct ipsec_rule *r, FILE *fd) +{ + char *p; + struct ipsec_addr_wrap *src = r->src; + struct ipsec_addr_wrap *dst = r->dst; - free(network); + if (src->netaddress) { + ike_section_p2ids_net(&src->mask, src->af, src->name, + r->p2lid, fd); } else { fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR force\n", r->p2lid, ((src->af == AF_INET) ? 4 : 6)); @@ -468,40 +479,22 @@ ike_section_p2ids(struct ipsec_rule *r, FILE *fd) fprintf(fd, SET "[%s]:Address=%s force\n", r->p2lid, src->name); } - if (dst->netaddress) { - bzero(&sas, sizeof(struct sockaddr_storage)); - bzero(mask, sizeof(mask)); - sa->sa_family = dst->af; - switch (dst->af) { - case AF_INET: - sa->sa_len = sizeof(struct sockaddr_in); - bcopy(&dst->mask.ipa, - &((struct sockaddr_in *)(sa))->sin_addr, - sizeof(struct in6_addr)); - break; - case AF_INET6: - sa->sa_len = sizeof(struct sockaddr_in6); - bcopy(&dst->mask.ipa, - &((struct sockaddr_in6 *)(sa))->sin6_addr, - sizeof(struct in6_addr)); - break; - } - if (getnameinfo(sa, sa->sa_len, mask, sizeof(mask), NULL, 0, - NI_NUMERICHOST)) - errx(1, "could not get a numeric mask"); - if ((network = strdup(dst->name)) == NULL) - err(1, "ike_section_p2ids: strdup"); - if ((p = strrchr(network, '/')) != NULL) + if (src->srcnat && src->srcnat->netaddress) { + ike_section_p2ids_net(&src->srcnat->mask, src->af, src->srcnat->name, + r->p2nid, fd); + } else if (src->srcnat) { + fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR force\n", + r->p2nid, ((src->af == AF_INET) ? 4 : 6)); + if ((p = strrchr(src->srcnat->name, '/')) != NULL) *p = '\0'; + fprintf(fd, SET "[%s]:Address=%s force\n", r->p2nid, + src->srcnat->name); + } - fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR_SUBNET force\n", - r->p2rid, ((dst->af == AF_INET) ? 4 : 6)); - fprintf(fd, SET "[%s]:Network=%s force\n", r->p2rid, - network); - fprintf(fd, SET "[%s]:Netmask=%s force\n", r->p2rid, mask); - - free(network); + if (dst->netaddress) { + ike_section_p2ids_net(&dst->mask, dst->af, dst->name, + r->p2rid, fd); } else { fprintf(fd, SET "[%s]:ID-type=IPV%d_ADDR force\n", r->p2rid, ((dst->af == AF_INET) ? 4 : 6)); @@ -656,6 +649,12 @@ ike_setup_ids(struct ipsec_rule *r) /* from-network/masklen=proto:port-to-network/masklen=proto:port */ if (asprintf(&r->p2name, "%s-%s", r->p2lid , r->p2rid) == -1) err(1, "ike_setup_ids"); + /* nat-network/masklen=proto:port */ + if (r->src->srcnat && r->src->srcnat->name) { + if (asprintf(&r->p2nid, "nat-%s%s%s", r->src->srcnat->name, sproto, + ssport) == -1) + err(1, "ike_setup_ids"); + } } int diff --git a/sbin/ipsecctl/ipsec.conf.5 b/sbin/ipsecctl/ipsec.conf.5 index 44b7e12e6e9..936aeb1cf09 100644 --- a/sbin/ipsecctl/ipsec.conf.5 +++ b/sbin/ipsecctl/ipsec.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ipsec.conf.5,v 1.119 2008/11/29 11:32:59 hshoexer Exp $ +.\" $OpenBSD: ipsec.conf.5,v 1.120 2009/01/20 14:36:19 mpf Exp $ .\" .\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved. .\" @@ -22,7 +22,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: November 29 2008 $ +.Dd $Mdocdate: January 20 2009 $ .Dt IPSEC.CONF 5 .Os .Sh NAME @@ -242,6 +242,7 @@ see the file .It Xo .Ic from Ar src .Op Ic port Ar sport +.Oo ( Ar srcnat ) Oc .Ic to Ar dst .Op Ic port Ar dport .Xc @@ -252,11 +253,19 @@ and destination address The keyword .Ar any will match any address (i.e. 0.0.0.0/0). -Host addresses will be parsed as type +If the +.Ar src +argument specifies a fictional source ID, +the +.Ar srcnat +parameter can be used to specify the actual source address. +This can be used in outgoing NAT/BINAT scenarios as described below. +Host addresses are parsed as type .Dq IPV4_ADDR ; adding the suffix /32 will change the type to .Dq IPV4_ADDR_SUBNET , which can improve interoperability with some IKE implementations. +.Pp The optional .Ic port modifiers restrict the flows to the specified ports. @@ -525,6 +534,39 @@ example: ike esp from 10.1.1.0/24 to 10.1.2.0/24 peer 192.168.3.2 \e tag ipsec-$domain .Ed +.Sh OUTGOING NETWORK ADDRESS TRANSLATION +In some network topologies it is desirable to perform NAT on traffic leaving +through the VPN tunnel. +In order to achieve that, +the +.Ar src +argument is used to negotiate the desired network ID with the peer +and the +.Ar srcnat +parameter defines the true local subnet, +so that a correct SA can be installed on the local side. +.Pp +For example, +if the local subnet is 192.168.1.0/24 and all the traffic +for a specific VPN peer should appear as coming from 10.10.10.1, +the following configuration is used: +.Bd -literal -offset indent +ike esp from 10.10.10.1 (192.168.1.0/24) to 192.168.2.0/24 \e + peer 10.10.20.1 +.Ed +.Pp +Naturally, +a relevant NAT rule is required in +.Xr pf.conf 5 . +For the example above, +this would be: +.Bd -literal -offset indent +nat on enc0 from 192.168.1.0/24 to 192.168.2.0/24 -> 10.10.10.1 +.Ed +.Pp +From the peer's point of view, +the local end of the VPN tunnel is declared to be 10.10.10.1 +and all the traffic arrives with that source address. .Sh CRYPTO TRANSFORMS It is very important that keys are not guessable. One practical way of generating keys is to use diff --git a/sbin/ipsecctl/ipsecctl.c b/sbin/ipsecctl/ipsecctl.c index b6eccce5e1a..9fb7ee084a6 100644 --- a/sbin/ipsecctl/ipsecctl.c +++ b/sbin/ipsecctl/ipsecctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsecctl.c,v 1.71 2008/07/21 14:37:53 bluhm Exp $ */ +/* $OpenBSD: ipsecctl.c,v 1.72 2009/01/20 14:36:19 mpf Exp $ */ /* * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org> * @@ -252,6 +252,8 @@ ipsecctl_free_rule(struct ipsec_rule *rp) free(rp->p2name); if (rp->p2lid) free(rp->p2lid); + if (rp->p2nid) + free(rp->p2nid); if (rp->p2rid) free(rp->p2rid); free(rp); diff --git a/sbin/ipsecctl/ipsecctl.h b/sbin/ipsecctl/ipsecctl.h index 52af45c08ff..8a77b52ddc8 100644 --- a/sbin/ipsecctl/ipsecctl.h +++ b/sbin/ipsecctl/ipsecctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsecctl.h,v 1.57 2008/07/01 15:00:53 bluhm Exp $ */ +/* $OpenBSD: ipsecctl.h,v 1.58 2009/01/20 14:36:19 mpf Exp $ */ /* * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org> * @@ -107,6 +107,7 @@ struct ipsec_addr_wrap { char *name; struct ipsec_addr_wrap *next; struct ipsec_addr_wrap *tail; + struct ipsec_addr_wrap *srcnat; }; struct ipsec_hosts { @@ -189,6 +190,7 @@ struct ipsec_rule { char *p2name; /* Phase 2 Name (IPsec-XX) */ char *p2lid; /* Phase 2 source ID */ char *p2rid; /* Phase 2 destination ID */ + char *p2nid; /* Phase 2 source NAT-ID */ u_int8_t satype; /* encapsulating prococol */ u_int8_t proto; /* encapsulated protocol */ u_int8_t proto2; diff --git a/sbin/ipsecctl/parse.y b/sbin/ipsecctl/parse.y index 39cbb5d3fc4..27b0d9066c0 100644 --- a/sbin/ipsecctl/parse.y +++ b/sbin/ipsecctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.140 2008/11/14 23:16:37 hshoexer Exp $ */ +/* $OpenBSD: parse.y,v 1.141 2009/01/20 14:36:19 mpf Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -262,7 +262,7 @@ typedef struct { %type <v.number> portval %type <v.peers> peers %type <v.singlehost> singlehost -%type <v.host> host host_list +%type <v.host> host host_list host_spec %type <v.ids> ids %type <v.id> id %type <v.spis> spispec @@ -407,12 +407,28 @@ dir : /* empty */ { $$ = IPSEC_INOUT; } ; hosts : FROM host port TO host port { + struct ipsec_addr_wrap *ipa; + for (ipa = $5; ipa; ipa = ipa->next) { + if (ipa->srcnat) { + yyerror("no flow NAT support for" + " destination network: %s", ipa->name); + YYERROR; + } + } $$.src = $2; $$.sport = $3; $$.dst = $5; $$.dport = $6; } | TO host port FROM host port { + struct ipsec_addr_wrap *ipa; + for (ipa = $2; ipa; ipa = ipa->next) { + if (ipa->srcnat) { + yyerror("no flow NAT support for" + " destination network: %s", ipa->name); + YYERROR; + } + } $$.src = $5; $$.sport = $6; $$.dst = $2; @@ -491,7 +507,7 @@ host_list : host { $$ = $1; } } ; -host : STRING { +host_spec : STRING { if (($$ = host($1)) == NULL) { free($1); yyerror("could not parse host specification"); @@ -512,6 +528,17 @@ host : STRING { } free(buf); } + ; + +host : host_spec { $$ = $1; } + | host_spec '(' host_spec ')' { + if ($3->af != $1->af) { + yyerror("Flow NAT address family mismatch"); + YYERROR; + } + $$ = $1; + $$->srcnat = $3; + } | ANY { struct ipsec_addr_wrap *ipa; diff --git a/sbin/isakmpd/ipsec.c b/sbin/isakmpd/ipsec.c index df423c22d78..dee610aec3f 100644 --- a/sbin/isakmpd/ipsec.c +++ b/sbin/isakmpd/ipsec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsec.c,v 1.130 2007/09/02 15:19:24 deraadt Exp $ */ +/* $OpenBSD: ipsec.c,v 1.131 2009/01/20 14:36:19 mpf Exp $ */ /* $EOM: ipsec.c,v 1.143 2000/12/11 23:57:42 niklas Exp $ */ /* @@ -46,6 +46,7 @@ #include "attribute.h" #include "conf.h" +#include "connection.h" #include "constants.h" #include "crypto.h" #include "dh.h" @@ -522,6 +523,22 @@ static int ipsec_set_network(u_int8_t *src_id, u_int8_t *dst_id, struct ipsec_sa *isa) { int id; + char *name, *nat = NULL; + u_int8_t *nat_id = NULL; + size_t nat_sz; + + if ((name = connection_passive_lookup_by_ids(src_id, dst_id))) + nat = conf_get_str(name, "NAT-ID"); + + if (nat) { + if ((nat_id = ipsec_build_id(nat, &nat_sz))) { + LOG_DBG((LOG_EXCHANGE, 50, "ipsec_set_network: SRC-NAT:" + " src: %s -> %s", name, nat)); + src_id = nat_id; + } else + log_print("ipsec_set_network: ipsec_build_id" + " failed for NAT-ID: %s", nat); + } /* Set source address/mask. */ id = GET_ISAKMP_ID_TYPE(src_id); @@ -594,6 +611,9 @@ ipsec_set_network(u_int8_t *src_id, u_int8_t *dst_id, struct ipsec_sa *isa) src_id + ISAKMP_ID_DOI_DATA_OFF + IPSEC_ID_PORT_OFF, IPSEC_ID_PORT_LEN); + if (nat_id) + free(nat_id); + /* Set destination address. */ id = GET_ISAKMP_ID_TYPE(dst_id); switch (id) { |