config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
        int "NSA SELinux maximum supported policy format version value"
        depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
-       range 15 20
+       range 15 21
        default 19
        help
          This option sets the value for the maximum policy format version
 
 #define POLICYDB_VERSION_VALIDATETRANS 19
 #define POLICYDB_VERSION_MLS           19
 #define POLICYDB_VERSION_AVTAB         20
+#define POLICYDB_VERSION_RANGETRANS    21
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
 #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
 #define POLICYDB_VERSION_MAX   CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
 #else
-#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_AVTAB
+#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_RANGETRANS
 #endif
 
 extern int selinux_enabled;
 
                    u32 specified,
                    struct context *newcontext)
 {
+       struct range_trans *rtr;
+
        if (!selinux_mls_enabled)
                return 0;
 
        switch (specified) {
        case AVTAB_TRANSITION:
-               if (tclass == SECCLASS_PROCESS) {
-                       struct range_trans *rangetr;
-                       /* Look for a range transition rule. */
-                       for (rangetr = policydb.range_tr; rangetr;
-                            rangetr = rangetr->next) {
-                               if (rangetr->dom == scontext->type &&
-                                   rangetr->type == tcontext->type) {
-                                       /* Set the range from the rule */
-                                       return mls_range_set(newcontext,
-                                                            &rangetr->range);
-                               }
+               /* Look for a range transition rule. */
+               for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
+                       if (rtr->source_type == scontext->type &&
+                           rtr->target_type == tcontext->type &&
+                           rtr->target_class == tclass) {
+                               /* Set the range from the rule */
+                               return mls_range_set(newcontext,
+                                                    &rtr->target_range);
                        }
                }
                /* Fallthrough */
 
                .sym_num        = SYM_NUM,
                .ocon_num       = OCON_NUM,
        },
+       {
+               .version        = POLICYDB_VERSION_RANGETRANS,
+               .sym_num        = SYM_NUM,
+               .ocon_num       = OCON_NUM,
+       },
 };
 
 static struct policydb_compat_info *policydb_lookup_compat(int version)
 
        for (rt = p->range_tr; rt; rt = rt -> next) {
                if (lrt) {
-                       ebitmap_destroy(&lrt->range.level[0].cat);
-                       ebitmap_destroy(&lrt->range.level[1].cat);
+                       ebitmap_destroy(&lrt->target_range.level[0].cat);
+                       ebitmap_destroy(&lrt->target_range.level[1].cat);
                        kfree(lrt);
                }
                lrt = rt;
        }
        if (lrt) {
-               ebitmap_destroy(&lrt->range.level[0].cat);
-               ebitmap_destroy(&lrt->range.level[1].cat);
+               ebitmap_destroy(&lrt->target_range.level[0].cat);
+               ebitmap_destroy(&lrt->target_range.level[1].cat);
                kfree(lrt);
        }
 
        }
 
        if (p->policyvers >= POLICYDB_VERSION_MLS) {
+               int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS;
                rc = next_entry(buf, fp, sizeof(u32));
                if (rc < 0)
                        goto bad;
                        rc = next_entry(buf, fp, (sizeof(u32) * 2));
                        if (rc < 0)
                                goto bad;
-                       rt->dom = le32_to_cpu(buf[0]);
-                       rt->type = le32_to_cpu(buf[1]);
-                       rc = mls_read_range_helper(&rt->range, fp);
+                       rt->source_type = le32_to_cpu(buf[0]);
+                       rt->target_type = le32_to_cpu(buf[1]);
+                       if (new_rangetr) {
+                               rc = next_entry(buf, fp, sizeof(u32));
+                               if (rc < 0)
+                                       goto bad;
+                               rt->target_class = le32_to_cpu(buf[0]);
+                       } else
+                               rt->target_class = SECCLASS_PROCESS;
+                       rc = mls_read_range_helper(&rt->target_range, fp);
                        if (rc)
                                goto bad;
                        lrt = rt;
 
 };
 
 struct range_trans {
-       u32 dom;                        /* current process domain */
-       u32 type;                       /* program executable type */
-       struct mls_range range;         /* new range */
+       u32 source_type;
+       u32 target_type;
+       u32 target_class;
+       struct mls_range target_range;
        struct range_trans *next;
 };