On Mon, Apr 13, 2020 at 11:56:36AM +0200, Tobias Heider wrote:
> Hi,
> 
> the diff below adds a new feature that allows the use of separate rdomains
> for the encrypted and unencrypted side of ipsec(4) flows.
> 
> The idea is that an edge router that controls access to a private network
> via ipsec can have its uplink in one rdomain and the private network in
> another. The kernel automatically moves successfully encrypted/decrypted
> packets to the respective rdomain. The outer (uplink) rdomain only ever sees
> encrypted packets, which could add another layer of protection against side
> channel leak attacks that we have seen in the past.
> 
> The feature can be configured in iked(8) with the new 'rdomain $NUMBER'
> option (see iked.conf(5)).  Only the inner (unencrypted) rdomain has to
> be configured, the outer rdomain is always the one the iked instance
> is running in.  For this to work, the inner rdomain must exist
> (have at least one interface assigned) and have an enc(4) interface
> assigned before starting iked.
> 
> The resulting flows and the outgoing SA are visible from the inner
> rdomain only, so don't forget to 'route -T $ID exec' ipsecctl.
> The incoming SA is visible from the outer rdomain.  tcpdump works
> as usual on the enc(4) interface of the inner rdomain.
> 
> Thanks to markus@ who did a lot of the work on this.
> Comments and tests welcome ;)

I haven't heard of any problems with this so far but I think it would
be nice to have a few more test results.

Also, ok?

diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5
index b0128610c81..980fa66952c 100644
--- a/sbin/iked/iked.conf.5
+++ b/sbin/iked/iked.conf.5
@@ -311,6 +311,18 @@ For a list of all the protocol name to number mappings 
used by
 .Xr iked 8 ,
 see the file
 .Pa /etc/protocols .
