#define perr(_ys, _msg)                        __yerr(&(_ys)->err, errno, _msg)
 
 /* -- Netlink boiler plate */
+static bool
+ynl_err_walk_is_sel(const struct ynl_policy_nest *policy,
+                   const struct nlattr *attr)
+{
+       unsigned int type = ynl_attr_type(attr);
+
+       return policy && type <= policy->max_attr &&
+               policy->table[type].is_selector;
+}
+
+static const struct ynl_policy_nest *
+ynl_err_walk_sel_policy(const struct ynl_policy_attr *policy_attr,
+                       const struct nlattr *selector)
+{
+       const struct ynl_policy_nest *policy = policy_attr->nest;
+       const char *sel;
+       unsigned int i;
+
+       if (!policy_attr->is_submsg)
+               return policy;
+
+       sel = ynl_attr_get_str(selector);
+       for (i = 0; i <= policy->max_attr; i++) {
+               if (!strcmp(sel, policy->table[i].name))
+                       return policy->table[i].nest;
+       }
+
+       return NULL;
+}
+
 static int
-ynl_err_walk_report_one(const struct ynl_policy_nest *policy, unsigned int type,
+ynl_err_walk_report_one(const struct ynl_policy_nest *policy,
+                       const struct nlattr *selector, unsigned int type,
                        char *str, int str_sz, int *n)
 {
        if (!policy) {
                return 1;
        }
 
-       if (*n < str_sz)
-               *n += snprintf(str, str_sz - *n,
-                              ".%s", policy->table[type].name);
+       if (*n < str_sz) {
+               int sz;
+
+               sz = snprintf(str, str_sz - *n,
+                             ".%s", policy->table[type].name);
+               *n += sz;
+               str += sz;
+       }
+
+       if (policy->table[type].is_submsg) {
+               if (!selector) {
+                       if (*n < str_sz)
+                               *n += snprintf(str, str_sz, "(!selector)");
+                       return 1;
+               }
+
+               if (ynl_attr_type(selector) !=
+                   policy->table[type].selector_type) {
+                       if (*n < str_sz)
+                               *n += snprintf(str, str_sz, "(!=selector)");
+                       return 1;
+               }
+
+               if (*n < str_sz)
+                       *n += snprintf(str, str_sz - *n, "(%s)",
+                                      ynl_attr_get_str(selector));
+       }
+
        return 0;
 }
 
             const struct ynl_policy_nest *policy, char *str, int str_sz,
             const struct ynl_policy_nest **nest_pol)
 {
+       const struct ynl_policy_nest *next_pol;
+       const struct nlattr *selector = NULL;
        unsigned int astart_off, aend_off;
        const struct nlattr *attr;
        unsigned int data_len;
        ynl_attr_for_each_payload(start, data_len, attr) {
                astart_off = (char *)attr - (char *)start;
                aend_off = (char *)ynl_attr_data_end(attr) - (char *)start;
+
+               if (ynl_err_walk_is_sel(policy, attr))
+                       selector = attr;
+
                if (aend_off <= off)
                        continue;
 
 
        type = ynl_attr_type(attr);
 
-       if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
+       if (ynl_err_walk_report_one(policy, selector, type, str, str_sz, &n))
+               return n;
+
+       next_pol = ynl_err_walk_sel_policy(&policy->table[type], selector);
+       if (!next_pol)
                return n;
 
        if (!off) {
                if (nest_pol)
-                       *nest_pol = policy->table[type].nest;
+                       *nest_pol = next_pol;
                return n;
        }
 
-       if (!policy->table[type].nest) {
+       if (!next_pol) {
                if (n < str_sz)
                        n += snprintf(str, str_sz, "!nest");
                return n;
        start =  ynl_attr_data(attr);
        end = start + ynl_attr_data_len(attr);
 
-       return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
+       return n + ynl_err_walk(ys, start, end, off, next_pol,
                                &str[n], str_sz - n, nest_pol);
 }
 
                }
 
                n2 = 0;
-               ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
+               ynl_err_walk_report_one(nest_pol, NULL, type, &miss_attr[n],
                                        sizeof(miss_attr) - n, &n2);
                n += n2;
 
 
         self.request = False
         self.reply = False
 
+        self.is_selector = False
+
         if 'len' in attr:
             self.len = attr['len']
 
         ri.cw.p(f"char *{self.c_name};")
 
     def _attr_typol(self):
-        return f'.type = YNL_PT_NUL_STR, '
+        typol = f'.type = YNL_PT_NUL_STR, '
+        if self.is_selector:
+            typol += '.is_selector = 1, '
+        return typol
 
     def _attr_policy(self, policy):
         if 'exact-len' in self.checks:
 
 
 class TypeSubMessage(TypeNest):
+    def __init__(self, family, attr_set, attr, value):
+        super().__init__(family, attr_set, attr, value)
+
+        self.selector = Selector(attr, attr_set)
+
+    def _attr_typol(self):
+        typol = f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
+        typol += f'.is_submsg = 1, .selector_type = {self.attr_set[self["selector"]].value} '
+        return typol
+
     def _attr_get(self, ri, var):
         sel = c_lower(self['selector'])
         get_lines = [f'if (!{var}->{sel})',
         return get_lines, init_lines, None
 
 
+class Selector:
+    def __init__(self, msg_attr, attr_set):
+        self.name = msg_attr["selector"]
+
+        if self.name in attr_set:
+            self.attr = attr_set[self.name]
+            self.attr.is_selector = True
+            self._external = False
+        else:
+            raise Exception("Passing selectors from external nests not supported")
+
+
 class Struct:
     def __init__(self, family, space_name, type_list=None,
                  inherited=None, submsg=None):