From 8fd570bd951d6ecffe5c8ff12ca193326762d454 Mon Sep 17 00:00:00 2001 From: dlg Date: Mon, 7 Aug 2023 04:10:08 +0000 Subject: [PATCH] add support route based ipsec vpn negotiation with sec(4) via isakmpd. this adds "interface secX" to the grammar that you can use instead of specifying tunnel/transport modes and traffic selectors. if you have config like "ike interface sec0 local ... peer ...", ipsecctl will generate the right config for isakmpd to negotiate esp tunnels for all traffic between 0.0.0.0/0 and 0.0.0.0/0. however, this also specifies that they should be set up as interface SAs in the kernel for use with sec(4). this supports route-based instead of policy based ipsec encapsulation, and allows us to more easily operate with other vendors and products that also offer route-based vpns with opinions about the negotiated policy that doesnt fit with the SPD. support from many including markus@ tobhe@ claudio@ sthen@ patrick@ now is a good time deraadt@ --- sbin/ipsecctl/ike.c | 45 ++++++++++++++++---------- sbin/ipsecctl/ipsecctl.h | 6 +++- sbin/ipsecctl/parse.y | 68 ++++++++++++++++++++++++++++++++++++++-- sbin/ipsecctl/pfkdump.c | 22 ++++++++++++- 4 files changed, 121 insertions(+), 20 deletions(-) diff --git a/sbin/ipsecctl/ike.c b/sbin/ipsecctl/ike.c index aef204b308f..6c3d0f338b7 100644 --- a/sbin/ipsecctl/ike.c +++ b/sbin/ipsecctl/ike.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ike.c,v 1.83 2022/06/25 20:33:40 mbuhl Exp $ */ +/* $OpenBSD: ike.c,v 1.84 2023/08/07 04:10:08 dlg Exp $ */ /* * Copyright (c) 2005 Hans-Joerg Hoexer * @@ -148,6 +148,10 @@ ike_section_ipsec(struct ipsec_rule *r, FILE *fd) if (r->tag) fprintf(fd, SET "[%s]:PF-Tag=%s force\n", r->p2name, r->tag); + if (r->flags & IPSEC_RULE_F_IFACE) { + fprintf(fd, SET "[%s]:Interface=%u force\n", r->p2name, + r->iface); + } } static int @@ -842,21 +846,30 @@ ike_setup_ids(struct ipsec_rule *r) err(1, "ike_setup_ids"); /* Phase 2 name is from and to network, protocol, port*/ - sproto[0] = ssport[0] = sdport[0] = 0; - if (r->proto) - snprintf(sproto, sizeof sproto, "=%u", r->proto); - if (r->sport) - snprintf(ssport, sizeof ssport, ":%u", ntohs(r->sport)); - if (r->dport) - snprintf(sdport, sizeof sdport, ":%u", ntohs(r->dport)); - /* from-network/masklen=proto:port */ - if (asprintf(&r->p2lid, "from-%s%s%s", r->src->name, sproto, ssport) - == -1) - err(1, "ike_setup_ids"); - /* to-network/masklen=proto:port */ - if (asprintf(&r->p2rid, "to-%s%s%s", r->dst->name, sproto, sdport) - == -1) - err(1, "ike_setup_ids"); + if (r->flags & IPSEC_RULE_F_IFACE) { + if (asprintf(&r->p2lid, "from-sec%u", r->iface) == -1) + err(1, "ike_setup_ids"); + if (asprintf(&r->p2rid, "to-sec%u", r->iface) == -1) + err(1, "ike_setup_ids"); + } else { + sproto[0] = ssport[0] = sdport[0] = 0; + if (r->proto) + snprintf(sproto, sizeof sproto, "=%u", r->proto); + if (r->sport) + snprintf(ssport, sizeof ssport, ":%u", ntohs(r->sport)); + if (r->dport) + snprintf(sdport, sizeof sdport, ":%u", ntohs(r->dport)); + + /* from-network/masklen=proto:port */ + if (asprintf(&r->p2lid, "from-%s%s%s", r->src->name, + sproto, ssport) == -1) + err(1, "ike_setup_ids"); + /* to-network/masklen=proto:port */ + if (asprintf(&r->p2rid, "to-%s%s%s", r->dst->name, + sproto, sdport) == -1) + err(1, "ike_setup_ids"); + } + /* 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"); diff --git a/sbin/ipsecctl/ipsecctl.h b/sbin/ipsecctl/ipsecctl.h index 0959c9b2641..c7f92d7d41c 100644 --- a/sbin/ipsecctl/ipsecctl.h +++ b/sbin/ipsecctl/ipsecctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsecctl.h,v 1.75 2021/10/22 12:30:54 bluhm Exp $ */ +/* $OpenBSD: ipsecctl.h,v 1.76 2023/08/07 04:10:08 dlg Exp $ */ /* * Copyright (c) 2004, 2005 Hans-Joerg Hoexer * @@ -178,6 +178,9 @@ TAILQ_HEAD(dst_bundle_queue, ipsec_rule); struct ipsec_rule { u_int8_t type; + unsigned int flags; +#define IPSEC_RULE_F_IFACE (1 << 0) /* iface is valid */ + struct ipsec_addr_wrap *src; struct ipsec_addr_wrap *dst; struct ipsec_addr_wrap *dst2; @@ -215,6 +218,7 @@ struct ipsec_rule { u_int32_t spi; u_int32_t spi2; u_int32_t nr; + unsigned int iface; TAILQ_ENTRY(ipsec_rule) rule_entry; TAILQ_ENTRY(ipsec_rule) bundle_entry; diff --git a/sbin/ipsecctl/parse.y b/sbin/ipsecctl/parse.y index 5ad108382b1..e1c6d47447f 100644 --- a/sbin/ipsecctl/parse.y +++ b/sbin/ipsecctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.182 2023/04/19 13:33:37 jsg Exp $ */ +/* $OpenBSD: parse.y,v 1.183 2023/08/07 04:10:08 dlg Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -232,6 +232,7 @@ struct ipsec_transforms *ipsec_transforms; typedef struct { union { int64_t number; + uint32_t unit; u_int8_t ikemode; u_int8_t dir; u_int8_t satype; /* encapsulating prococol */ @@ -284,9 +285,10 @@ typedef struct { %token AUTHKEY ENCKEY FILENAME AUTHXF ENCXF ERROR IKE MAIN QUICK AGGRESSIVE %token PASSIVE ACTIVE ANY IPIP IPCOMP COMPXF TUNNEL TRANSPORT DYNAMIC LIFETIME %token TYPE DENY BYPASS LOCAL PROTO USE ACQUIRE REQUIRE DONTACQ GROUP PORT TAG -%token INCLUDE BUNDLE UDPENCAP +%token INCLUDE BUNDLE UDPENCAP INTERFACE %token STRING %token NUMBER +%type iface %type string %type dir %type satype @@ -401,6 +403,41 @@ ikerule : IKE ikemode satype tmode proto hosts peers if (expand_rule(r, &$7, 0, 0, NULL, NULL, NULL)) errx(1, "ikerule: expand_rule"); } + + /* ike interface sec0 local $h_self peer $h_s2s1 ... */ + | IKE ikemode iface peers + phase1mode phase2mode ids ikeauth { + uint8_t proto = 0; // IPPROTO_IPIP; + struct ipsec_hosts hosts; + struct ike_mode *phase1mode = $5; + struct ike_mode *phase2mode = $6; + uint8_t satype = IPSEC_ESP; + uint8_t tmode = IPSEC_TUNNEL; + uint8_t mode = $2; + struct ike_auth *authtype = &$8; + char *tag = NULL; + + struct ipsec_rule *r; + + hosts.src = host_v4("0.0.0.0/0", 1); + hosts.sport = htons(0); + hosts.dst = host_v4("0.0.0.0/0", 1); + hosts.dport = htons(0); + + r = create_ike(proto, &hosts, phase1mode, phase2mode, + satype, tmode, mode, $7.srcid, $7.dstid, + authtype, tag); + if (r == NULL) { + YYERROR; + } + + r->flags |= IPSEC_RULE_F_IFACE; + r->iface = $3; + + if (expand_rule(r, &$4, 0, 0, NULL, NULL, NULL)) + errx(1, "ikerule: expand interface rule"); + + } ; satype : /* empty */ { $$ = IPSEC_ESP; } @@ -909,6 +946,30 @@ tag : /* empty */ } ; +iface : INTERFACE STRING { + static const char prefix[] = "sec"; + const char *errstr = NULL; + size_t len, plen; + + plen = strlen(prefix); + len = strlen($2); + + if (len <= plen || memcmp($2, prefix, plen) != 0) { + yyerror("invalid %s interface name", prefix); + free($2); + YYERROR; + } + + $$ = strtonum($2 + plen, 0, UINT_MAX, &errstr); + free($2); + if (errstr != NULL) { + yyerror("invalid %s interface unit: %s", + prefix, errstr); + YYERROR; + } + } + ; + string : string STRING { if (asprintf(&$$, "%s %s", $1, $2) == -1) @@ -1009,6 +1070,7 @@ lookup(char *s) { "ike", IKE }, { "in", IN }, { "include", INCLUDE }, + { "interface", INTERFACE }, { "ipcomp", IPCOMP }, { "ipip", IPIP }, { "lifetime", LIFETIME }, @@ -2216,6 +2278,7 @@ copyrule(struct ipsec_rule *rule) r->enckey = copykey(rule->enckey); r->tag = copytag(rule->tag); + r->flags = rule->flags; r->p1ie = rule->p1ie; r->p2ie = rule->p2ie; r->type = rule->type; @@ -2231,6 +2294,7 @@ copyrule(struct ipsec_rule *rule) r->udpencap = rule->udpencap; r->udpdport = rule->udpdport; r->nr = rule->nr; + r->iface = rule->iface; return (r); } diff --git a/sbin/ipsecctl/pfkdump.c b/sbin/ipsecctl/pfkdump.c index b2bd5825c63..9bd728398de 100644 --- a/sbin/ipsecctl/pfkdump.c +++ b/sbin/ipsecctl/pfkdump.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfkdump.c,v 1.56 2023/03/07 17:43:59 guenther Exp $ */ +/* $OpenBSD: pfkdump.c,v 1.57 2023/08/07 04:10:08 dlg Exp $ */ /* * Copyright (c) 2003 Markus Friedl. All rights reserved. @@ -62,6 +62,7 @@ static void print_mtu(struct sadb_ext *, struct sadb_msg *, int); static void print_tap(struct sadb_ext *, struct sadb_msg *, int); static void print_satype(struct sadb_ext *, struct sadb_msg *, int); static void print_counter(struct sadb_ext *, struct sadb_msg *, int); +static void print_iface(struct sadb_ext *, struct sadb_msg *, int); static struct idname *lookup(struct idname *, u_int32_t); static char *lookup_name(struct idname *, u_int32_t); @@ -115,6 +116,7 @@ struct idname ext_types[] = { { SADB_X_EXT_TAP, "tap", print_tap }, { SADB_X_EXT_SATYPE2, "satype2", print_satype }, { SADB_X_EXT_COUNTER, "counter", print_counter }, + { SADB_X_EXT_IFACE, "interface", print_iface }, { 0, NULL, NULL } }; @@ -463,6 +465,24 @@ print_counter(struct sadb_ext *ext, struct sadb_msg *msg, int opts) #undef plural } +static void +print_iface(struct sadb_ext *ext, struct sadb_msg *msg, int opts) +{ + struct sadb_x_iface *siface = (struct sadb_x_iface *)ext; + const char *dir = "unknown"; + + switch (siface->sadb_x_iface_direction) { + case IPSP_DIRECTION_IN: + dir = "in"; + break; + case IPSP_DIRECTION_OUT: + dir = "out"; + break; + } + + printf("sec%u direction %s", siface->sadb_x_iface_unit, dir); +} + static char * alg_by_ext(u_int8_t ext_type, u_int8_t id) { -- 2.20.1