+.It Ic rdomain Ar number
+Specify a different routing domain for unencrypted traffic.
+The resulting IPsec SAs will match outgoing packets in the specified
+.Ic rdomain Ar number
+and move the encrypted packets to the rdomain the
+.Xr iked 8
+instance is running in.
+Vice versa, incoming
+.Xr ipsec 4
+traffic is moved to
+.Ic rdomain Ar number
+after decryption.
 .It Xo
 .Ic from Ar src
 .Op Ic port Ar sport
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index 598e82d0157..203f34d637c 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -151,6 +151,7 @@ struct iked_flow {
        struct iked_addr                 flow_src;
        struct iked_addr                 flow_dst;
        unsigned int                     flow_dir;      /* in/out */
+       int                              flow_rdomain;
        struct iked_addr                 flow_prenat;
 
        unsigned int                     flow_loaded;   /* pfkey done */
@@ -261,6 +262,7 @@ struct iked_policy {
        uint8_t                          pol_certreqtype;
 
        int                              pol_af;
+       int                              pol_rdomain;
        uint8_t                          pol_saproto;
        unsigned int                     pol_ipproto;
 
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 4074ea549b8..4d960a60342 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -5371,6 +5371,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa 
*sa,
                        flowa->flow_dir = IPSP_DIRECTION_OUT;
                        flowa->flow_saproto = ic ? IKEV2_SAPROTO_IPCOMP :
                            prop->prop_protoid;
+                       flowa->flow_rdomain = sa->sa_policy->pol_rdomain;
                        flowa->flow_local = &sa->sa_local;
                        flowa->flow_peer = &sa->sa_peer;
                        flowa->flow_ikesa = sa;
@@ -6440,7 +6441,7 @@ ikev2_info_flow(struct iked *env, int dolog, const char 
*msg, struct iked_flow *
        int             buflen;
 
        buflen = asprintf(&buf,
-           "%s: %p %s %s %s/%d -> %s/%d [%u] (%s) @%p\n", msg, flow,
+           "%s: %p %s %s %s/%d -> %s/%d [%u]@%d (%s) @%p\n", msg, flow,
            print_map(flow->flow_saproto, ikev2_saproto_map),
            flow->flow_dir == IPSP_DIRECTION_IN ? "in" : "out",
            print_host((struct sockaddr *)&flow->flow_src.addr, NULL, 0),
@@ -6448,6 +6449,7 @@ ikev2_info_flow(struct iked *env, int dolog, const char 
*msg, struct iked_flow *
            print_host((struct sockaddr *)&flow->flow_dst.addr, NULL, 0),
            flow->flow_dst.addr_mask,
            flow->flow_ipproto,
+           flow->flow_rdomain,
            flow->flow_loaded ? "L" : "",
            flow->flow_ikesa);
 
diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y
index 38c07b95de2..bb578d45399 100644
--- a/sbin/iked/parse.y
+++ b/sbin/iked/parse.y
@@ -351,7 +351,8 @@ void                         copy_transforms(unsigned int,
                            const struct ipsec_xf **, unsigned int,
                            struct iked_transform **, unsigned int *,
                            struct iked_transform *, size_t);
-int                     create_ike(char *, int, uint8_t, struct ipsec_hosts *,
+int                     create_ike(char *, int, uint8_t,
+                           int, struct ipsec_hosts *,
                            struct ipsec_hosts *, struct ipsec_mode *,
                            struct ipsec_mode *, uint8_t,
                            uint8_t, char *, char *,
@@ -408,7 +409,7 @@ typedef struct {
 %token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER
 %token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
 %token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
-%token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE
+%token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE RDOMAIN
 %token FRAGMENTATION NOFRAGMENTATION
 %token <v.string>              STRING
 %token <v.number>              NUMBER
@@ -418,7 +419,7 @@ typedef struct {
 %type  <v.number>              protoval
 %type  <v.hosts>               hosts hosts_list
 %type  <v.port>                port
-%type  <v.number>              portval af
+%type  <v.number>              portval af rdomain
 %type  <v.peers>               peers
 %type  <v.anyhost>             anyhost
 %type  <v.host>                host host_spec
@@ -491,12 +492,12 @@ user              : USER STRING STRING            {
                }
                ;
 
-ikev2rule      : IKEV2 name ikeflags satype af proto hosts_list peers
+ikev2rule      : IKEV2 name ikeflags satype af proto rdomain hosts_list peers
                    ike_sas child_sas ids ikelifetime lifetime ikeauth ikecfg
                    filters {
-                       if (create_ike($2, $5, $6, $7, &$8, $9, $10, $4, $3,
-                           $11.srcid, $11.dstid, $12, &$13, &$14,
-                           $16, $15) == -1) {
+                       if (create_ike($2, $5, $6, $7, $8, &$9, $10, $11, $4,
+                           $3, $12.srcid, $12.dstid, $13, &$14, &$15,
+                           $17, $16) == -1) {
                                yyerror("create_ike failed");
                                YYERROR;
                        }
@@ -577,6 +578,15 @@ protoval   : STRING                        {
                }
                ;
 
+rdomain                : /* empty */                   { $$ = -1; }
+               | RDOMAIN NUMBER                {
+                       if ($2 > 255 || $2 < 0) {
+                               yyerror("rdomain outside range");
+                               YYERROR;
+                       }
+                       $$ = $2;
+               }
+
 hosts_list     : hosts                         { $$ = $1; }
                | hosts_list comma hosts        {
                        if ($3 == NULL)
@@ -1264,6 +1274,7 @@ lookup(char *s)
                { "proto",              PROTO },
                { "psk",                PSK },
                { "quick",              QUICK },
+               { "rdomain",            RDOMAIN },
                { "sa",                 SA },
                { "set",                SET },
                { "skip",               SKIP },
@@ -2497,6 +2508,9 @@ print_policy(struct iked_policy *pol)
                        print_verbose(" inet6");
        }
 
+       if (pol->pol_rdomain)
+               print_verbose(" rdomain %d", pol->pol_rdomain);
+
        RB_FOREACH(flow, iked_flows, &pol->pol_flows) {
                print_verbose(" from %s",
                    print_host((struct sockaddr *)&flow->flow_src.addr, NULL,
@@ -2679,7 +2693,8 @@ copy_transforms(unsigned int type,
 }
 
 int
-create_ike(char *name, int af, uint8_t ipproto, struct ipsec_hosts *hosts,
+create_ike(char *name, int af, uint8_t ipproto,
+    int rdomain, struct ipsec_hosts *hosts,
     struct ipsec_hosts *peers, struct ipsec_mode *ike_sa,
     struct ipsec_mode *ipsec_sa, uint8_t saproto,
     uint8_t flags, char *srcid, char *dstid,
@@ -2709,6 +2724,7 @@ create_ike(char *name, int af, uint8_t ipproto, struct 
ipsec_hosts *hosts,
        pol.pol_saproto = saproto;
        pol.pol_ipproto = ipproto;
        pol.pol_flags = flags;
+       pol.pol_rdomain = rdomain;
        memcpy(&pol.pol_auth, authtype, sizeof(struct iked_auth));
 
        if (name != NULL) {
@@ -2969,6 +2985,7 @@ create_ike(char *name, int af, uint8_t ipproto, struct 
ipsec_hosts *hosts,
                }
 
                flow->flow_ipproto = ipproto;
+               flow->flow_rdomain = rdomain;
 
                if (RB_INSERT(iked_flows, &pol.pol_flows, flow) == NULL)
                        pol.pol_nflows++;
diff --git a/sbin/iked/pfkey.c b/sbin/iked/pfkey.c
index b4d2ffff537..a225432d159 100644
--- a/sbin/iked/pfkey.c
+++ b/sbin/iked/pfkey.c
@@ -40,7 +40,7 @@
 #include "ikev2.h"
 
 #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
-#define IOV_CNT 20
+#define IOV_CNT 21
 
 #define PFKEYV2_CHUNK sizeof(uint64_t)
 #define PFKEY_REPLY_TIMEOUT 1000
@@ -51,6 +51,8 @@
 static uint32_t sadb_msg_seq = 0;
 static unsigned int sadb_decoupled = 0;
 
+static int iked_rdomain = 0;
+
 static struct event pfkey_timer_ev;
 static struct timeval pfkey_timer_tv;
 
@@ -184,6 +186,7 @@ pfkey_flow(int sd, uint8_t satype, uint8_t action, struct 
iked_flow *flow)
        struct sadb_address      sa_src, sa_dst, sa_local, sa_peer, sa_smask,
                                 sa_dmask;
        struct sadb_protocol     sa_flowtype, sa_protocol;
+       struct sadb_x_rdomain    sa_rdomain;
        struct sadb_ident       *sa_srcid, *sa_dstid;
        struct sockaddr_storage  ssrc, sdst, slocal, speer, smask, dmask;
        struct iovec             iov[IOV_CNT];
@@ -335,6 +338,14 @@ pfkey_flow(int sd, uint8_t satype, uint8_t action, struct 
iked_flow *flow)
                    SADB_EXT_IDENTITY_DST);
        }
 
+       if (flow->flow_rdomain >= 0) {
+               /* install flow in specific rdomain */
+               bzero(&sa_rdomain, sizeof(sa_rdomain));
+               sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
+               sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
+               sa_rdomain.sadb_x_rdomain_dom1 = flow->flow_rdomain;
+       }
+
        iov_cnt = 0;
 
        /* header */
@@ -427,6 +438,13 @@ pfkey_flow(int sd, uint8_t satype, uint8_t action, struct 
iked_flow *flow)
                iov_cnt++;
        }
 
+       if (flow->flow_rdomain >= 0) {
+               iov[iov_cnt].iov_base = &sa_rdomain;
+               iov[iov_cnt].iov_len = sizeof(sa_rdomain);
+               smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
+               iov_cnt++;
+       }
+
        ret = pfkey_write(sd, &smsg, iov, iov_cnt, NULL, NULL);
 
        free(sa_srcid);
@@ -447,6 +465,7 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct 
iked_childsa *sa)
        struct sadb_x_tag        sa_tag;
        char                    *tag = NULL;
        struct sadb_x_tap        sa_tap;
+       struct sadb_x_rdomain    sa_rdomain;
        struct sockaddr_storage  ssrc, sdst, spxy;
        struct sadb_ident       *sa_srcid, *sa_dstid;
        struct iked_lifetime    *lt;
@@ -532,6 +551,24 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct 
iked_childsa *sa)
        bzero(&sa_ltime_hard, sizeof(sa_ltime_hard));
        bzero(&sa_ltime_soft, sizeof(sa_ltime_soft));
 
+       if (pol->pol_rdomain >= 0) {
+               bzero(&sa_rdomain, sizeof(sa_rdomain));
+               sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
+               sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
+               if (satype == SADB_X_SATYPE_IPCOMP) {
+                       /* IPCOMP SAs are always in the pol_rdomain */
+                       sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
+                       sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
+               } else if (sa->csa_dir == IPSP_DIRECTION_OUT) {
+                       /* switch rdomain on encrypt/decrypt */
+                       sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
+                       sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
+               } else {
+                       sa_rdomain.sadb_x_rdomain_dom1 = iked_rdomain;
+                       sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
+               }
+       }
+
        if (action == SADB_DELETE)
                goto send;
 
@@ -775,6 +812,13 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct 
iked_childsa *sa)
                iov_cnt++;
        }
 
+       if (pol->pol_rdomain >= 0) {
+               iov[iov_cnt].iov_base = &sa_rdomain;
+               iov[iov_cnt].iov_len = sizeof(sa_rdomain);
+               smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
+               iov_cnt++;
+       }
+
        ret = pfkey_write(sd, &smsg, iov, iov_cnt, NULL, NULL);
 
        free(sa_srcid);
@@ -786,15 +830,17 @@ pfkey_sa(int sd, uint8_t satype, uint8_t action, struct 
iked_childsa *sa)
 int
 pfkey_sa_last_used(int sd, struct iked_childsa *sa, uint64_t *last_used)
 {
+       struct iked_policy      *pol = sa->csa_ikesa->sa_policy;
        struct sadb_msg         *msg, smsg;
        struct sadb_address      sa_src, sa_dst;
        struct sadb_sa           sadb;
+       struct sadb_x_rdomain    sa_rdomain;
        struct sadb_lifetime    *sa_life;
        struct sockaddr_storage  ssrc, sdst;
        struct iovec             iov[IOV_CNT];
        uint8_t                 *data;
        ssize_t                  n;
-       int                      iov_cnt, ret = -1;
+       int                      iov_cnt, ret = -1, rdomain;
        uint8_t                  satype;
 
        *last_used = 0;
@@ -831,6 +877,15 @@ pfkey_sa_last_used(int sd, struct iked_childsa *sa, 
uint64_t *last_used)
        sadb.sadb_sa_state = SADB_SASTATE_MATURE;
        sadb.sadb_sa_replay = 64;
 
+       if (pol->pol_rdomain >= 0) {
+               rdomain = (sa->csa_dir == IPSP_DIRECTION_IN) ?
+                   iked_rdomain : pol->pol_rdomain;
+               bzero(&sa_rdomain, sizeof(sa_rdomain));
+               sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
+               sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
+               sa_rdomain.sadb_x_rdomain_dom1 = rdomain;
+       }
+
        bzero(&sa_src, sizeof(sa_src));
        sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
        sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
@@ -870,6 +925,13 @@ pfkey_sa_last_used(int sd, struct iked_childsa *sa, 
uint64_t *last_used)
        smsg.sadb_msg_len += sa_dst.sadb_address_len;
        iov_cnt++;
 
+       if (pol->pol_rdomain >= 0) {
+               iov[iov_cnt].iov_base = &sa_rdomain;
+               iov[iov_cnt].iov_len = sizeof(sa_rdomain);
+               smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
+               iov_cnt++;
+       }
+
        if ((ret = pfkey_write(sd, &smsg, iov, iov_cnt, &data, &n)) != 0)
                return (-1);
 
@@ -1010,8 +1072,11 @@ pfkey_sagroup(int sd, uint8_t satype1, uint8_t action,
        struct sadb_address     sa_dst1, sa_dst2;
        struct sockaddr_storage sdst1, sdst2;
        struct sadb_protocol    sa_proto;
+       struct sadb_x_rdomain   sa_rdomain;
+       struct iked_policy      *pol;
        struct iovec            iov[IOV_CNT];
        int                     iov_cnt;
+       int                     group_rdomain;
        uint8_t                 satype2;
 
        if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1)
@@ -1052,6 +1117,28 @@ pfkey_sagroup(int sd, uint8_t satype1, uint8_t action,
        sadb2.sadb_sa_exttype = SADB_X_EXT_SA2;
        sadb2.sadb_sa_spi = htonl(sa2->csa_spi.spi);
        sadb2.sadb_sa_state = SADB_SASTATE_MATURE;
+
+       /* Incoming SA1 (IPCOMP) and SA2 (ESP) are in different/other rdomain */
+       group_rdomain =
+           (pol = sa1->csa_ikesa->sa_policy) != NULL &&
+           pol->pol_rdomain >= 0 &&
+           satype1 == SADB_X_SATYPE_IPCOMP &&
+           satype2 == SADB_SATYPE_ESP;
+       if (group_rdomain) {
+               bzero(&sa_rdomain, sizeof(sa_rdomain));
+               sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
+               sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
+               if (sa1->csa_dir == IPSP_DIRECTION_IN) {
+                       /* only ESP SA is iked's rdomain */
+                       sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
+                       sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
+               } else {
+                       /* both SAs are in pol_rdomain */
+                       sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
+                       sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
+               }
+       }
+
        iov_cnt = 0;
 
        bzero(&sa_dst1, sizeof(sa_dst1));
@@ -1111,6 +1198,14 @@ pfkey_sagroup(int sd, uint8_t satype1, uint8_t action,
        smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
        iov_cnt++;
 
+       /* SA1 and SA2 are from different rdomains */
+       if (group_rdomain) {
+               iov[iov_cnt].iov_base = &sa_rdomain;
+               iov[iov_cnt].iov_len = sizeof(sa_rdomain);
+               smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
+               iov_cnt++;
+       }
+
        return (pfkey_write(sd, &smsg, iov, iov_cnt, NULL, NULL));
 }
 
@@ -1467,6 +1562,8 @@ pfkey_init(struct iked *env, int fd)
        struct sadb_msg         smsg;
        struct iovec            iov;
 
+       iked_rdomain = getrtable();
+
        /* Set up a timer to process messages deferred by the pfkey_reply */
        pfkey_timer_tv.tv_sec = 1;
        pfkey_timer_tv.tv_usec = 0;
@@ -1787,6 +1884,7 @@ pfkey_process(struct iked *env, struct pfkey_message *pm)
                        goto out;
                }
                flow.flow_dir = sa_proto->sadb_protocol_direction;
+               flow.flow_rdomain = -1; /* XXX get from kernel */
 
                log_debug("%s: flow %s from %s/%s to %s/%s via %s", __func__,
                    flow.flow_dir == IPSP_DIRECTION_IN ? "in" : "out",
diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c
index 3a05d947927..51fd1cfa55c 100644
--- a/sbin/iked/policy.c
+++ b/sbin/iked/policy.c
@@ -874,6 +874,8 @@ flow_cmp(struct iked_flow *a, struct iked_flow *b)
 {
        int             diff = 0;
 
+       if (!diff)
+               diff = a->flow_rdomain - b->flow_rdomain;
        if (!diff)
                diff = (int)a->flow_ipproto - (int)b->flow_ipproto;
        if (!diff)
diff --git a/sbin/ipsecctl/pfkdump.c b/sbin/ipsecctl/pfkdump.c
index 3ab5ea17c4c..5dd99a49a19 100644
--- a/sbin/ipsecctl/pfkdump.c
+++ b/sbin/ipsecctl/pfkdump.c
@@ -56,6 +56,7 @@ static void   print_life(struct sadb_ext *, struct sadb_msg 
*, int);
 static void    print_ident(struct sadb_ext *, struct sadb_msg *, int);
 static void    print_udpenc(struct sadb_ext *, struct sadb_msg *, int);
 static void    print_tag(struct sadb_ext *, struct sadb_msg *, int);
+static void    print_rdomain(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);
@@ -106,6 +107,7 @@ struct idname ext_types[] = {
        { SADB_X_EXT_UDPENCAP,          "udpencap",             print_udpenc },
        { SADB_X_EXT_LIFETIME_LASTUSE,  "lifetime_lastuse",     print_life },
        { SADB_X_EXT_TAG,               "tag",                  print_tag },
+       { SADB_X_EXT_RDOMAIN,           "rdomain",              print_rdomain },
        { SADB_X_EXT_TAP,               "tap",                  print_tap },
        { SADB_X_EXT_SATYPE2,           "satype2",              print_satype },
        { SADB_X_EXT_COUNTER,           "counter",              print_counter },
@@ -582,6 +584,16 @@ print_udpenc(struct sadb_ext *ext, struct sadb_msg *msg, 
int opts)
        printf("udpencap port %u", ntohs(x_udpencap->sadb_x_udpencap_port));
 }
 
+/* ARGSUSED1 */
+static void
+print_rdomain(struct sadb_ext *ext, struct sadb_msg *msg, int opts)
+{
+       struct sadb_x_rdomain *srdomain = (struct sadb_x_rdomain *)ext;
+
+       printf("%d/%d", srdomain->sadb_x_rdomain_dom1,
+           srdomain->sadb_x_rdomain_dom2);
+}
+
 static void
 setup_extensions(struct sadb_msg *msg)
 {
diff --git a/sys/net/pfkeyv2.c b/sys/net/pfkeyv2.c
index 0094555edba..d48993321bd 100644
--- a/sys/net/pfkeyv2.c
+++ b/sys/net/pfkeyv2.c
@@ -855,6 +855,9 @@ pfkeyv2_get(struct tdb *tdb, void **headers, void **buffer, 
int *lenp)
        if (tdb->tdb_udpencap_port)
                i += sizeof(struct sadb_x_udpencap);
 
+       if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
+               i += sizeof(struct sadb_x_rdomain);
+
 #if NPF > 0
        if (tdb->tdb_tag)
                i += sizeof(struct sadb_x_tag) + PADUP(PF_TAG_NAME_SIZE);
@@ -945,6 +948,12 @@ pfkeyv2_get(struct tdb *tdb, void **headers, void 
**buffer, int *lenp)
                export_udpencap(&p, tdb);
        }
 
+       /* Export rdomain switch, if present */
+       if (tdb->tdb_rdomain != tdb->tdb_rdomain_post) {
+               headers[SADB_X_EXT_RDOMAIN] = p;
+               export_rdomain(&p, tdb);
+       }
+
 #if NPF > 0
        /* Export tag information, if present */
        if (tdb->tdb_tag) {
@@ -1109,7 +1118,8 @@ pfkeyv2_send(struct socket *so, void *message, int len)
        struct sadb_supported *ssup;
        struct sadb_ident *sid, *did;
        struct srp_ref sr;
-       u_int rdomain;
+       struct sadb_x_rdomain *srdomain;
+       u_int rdomain = 0;
        int promisc, s;
 
        mtx_enter(&pfkeyv2_mtx);
@@ -1155,7 +1165,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
 
                /* Send to all promiscuous listeners */
                SRPL_FOREACH(bkp, &sr, &pkptable.pkp_list, kcb_list) {
-                       if (bkp->kcb_rdomain != rdomain)
+                       if (bkp->kcb_rdomain != kp->kcb_rdomain)
                                continue;
 
                        s = keylock(bkp);
@@ -1177,6 +1187,17 @@ pfkeyv2_send(struct socket *so, void *message, int len)
        if ((rval = pfkeyv2_parsemessage(message, len, headers)) != 0)
                goto ret;
 
+       /* use specified rdomain */
+       srdomain = (struct sadb_x_rdomain *) headers[SADB_X_EXT_RDOMAIN];
+       if (srdomain) {
+               if (!rtable_exists(srdomain->sadb_x_rdomain_dom1) ||
+                   !rtable_exists(srdomain->sadb_x_rdomain_dom2)) {
+                       rval = EINVAL;
+                       goto ret;
+               }
+               rdomain = srdomain->sadb_x_rdomain_dom1;
+       }
+
        smsg = (struct sadb_msg *) headers[0];
        switch (smsg->sadb_msg_type) {
        case SADB_GETSPI:  /* Reserve an SPI */
@@ -1316,6 +1337,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
                            headers[SADB_X_EXT_PROTOCOL],
                            headers[SADB_X_EXT_FLOW_TYPE]);
                        import_udpencap(newsa, headers[SADB_X_EXT_UDPENCAP]);
+                       import_rdomain(newsa, headers[SADB_X_EXT_RDOMAIN]);
 #if NPF > 0
                        import_tag(newsa, headers[SADB_X_EXT_TAG]);
                        import_tap(newsa, headers[SADB_X_EXT_TAP]);
@@ -1486,6 +1508,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
                            headers[SADB_X_EXT_PROTOCOL],
                            headers[SADB_X_EXT_FLOW_TYPE]);
                        import_udpencap(newsa, headers[SADB_X_EXT_UDPENCAP]);
+                       import_rdomain(newsa, headers[SADB_X_EXT_RDOMAIN]);
 #if NPF > 0
                        import_tag(newsa, headers[SADB_X_EXT_TAG]);
                        import_tap(newsa, headers[SADB_X_EXT_TAP]);
@@ -1720,7 +1743,9 @@ pfkeyv2_send(struct socket *so, void *message, int len)
                    sizeof(struct sadb_address));
                sa_proto = (struct sadb_protocol *) headers[SADB_X_EXT_SATYPE2];
 
-               tdb2 = gettdb(rdomain, ssa->sadb_sa_spi, sunionp,
+               /* optionally fetch tdb2 from rdomain2 */
+               tdb2 = gettdb(srdomain ? srdomain->sadb_x_rdomain_dom2 : 
rdomain,
+                   ssa->sadb_sa_spi, sunionp,
                    SADB_X_GETSPROTO(sa_proto->sadb_protocol_proto));
                if (tdb2 == NULL) {
                        rval = ESRCH;
@@ -1983,7 +2008,7 @@ pfkeyv2_send(struct socket *so, void *message, int len)
                                goto ret;
 
                        SRPL_FOREACH(bkp, &sr, &pkptable.pkp_list, kcb_list) {
-                               if (bkp == kp || bkp->kcb_rdomain != rdomain)
+                               if (bkp == kp || bkp->kcb_rdomain != 
kp->kcb_rdomain)
                                        continue;
 
                                if (!smsg->sadb_msg_seq ||
@@ -2061,7 +2086,7 @@ ret:
                }
        }
 
-       rval = pfkeyv2_sendmessage(headers, mode, so, 0, 0, rdomain);
+       rval = pfkeyv2_sendmessage(headers, mode, so, 0, 0, kp->kcb_rdomain);
 
 realret:
 
@@ -2360,6 +2385,12 @@ pfkeyv2_expire(struct tdb *tdb, u_int16_t type)
        if ((rval = pfkeyv2_sendmessage(headers, PFKEYV2_SENDMESSAGE_BROADCAST,
            NULL, 0, 0, tdb->tdb_rdomain)) != 0)
                goto ret;
+       /* XXX */
+       if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
+               if ((rval = pfkeyv2_sendmessage(headers,
+                   PFKEYV2_SENDMESSAGE_BROADCAST, NULL, 0, 0,
+                   tdb->tdb_rdomain_post)) != 0)
+                       goto ret;
 
        rval = 0;
 
@@ -2620,6 +2651,7 @@ pfkeyv2_sysctl(int *name, u_int namelen, void *oldp, 
size_t *oldlenp,
        struct pfkeyv2_sysctl_walk w;
        int error = EINVAL;
        u_int rdomain;
+       u_int tableid;
 
        if (new)
                return (EPERM);
@@ -2630,7 +2662,13 @@ pfkeyv2_sysctl(int *name, u_int namelen, void *oldp, 
size_t *oldlenp,
        w.w_where = oldp;
        w.w_len = oldp ? *oldlenp : 0;
 
-       rdomain = rtable_l2(curproc->p_p->ps_rtableid);
+       if (namelen == 3) {
+               tableid = name[2];
+               if (!rtable_exists(tableid))
+                       return (ENOENT);
+       } else
+               tableid = curproc->p_p->ps_rtableid;
+       rdomain = rtable_l2(tableid);
 
        switch(w.w_op) {
        case NET_KEY_SADB_DUMP:
diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h
index 9c31163e95e..4e0f71e0dcd 100644
--- a/sys/net/pfkeyv2.h
+++ b/sys/net/pfkeyv2.h
@@ -212,6 +212,13 @@ struct sadb_x_tag {
        u_int32_t sadb_x_tag_taglen;
 };
 
+struct sadb_x_rdomain {
+       uint16_t  sadb_x_rdomain_len;
+       uint16_t  sadb_x_rdomain_exttype;
+       uint16_t  sadb_x_rdomain_dom1;
+       uint16_t  sadb_x_rdomain_dom2;
+};
+
 struct sadb_x_tap {
        uint16_t  sadb_x_tap_len;
        uint16_t  sadb_x_tap_exttype;
@@ -277,7 +284,8 @@ struct sadb_x_counter {
 #define SADB_X_EXT_TAP                34
 #define SADB_X_EXT_SATYPE2            35
 #define SADB_X_EXT_COUNTER            36
-#define SADB_EXT_MAX                  36
+#define SADB_X_EXT_RDOMAIN            37
+#define SADB_EXT_MAX                  37
 
 /* Fix pfkeyv2.c struct pfkeyv2_socket if SATYPE_MAX > 31 */
 #define SADB_SATYPE_UNSPEC              0
@@ -409,6 +417,7 @@ void export_flow(void **, u_int8_t, struct sockaddr_encap *,
 void export_key(void **, struct tdb *, int);
 void export_udpencap(void **, struct tdb *);
 void export_tag(void **, struct tdb *);
+void export_rdomain(void **, struct tdb *);
 void export_tap(void **, struct tdb *);
 void export_satype(void **, struct tdb *);
 void export_counter(void **, struct tdb *);
@@ -424,6 +433,7 @@ void import_flow(struct sockaddr_encap *, struct 
sockaddr_encap *,
     struct sadb_address *, struct sadb_protocol *, struct sadb_protocol *);
 void import_udpencap(struct tdb *, struct sadb_x_udpencap *);
 void import_tag(struct tdb *, struct sadb_x_tag *);
+void import_rdomain(struct tdb *, struct sadb_x_rdomain *);
 void import_tap(struct tdb *, struct sadb_x_tap *);
 #endif /* _KERNEL */
 
diff --git a/sys/net/pfkeyv2_convert.c b/sys/net/pfkeyv2_convert.c
index c72bc152fa8..c3a53b35c80 100644
--- a/sys/net/pfkeyv2_convert.c
+++ b/sys/net/pfkeyv2_convert.c
@@ -840,6 +840,27 @@ export_udpencap(void **p, struct tdb *tdb)
        *p += sizeof(struct sadb_x_udpencap);
 }
 
+/* Import rdomain switch for SA */
+void
+import_rdomain(struct tdb *tdb, struct sadb_x_rdomain *srdomain)
+{
+       if (srdomain)
+               tdb->tdb_rdomain_post = srdomain->sadb_x_rdomain_dom2;
+}
+
+/* Export rdomain switch for SA */
+void
+export_rdomain(void **p, struct tdb *tdb)
+{
+       struct sadb_x_rdomain *srdomain = (struct sadb_x_rdomain *)*p;
+
+       srdomain->sadb_x_rdomain_dom1 = tdb->tdb_rdomain;
+       srdomain->sadb_x_rdomain_dom2 = tdb->tdb_rdomain_post;
+       srdomain->sadb_x_rdomain_len =
+           sizeof(struct sadb_x_rdomain) / sizeof(uint64_t);
+       *p += sizeof(struct sadb_x_rdomain);
+}
+
 #if NPF > 0
 /* Import PF tag information for SA */
 void
diff --git a/sys/net/pfkeyv2_parsemessage.c b/sys/net/pfkeyv2_parsemessage.c
index 2d351addd6e..20bd8960714 100644
--- a/sys/net/pfkeyv2_parsemessage.c
+++ b/sys/net/pfkeyv2_parsemessage.c
@@ -126,6 +126,7 @@
 #define BITMAP_X_TAG                   (1LL << SADB_X_EXT_TAG)
 #define BITMAP_X_TAP                   (1LL << SADB_X_EXT_TAP)
 #define BITMAP_X_SATYPE2               (1LL << SADB_X_EXT_SATYPE2)
+#define BITMAP_X_RDOMAIN               (1LL << SADB_X_EXT_RDOMAIN)
 #define BITMAP_X_COUNTER               (1LL << SADB_X_EXT_COUNTER)
 
 uint64_t sadb_exts_allowed_in[SADB_MAX+1] =
@@ -135,13 +136,13 @@ uint64_t sadb_exts_allowed_in[SADB_MAX+1] =
        /* GETSPI */
        BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_SPIRANGE,
        /* UPDATE */
-       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | 
BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG 
| BITMAP_X_TAP,
+       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | 
BITMAP_KEY | BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG 
| BITMAP_X_TAP | BITMAP_X_RDOMAIN,
        /* ADD */
-       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | 
BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE 
| BITMAP_X_TAG | BITMAP_X_TAP,
+       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | 
BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE 
| BITMAP_X_TAG | BITMAP_X_TAP | BITMAP_X_RDOMAIN,
        /* DELETE */
-       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
+       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
        /* GET */
-       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
+       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
        /* ACQUIRE */
        BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | 
BITMAP_PROPOSAL,
        /* REGISTER */
@@ -155,11 +156,11 @@ uint64_t sadb_exts_allowed_in[SADB_MAX+1] =
        /* X_PROMISC */
        0,
        /* X_ADDFLOW */
-       BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY_SRC | 
BITMAP_IDENTITY_DST | BITMAP_X_FLOW,
+       BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY_SRC | 
BITMAP_IDENTITY_DST | BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
        /* X_DELFLOW */
-       BITMAP_X_FLOW,
+       BITMAP_X_FLOW | BITMAP_X_RDOMAIN,
        /* X_GRPSPIS */
-       BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | 
BITMAP_X_SATYPE2,
+       BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | 
BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
        /* X_ASKPOLICY */
        BITMAP_X_POLICY,
 };
@@ -207,13 +208,13 @@ uint64_t sadb_exts_allowed_out[SADB_MAX+1] =
        /* GETSPI */
        BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
        /* UPDATE */
-       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | 
BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | 
BITMAP_X_TAP,
+       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_ADDRESS_PROXY | 
BITMAP_IDENTITY | BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | 
BITMAP_X_TAP | BITMAP_X_RDOMAIN,
        /* ADD */
-       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY | 
BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP,
+       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_IDENTITY | 
BITMAP_X_FLOW | BITMAP_X_UDPENCAP | BITMAP_X_TAG | BITMAP_X_TAP | 
BITMAP_X_RDOMAIN,
        /* DELETE */
-       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST,
+       BITMAP_SA | BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_RDOMAIN,
        /* GET */
-       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | 
BITMAP_IDENTITY | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | 
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_FLOW_TYPE 
| BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_TAG | BITMAP_X_TAP | 
BITMAP_X_COUNTER,
+       BITMAP_SA | BITMAP_LIFETIME | BITMAP_ADDRESS | BITMAP_KEY | 
BITMAP_IDENTITY | BITMAP_X_UDPENCAP | BITMAP_X_LIFETIME_LASTUSE | 
BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_FLOW_TYPE 
| BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_TAG | BITMAP_X_TAP | 
BITMAP_X_COUNTER | BITMAP_X_RDOMAIN,
        /* ACQUIRE */
        BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_IDENTITY | 
BITMAP_PROPOSAL,
        /* REGISTER */
@@ -227,11 +228,11 @@ uint64_t sadb_exts_allowed_out[SADB_MAX+1] =
        /* X_PROMISC */
        0,
        /* X_ADDFLOW */
-       BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_SRC_MASK | 
BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | 
BITMAP_X_FLOW_TYPE | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST,
+       BITMAP_ADDRESS_SRC | BITMAP_ADDRESS_DST | BITMAP_X_SRC_MASK | 
BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | 
BITMAP_X_FLOW_TYPE | BITMAP_IDENTITY_SRC | BITMAP_IDENTITY_DST | 
BITMAP_X_RDOMAIN,
        /* X_DELFLOW */
-       BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | 
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE,
+       BITMAP_X_SRC_MASK | BITMAP_X_DST_MASK | BITMAP_X_PROTOCOL | 
BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_FLOW_TYPE | BITMAP_X_RDOMAIN,
        /* X_GRPSPIS */
-       BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | 
BITMAP_X_SATYPE2,
+       BITMAP_SA | BITMAP_X_SA2 | BITMAP_X_DST2 | BITMAP_ADDRESS_DST | 
BITMAP_X_SATYPE2 | BITMAP_X_RDOMAIN,
        /* X_ASKPOLICY */
        BITMAP_X_SRC_FLOW | BITMAP_X_DST_FLOW | BITMAP_X_SRC_MASK | 
BITMAP_X_DST_MASK | BITMAP_X_FLOW_TYPE | BITMAP_X_POLICY,
 };
@@ -879,6 +880,13 @@ pfkeyv2_parsemessage(void *p, int len, void **headers)
                                return (EINVAL);
                        }
                        break;
+               case SADB_X_EXT_RDOMAIN:
+                       if (i != sizeof(struct sadb_x_rdomain)) {
+                               DPRINTF(("pfkeyv2_parsemessage: bad RDOMAIN "
+                                   "header length\n"));
+                               return (EINVAL);
+                       }
+                       break;
 #if NPF > 0
                case SADB_X_EXT_TAG:
                        if (i < sizeof(struct sadb_x_tag)) {
diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c
index c27cabc82d3..32da310545e 100644
--- a/sys/netinet/ip_ipsp.c
+++ b/sys/netinet/ip_ipsp.c
@@ -84,7 +84,7 @@ void          tdb_timeout(void *);
 void           tdb_firstuse(void *);
 void           tdb_soft_timeout(void *);
 void           tdb_soft_firstuse(void *);
-int            tdb_hash(u_int, u_int32_t, union sockaddr_union *, u_int8_t);
+int            tdb_hash(u_int32_t, union sockaddr_union *, u_int8_t);
 
 int ipsec_in_use = 0;
 u_int64_t ipsec_last_added = 0;
@@ -185,7 +185,7 @@ static int tdb_count;
  * so we cannot be DoS-attacked via choosing of the data to hash.
  */
 int
-tdb_hash(u_int rdomain, u_int32_t spi, union sockaddr_union *dst,
+tdb_hash(u_int32_t spi, union sockaddr_union *dst,
     u_int8_t proto)
 {
        SIPHASH_CTX ctx;
@@ -193,7 +193,6 @@ tdb_hash(u_int rdomain, u_int32_t spi, union sockaddr_union 
*dst,
        NET_ASSERT_LOCKED();
 
        SipHash24_Init(&ctx, &tdbkey);
-       SipHash24_Update(&ctx, &rdomain, sizeof(rdomain));
        SipHash24_Update(&ctx, &spi, sizeof(spi));
        SipHash24_Update(&ctx, &proto, sizeof(proto));
        SipHash24_Update(&ctx, dst, dst->sa.sa_len);
@@ -306,7 +305,8 @@ reserve_spi(u_int rdomain, u_int32_t sspi, u_int32_t tspi,
  * is really one of our addresses if we received the packet!
  */
 struct tdb *
-gettdb(u_int rdomain, u_int32_t spi, union sockaddr_union *dst, u_int8_t proto)
+gettdb_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *dst, u_int8_t 
proto,
+    int reverse)
 {
        u_int32_t hashval;
        struct tdb *tdbp;
@@ -316,11 +316,12 @@ gettdb(u_int rdomain, u_int32_t spi, union sockaddr_union 
*dst, u_int8_t proto)
        if (tdbh == NULL)
                return (struct tdb *) NULL;
 
-       hashval = tdb_hash(rdomain, spi, dst, proto);
+       hashval = tdb_hash(spi, dst, proto);
 
        for (tdbp = tdbh[hashval]; tdbp != NULL; tdbp = tdbp->tdb_hnext)
                if ((tdbp->tdb_spi == spi) && (tdbp->tdb_sproto == proto) &&
-                   (tdbp->tdb_rdomain == rdomain) &&
+                   ((!reverse && tdbp->tdb_rdomain == rdomain) ||
+                   (reverse && tdbp->tdb_rdomain_post == rdomain)) &&
                    !memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len))
                        break;
 
@@ -333,8 +334,8 @@ gettdb(u_int rdomain, u_int32_t spi, union sockaddr_union 
*dst, u_int8_t proto)
  * matches all SPIs.
  */
 struct tdb *
-gettdbbysrcdst(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
-    union sockaddr_union *dst, u_int8_t proto)
+gettdbbysrcdst_dir(u_int rdomain, u_int32_t spi, union sockaddr_union *src,
+    union sockaddr_union *dst, u_int8_t proto, int reverse)
 {
        u_int32_t hashval;
        struct tdb *tdbp;
@@ -345,12 +346,13 @@ gettdbbysrcdst(u_int rdomain, u_int32_t spi, union 
sockaddr_union *src,
        if (tdbsrc == NULL)
                return (struct tdb *) NULL;
 
-       hashval = tdb_hash(rdomain, 0, src, proto);
+       hashval = tdb_hash(0, src, proto);
 
        for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext)
                if (tdbp->tdb_sproto == proto &&
                    (spi == 0 || tdbp->tdb_spi == spi) &&
-                   (tdbp->tdb_rdomain == rdomain) &&
+                   ((!reverse && tdbp->tdb_rdomain == rdomain) ||
+                   (reverse && tdbp->tdb_rdomain_post == rdomain)) &&
                    ((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
                    (tdbp->tdb_dst.sa.sa_family == AF_UNSPEC ||
                    !memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len)) &&
@@ -362,12 +364,13 @@ gettdbbysrcdst(u_int rdomain, u_int32_t spi, union 
sockaddr_union *src,
 
        memset(&su_null, 0, sizeof(su_null));
        su_null.sa.sa_len = sizeof(struct sockaddr);
-       hashval = tdb_hash(rdomain, 0, &su_null, proto);
+       hashval = tdb_hash(0, &su_null, proto);
 
        for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext)
                if (tdbp->tdb_sproto == proto &&
                    (spi == 0 || tdbp->tdb_spi == spi) &&
-                   (tdbp->tdb_rdomain == rdomain) &&
+                   ((!reverse && tdbp->tdb_rdomain == rdomain) ||
+                   (reverse && tdbp->tdb_rdomain_post == rdomain)) &&
                    ((tdbp->tdb_flags & TDBF_INVALID) == 0) &&
                    (tdbp->tdb_dst.sa.sa_family == AF_UNSPEC ||
                    !memcmp(&tdbp->tdb_dst, dst, dst->sa.sa_len)) &&
@@ -431,7 +434,7 @@ gettdbbydst(u_int rdomain, union sockaddr_union *dst, 
u_int8_t sproto,
        if (tdbdst == NULL)
                return (struct tdb *) NULL;
 
-       hashval = tdb_hash(rdomain, 0, dst, sproto);
+       hashval = tdb_hash(0, dst, sproto);
 
        for (tdbp = tdbdst[hashval]; tdbp != NULL; tdbp = tdbp->tdb_dnext)
                if ((tdbp->tdb_sproto == sproto) &&
@@ -464,7 +467,7 @@ gettdbbysrc(u_int rdomain, union sockaddr_union *src, 
u_int8_t sproto,
        if (tdbsrc == NULL)
                return (struct tdb *) NULL;
 
-       hashval = tdb_hash(rdomain, 0, src, sproto);
+       hashval = tdb_hash(0, src, sproto);
 
        for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext)
                if ((tdbp->tdb_sproto == sproto) &&
@@ -620,8 +623,7 @@ tdb_rehash(void)
        for (i = 0; i <= old_hashmask; i++) {
                for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbnp) {
                        tdbnp = tdbp->tdb_hnext;
-                       hashval = tdb_hash(tdbp->tdb_rdomain,
-                           tdbp->tdb_spi, &tdbp->tdb_dst,
+                       hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst,
                            tdbp->tdb_sproto);
                        tdbp->tdb_hnext = new_tdbh[hashval];
                        new_tdbh[hashval] = tdbp;
@@ -629,18 +631,14 @@ tdb_rehash(void)
 
                for (tdbp = tdbdst[i]; tdbp != NULL; tdbp = tdbnp) {
                        tdbnp = tdbp->tdb_dnext;
-                       hashval = tdb_hash(tdbp->tdb_rdomain,
-                           0, &tdbp->tdb_dst,
-                           tdbp->tdb_sproto);
+                       hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
                        tdbp->tdb_dnext = new_tdbdst[hashval];
                        new_tdbdst[hashval] = tdbp;
                }
 
                for (tdbp = tdbsrc[i]; tdbp != NULL; tdbp = tdbnp) {
                        tdbnp = tdbp->tdb_snext;
-                       hashval = tdb_hash(tdbp->tdb_rdomain,
-                           0, &tdbp->tdb_src,
-                           tdbp->tdb_sproto);
+                       hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
                        tdbp->tdb_snext = new_srcaddr[hashval];
                        new_srcaddr[hashval] = tdbp;
                }
@@ -676,8 +674,7 @@ puttdb(struct tdb *tdbp)
                    M_TDB, M_WAITOK | M_ZERO);
        }
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_spi,
-           &tdbp->tdb_dst, tdbp->tdb_sproto);
+       hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto);
 
        /*
         * Rehash if this tdb would cause a bucket to have more than
@@ -690,20 +687,18 @@ puttdb(struct tdb *tdbp)
        if (tdbh[hashval] != NULL && tdbh[hashval]->tdb_hnext != NULL &&
            tdb_count * 10 > tdb_hashmask + 1) {
                tdb_rehash();
-               hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_spi,
-                   &tdbp->tdb_dst, tdbp->tdb_sproto);
+               hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst,
+                   tdbp->tdb_sproto);
        }
 
        tdbp->tdb_hnext = tdbh[hashval];
        tdbh[hashval] = tdbp;
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_dst,
-           tdbp->tdb_sproto);
+       hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
        tdbp->tdb_dnext = tdbdst[hashval];
        tdbdst[hashval] = tdbp;
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_src,
-           tdbp->tdb_sproto);
+       hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
        tdbp->tdb_snext = tdbsrc[hashval];
        tdbsrc[hashval] = tdbp;
 
@@ -727,8 +722,7 @@ tdb_unlink(struct tdb *tdbp)
        if (tdbh == NULL)
                return;
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, tdbp->tdb_spi,
-           &tdbp->tdb_dst, tdbp->tdb_sproto);
+       hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto);
 
        if (tdbh[hashval] == tdbp) {
                tdbh[hashval] = tdbp->tdb_hnext;
@@ -744,8 +738,7 @@ tdb_unlink(struct tdb *tdbp)
 
        tdbp->tdb_hnext = NULL;
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_dst,
-           tdbp->tdb_sproto);
+       hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto);
 
        if (tdbdst[hashval] == tdbp) {
                tdbdst[hashval] = tdbp->tdb_dnext;
@@ -761,8 +754,7 @@ tdb_unlink(struct tdb *tdbp)
 
        tdbp->tdb_dnext = NULL;
 
-       hashval = tdb_hash(tdbp->tdb_rdomain, 0, &tdbp->tdb_src,
-           tdbp->tdb_sproto);
+       hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto);
 
        if (tdbsrc[hashval] == tdbp) {
                tdbsrc[hashval] = tdbp->tdb_snext;
@@ -816,6 +808,7 @@ tdb_alloc(u_int rdomain)
 
        /* Save routing domain */
        tdbp->tdb_rdomain = rdomain;
+       tdbp->tdb_rdomain_post = rdomain;
 
        /* Initialize timeouts. */
        timeout_set_proc(&tdbp->tdb_timer_tmo, tdb_timeout, tdbp);
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index 49c9d661d57..0917af9f35c 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -409,6 +409,7 @@ struct tdb {                                /* tunnel 
descriptor block */
        u_int32_t       tdb_tap;                /* Alternate enc(4) interface */
 
        u_int           tdb_rdomain;            /* Routing domain */
+       u_int           tdb_rdomain_post;       /* Change domain */
 
        struct sockaddr_encap   tdb_filter; /* What traffic is acceptable */
        struct sockaddr_encap   tdb_filtermask; /* And the mask */
@@ -574,15 +575,19 @@ int spd_table_walk(unsigned int,
 /* TDB management routines */
 uint32_t reserve_spi(u_int, u_int32_t, u_int32_t, union sockaddr_union *,
                union sockaddr_union *, u_int8_t, int *);
-struct tdb *gettdb(u_int, u_int32_t, union sockaddr_union *, u_int8_t);
+struct tdb *gettdb_dir(u_int, u_int32_t, union sockaddr_union *, u_int8_t, 
int);
+#define gettdb(a,b,c,d)                gettdb_dir((a),(b),(c),(d),0)
+#define gettdb_rev(a,b,c,d)    gettdb_dir((a),(b),(c),(d),1)
 struct tdb *gettdbbydst(u_int, union sockaddr_union *, u_int8_t,
                struct ipsec_ids *,
                struct sockaddr_encap *, struct sockaddr_encap *);
 struct tdb *gettdbbysrc(u_int, union sockaddr_union *, u_int8_t,
                struct ipsec_ids *,
                struct sockaddr_encap *, struct sockaddr_encap *);
-struct tdb *gettdbbysrcdst(u_int, u_int32_t, union sockaddr_union *,
-               union sockaddr_union *, u_int8_t);
+struct tdb *gettdbbysrcdst_dir(u_int, u_int32_t, union sockaddr_union *,
+               union sockaddr_union *, u_int8_t, int);
+#define gettdbbysrcdst(a,b,c,d,e) gettdbbysrcdst_dir((a),(b),(c),(d),(e),0)
+#define gettdbbysrcdst_rev(a,b,c,d,e) gettdbbysrcdst_dir((a),(b),(c),(d),(e),1)
 void   puttdb(struct tdb *);
 void   tdb_delete(struct tdb *);
 struct tdb *tdb_alloc(u_int);
diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c
index b6d0083af40..fcf4ba3d309 100644
--- a/sys/netinet/ipsec_input.c
+++ b/sys/netinet/ipsec_input.c
@@ -299,7 +299,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, 
int af, int sproto,
        }
 
        if (sproto != IPPROTO_IPCOMP) {
-               if ((encif = enc_getif(tdbp->tdb_rdomain,
+               if ((encif = enc_getif(tdbp->tdb_rdomain_post,
                    tdbp->tdb_tap)) == NULL) {
                        DPRINTF(("%s: no enc%u interface for SA %s/%08x/%u\n",
                            __func__,
@@ -657,6 +657,8 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int 
skip, int protoff)
        pf_tag_packet(m, tdbp->tdb_tag, -1);
        pf_pkt_addr_changed(m);
 #endif
+       if (tdbp->tdb_rdomain != tdbp->tdb_rdomain_post)
+               m->m_pkthdr.ph_rtableid = tdbp->tdb_rdomain_post;
 
        if (tdbp->tdb_flags & TDBF_TUNNELING)
                m->m_flags |= M_TUNNEL;
@@ -665,7 +667,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int 
skip, int protoff)
        tdbp->tdb_idecompbytes += m->m_pkthdr.len;
 
 #if NBPFILTER > 0
-       if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) != NULL) {
+       if ((encif = enc_getif(tdbp->tdb_rdomain_post, tdbp->tdb_tap)) != NULL) 
{
                encif->if_ipackets++;
                encif->if_ibytes += m->m_pkthdr.len;
 
@@ -966,7 +968,7 @@ ipsec_common_ctlinput(u_int rdomain, int cmd, struct 
sockaddr *sa,
 
                memcpy(&spi, (caddr_t)ip + hlen, sizeof(u_int32_t));
 
-               tdbp = gettdb(rdomain, spi, (union sockaddr_union *)&dst,
+               tdbp = gettdb_rev(rdomain, spi, (union sockaddr_union *)&dst,
                    proto);
                if (tdbp == NULL || tdbp->tdb_flags & TDBF_INVALID)
                        return;
@@ -1025,7 +1027,8 @@ udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int 
rdomain, void *v)
        src.sin_addr.s_addr = ip->ip_src.s_addr;
        su_src = (union sockaddr_union *)&src;
 
-       tdbp = gettdbbysrcdst(rdomain, 0, su_src, su_dst, IPPROTO_ESP);
+       tdbp = gettdbbysrcdst_rev(rdomain, 0, su_src, su_dst,
+           IPPROTO_ESP);
 
        for (; tdbp != NULL; tdbp = tdbp->tdb_snext) {
                if (tdbp->tdb_sproto == IPPROTO_ESP &&
diff --git a/sys/netinet/ipsec_output.c b/sys/netinet/ipsec_output.c
index 28ff5b92781..436998f097d 100644
--- a/sys/netinet/ipsec_output.c
+++ b/sys/netinet/ipsec_output.c
@@ -592,6 +592,8 @@ ipsp_process_done(struct mbuf *m, struct tdb *tdb)
        pf_tag_packet(m, tdb->tdb_tag, -1);
        pf_pkt_addr_changed(m);
 #endif
+       if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
+               m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
 
        /*
         * We're done with IPsec processing, transmit the packet using the


Reply via email to