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 ;)

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