add support route based ipsec vpn negotiation with sec(4) via isakmpd.
authordlg <dlg@openbsd.org>
Mon, 7 Aug 2023 04:10:08 +0000 (04:10 +0000)
committerdlg <dlg@openbsd.org>
Mon, 7 Aug 2023 04:10:08 +0000 (04:10 +0000)
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
sbin/ipsecctl/ipsecctl.h
sbin/ipsecctl/parse.y
sbin/ipsecctl/pfkdump.c

index aef204b..6c3d0f3 100644 (file)
@@ -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 <hshoexer@openbsd.org>
  *
@@ -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");
index 0959c9b..c7f92d7 100644 (file)
@@ -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 <hshoexer@openbsd.org>
  *
@@ -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;
index 5ad1083..e1c6d47 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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 <v.string>              STRING
 %token <v.number>              NUMBER
+%type  <v.unit>                iface
 %type  <v.string>              string
 %type  <v.dir>                 dir
 %type  <v.satype>              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);
 }
index b2bd582..9bd7283 100644 (file)
@@ -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)
 {