Whamcloud - gitweb
LU-9680 utils: fix nested attribute handling in liblnetconfig
[fs/lustre-release.git] / lnet / utils / lnetconfig / liblnetconfig_netlink.c
index 65bea40..3c7e7f7 100644 (file)
@@ -48,8 +48,8 @@
 #define NLM_F_ACK_TLVS 0x200   /* extended ACK TVLs were included */
 #endif
 
-#ifndef NLA_NUL_STRING
-# define NLA_NUL_STRING 10
+#ifndef NLA_S8
+# define NLA_S8 12
 #endif
 
 #ifndef NLA_S16
@@ -97,7 +97,121 @@ int64_t nla_get_s64(const struct nlattr *nla)
 #define NLA_PUT_S64(msg, attrtype, value) \
        NLA_PUT_TYPE(msg, int64_t, attrtype, value)
 
-#endif /* ! HAVE_NLA_GET_S64 */
+#ifndef NLA_NUL_STRING
+#define NLA_NUL_STRING 10
+#endif
+
+enum nla_types {
+       LNET_NLA_UNSPEC         = NLA_UNSPEC,
+       LNET_NLA_U8             = NLA_U8,
+       LNET_NLA_U16            = NLA_U16,
+       LNET_NLA_U32            = NLA_U32,
+       LNET_NLA_U64            = NLA_U64,
+       LNET_NLA_STRING         = NLA_STRING,
+       LNET_NLA_FLAG           = NLA_FLAG,
+       LNET_NLA_MSECS          = NLA_MSECS,
+       LNET_NLA_NESTED         = NLA_NESTED,
+       LNET_NLA_NESTED_COMPAT  = NLA_NESTED + 1,
+       LNET_NLA_NUL_STRING     = NLA_NUL_STRING,
+       LNET_NLA_BINARY         = NLA_NUL_STRING + 1,
+       LNET_NLA_S8             = NLA_S8,
+       LNET_NLA_S16            = NLA_S16,
+       LNET_NLA_S32            = NLA_S32,
+       LNET_NLA_S64            = NLA_S64,
+       __LNET_NLA_TYPE_MAX,
+};
+
+#define LNET_NLA_TYPE_MAX (__LNET_NLA_TYPE_MAX - 1)
+
+static uint16_t nla_attr_minlen[LNET_NLA_TYPE_MAX+1] = {
+       [NLA_U8]        = sizeof(uint8_t),
+       [NLA_U16]       = sizeof(uint16_t),
+       [NLA_U32]       = sizeof(uint32_t),
+       [NLA_U64]       = sizeof(uint64_t),
+       [NLA_STRING]    = 1,
+       [NLA_FLAG]      = 0,
+};
+
+static int lnet_validate_nla(const struct nlattr *nla, int maxtype,
+                            const struct nla_policy *policy)
+{
+       const struct nla_policy *pt;
+       unsigned int minlen = 0;
+       int type = nla_type(nla);
+
+       if (type < 0 || type > maxtype)
+               return 0;
+
+       pt = &policy[type];
+
+       if (pt->type > NLA_TYPE_MAX)
+               return -NLE_INVAL;
+
+       if (pt->minlen)
+               minlen = pt->minlen;
+       else if (pt->type != NLA_UNSPEC)
+               minlen = nla_attr_minlen[pt->type];
+
+       if (nla_len(nla) < minlen)
+               return -NLE_RANGE;
+
+       if (pt->maxlen && nla_len(nla) > pt->maxlen)
+               return -NLE_RANGE;
+
+       if (pt->type == NLA_STRING) {
+               const char *data = nla_data(nla);
+
+               if (data[nla_len(nla) - 1] != '\0')
+                       return -NLE_INVAL;
+       }
+
+       return 0;
+}
+
+int lnet_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
+                  int len, const struct nla_policy *policy)
+{
+       struct nlattr *nla;
+       int rem, err;
+
+       memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
+       nla_for_each_attr(nla, head, len, rem) {
+               int type = nla_type(nla);
+
+               if (type > maxtype)
+                       continue;
+
+               if (policy) {
+                       err = lnet_validate_nla(nla, maxtype, policy);
+                       if (err < 0)
+                               return err;
+               }
+
+               tb[type] = nla;
+       }
+
+       return 0;
+}
+
+int lnet_genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
+                      int maxtype, const struct nla_policy *policy)
+{
+       struct genlmsghdr *ghdr;
+
+       if (!genlmsg_valid_hdr(nlh, hdrlen))
+               return -NLE_MSG_TOOSHORT;
+
+       ghdr = nlmsg_data(nlh);
+       return lnet_nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
+                             genlmsg_attrlen(ghdr, hdrlen), policy);
+}
+
+#else /* !HAVE_NLA_GET_S64 */
+
+#define lnet_genlmsg_parse     genlmsg_parse
+
+#endif /* HAVE_NLA_GET_S64 */
 
 /**
  * Set NETLINK_BROADCAST_ERROR flags on socket to report ENOBUFS errors.
@@ -106,7 +220,7 @@ int64_t nla_get_s64(const struct nlattr *nla)
  *
  * Return      0 on success or a Netlink error code.
  */
-int nl_socket_enable_broadcast_error(struct nl_sock *sk)
+static int nl_socket_enable_broadcast_error(struct nl_sock *sk)
 {
        const int state = 1; /* enable errors */
        int err;
@@ -131,7 +245,7 @@ int nl_socket_enable_broadcast_error(struct nl_sock *sk)
  *
  * @return 0 on success or a negative error code
  */
-int nl_socket_set_ext_ack(struct nl_sock *sk, int state)
+static int nl_socket_set_ext_ack(struct nl_sock *sk, int state)
 {
        int err;
 
@@ -156,7 +270,7 @@ int nl_socket_set_ext_ack(struct nl_sock *sk, int state)
  *
  * Return              0 on success or a negative error code.
  */
-int lustre_netlink_register(struct nl_sock *sk, bool async_events)
+static int lustre_netlink_register(struct nl_sock *sk, bool async_events)
 {
        int rc;
 
@@ -192,8 +306,8 @@ int lustre_netlink_register(struct nl_sock *sk, bool async_events)
  *
  * Return      0 on success or a negative error code.
  */
-int lustre_netlink_add_group(struct nl_sock *nl, const char *family,
-                            const char *group)
+static int lustre_netlink_add_group(struct nl_sock *nl, const char *family,
+                                   const char *group)
 {
        int group_id;
 
@@ -464,6 +578,38 @@ static int yaml_parse_key_list(struct yaml_netlink_input *data,
        return NL_OK;
 }
 
+/* We translate Netlink nested list into either a YAML mappping or sequence.
+ * This generates the start of such a YAML block.
+ */
+static int yaml_nested_header(struct yaml_netlink_input *data,
+                             int *size, unsigned int *indent,
+                             int mapping, struct ln_key_props *keys)
+{
+       int len = 0;
+
+       if (keys->lkp_key_format & LNKF_FLOW) {
+               char brace = '{';
+
+               if (keys->lkp_key_format & LNKF_SEQUENCE)
+                       brace = '[';
+
+               len = snprintf(data->buffer, *size, "%*s%s: %c ", data->indent,
+                              "", keys->lkp_value, brace);
+       } else {
+               int count = mapping & LNKF_SEQUENCE ? 0 : data->indent;
+
+               if (keys->lkp_key_format & LNKF_MAPPING)
+                       *indent += 2;
+               if (keys->lkp_key_format & LNKF_SEQUENCE)
+                       *indent += 2;
+
+               len = snprintf(data->buffer, *size, "%*s%s:\n", count, "",
+                              keys->lkp_value);
+       }
+
+       return len;
+}
+
 static struct yaml_nl_node *get_next_child(struct yaml_nl_node *node,
                                           unsigned int idx)
 {
@@ -526,6 +672,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size,
        struct ln_key_props *keys = node->keys.lkl_list;
        int mapping = parent->lkp_key_format;
        int child_idx = 0, len = 0, i;
+       bool first = true;
 
        for (i = 1; i < node->keys.lkl_maxattr; i++) {
                struct nlattr *attr;
@@ -534,11 +681,56 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size,
                if (!attr && !keys[i].lkp_value)
                        continue;
 
-               if (keys[i].lkp_data_type != NLA_NUL_STRING &&
-                   keys[i].lkp_data_type != NLA_NESTED) {
+               /* This function is called for each Netlink nested list.
+                * Each nested list is treated as a YAML block. It is here
+                * we handle data for the YAML block. How that data is seen
+                * for YAML is based on the parents mapping and the type of
+                * data value sent.
+                *
+                * The cases are:
+                *
+                * the value type is NLA_NUL_STRING which is interepted as
+                *      key:\n
+                *
+                * Also NLA_NUL_STRING is used to update a single key value.
+                *
+                * the key has no lkp_value and we do receive a 'value'
+                * that is not a nested list in the Netlink packet. This is
+                * treated as a plain scalar.
+                *
+                * we have a key lkp_value and the parent mapping is
+                * LNKF_MAPPING then we have a key : value pair. During
+                * our loop the key normally doesn't change.
+                *
+                * This data belongs to a YAML block which can be of
+                * different kinds (FLOW, SEQUENCE, MAPPING). We determine
+                * the type and adjust the first line of output for the
+                * YAML results if needed. Most of the time the creation
+                * of the nested header is done in the NLA_NESTED case
+                * switch below which happens before this function is
+                * called. Specific handling is done here.
+                *
+                * The common case handled here is for building of the
+                * mapping key : value pair. Another case is that we
+                * are at the start of a SEQUENCE block. If this is the
+                * case we add '-' to the output and clear the flag
+                * LNKF_SEQUENCE to prevent multiple instanstances of
+                * '-'. Only one '-' per SEQUENCE block. We need to
+                * manually add '-' also in the case of were our nested
+                * block first PROCESSED attr instance is another nested
+                * block. For example:
+                *      local NI(s):
+                *      -     interfaces:
+                *                 0: ib0
+                */
+               if ((first && (mapping & LNKF_SEQUENCE) &&
+                    keys[i].lkp_data_type == NLA_NESTED) ||
+                   (keys[i].lkp_data_type != NLA_NUL_STRING &&
+                    keys[i].lkp_data_type != NLA_NESTED)) {
                        if (!attr && keys[i].lkp_data_type != NLA_FLAG)
                                continue;
 
+                       /* Mark this as the start of a SEQUENCE block */
                        if (!(mapping & LNKF_FLOW)) {
                                unsigned int indent = data->indent ?
                                                      data->indent : 2;
@@ -546,14 +738,19 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size,
                                memset(data->buffer, ' ', indent);
                                if (mapping & LNKF_SEQUENCE) {
                                        ((char *)data->buffer)[indent - 2] = '-';
-                                       if (mapping & LNKF_MAPPING)
+                                       if (keys[i].lkp_data_type != NLA_NESTED &&
+                                           mapping & LNKF_MAPPING)
                                                mapping &= ~LNKF_SEQUENCE;
                                }
                                data->buffer += indent;
                                *size -= indent;
                        }
 
-                       if (mapping & LNKF_MAPPING) {
+                       /* Start of the build of the key : value pair.
+                        * Very common case.
+                        */
+                       if (keys[i].lkp_data_type != NLA_NESTED &&
+                           mapping & LNKF_MAPPING) {
                                len = snprintf(data->buffer, *size, "%s: ",
                                               keys[i].lkp_value);
                                if (len < 0)
@@ -567,87 +764,98 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size,
                case NLA_NESTED: {
                        struct yaml_nl_node *next = get_next_child(node,
                                                                   child_idx++);
-                       int num = next->keys.lkl_maxattr;
+                       int num = next ? next->keys.lkl_maxattr : 0;
                        struct nla_policy nest_policy[num];
                        struct yaml_nl_node *old;
                        struct nlattr *cnt_attr;
+                       unsigned int indent = 0;
+                       bool start = true;
                        int rem, j;
 
-                       if (!attr)
+                       if (!attr || !next)
                                continue;
 
                        memset(nest_policy, 0, sizeof(struct nla_policy) * num);
                        for (j = 1; j < num; j++)
                                nest_policy[j].type = next->keys.lkl_list[j].lkp_data_type;
 
+                       /* We might have a empty list but by YAML standards
+                        * we still need to display the header.
+                        */
+                       if (!nla_len(attr)) {
+                               len = yaml_nested_header(data, size, &indent,
+                                                        first ? mapping : 0,
+                                                        &keys[i]);
+                               if (len < 0)
+                                       goto unwind;
+                               data->buffer += len;
+                               *size -= len;
+                               len = 0;
+                       }
+
                        old = data->cur;
                        data->cur = next;
                        nla_for_each_nested(cnt_attr, attr, rem) {
                                struct nlattr *nest_info[num];
-                               uint16_t indent = 0;
 
                                if (nla_parse_nested(nest_info, num, cnt_attr,
                                                     nest_policy))
                                        break;
 
-                               if (keys[i].lkp_key_format & LNKF_FLOW) {
-                                       char brace = '{';
-
-                                       if (keys[i].lkp_key_format &
-                                           LNKF_SEQUENCE)
-                                               brace = '[';
-
-                                       len = snprintf(data->buffer, *size,
-                                                      "%*s%s: %c ",
-                                                      data->indent, "",
-                                                      keys[i].lkp_value,
-                                                      brace);
-                               } else {
-                                       if (keys[i].lkp_key_format &
-                                           LNKF_MAPPING)
-                                               indent += 2;
-                                       if (keys[i].lkp_key_format &
-                                           LNKF_SEQUENCE)
-                                               indent += 2;
-
-                                       if (keys[i].lkp_value) {
-                                               len = snprintf(data->buffer,
-                                                              *size,
-                                                              "%*s%s:\n",
-                                                              data->indent, "",
-                                                              keys[i].lkp_value);
-                                       } else {
-                                               len = 0;
-                                       }
-                               }
+                               /* Create the nested header only once at start */
+                               if (!start)
+                                       goto skip_nested_header;
+                               start = false;
+
+                               /* Update the header's first key */
+                               if (next->keys.lkl_list[1].lkp_data_type == NLA_NUL_STRING &&
+                                   !keys[i].lkp_value)
+                                       keys[i].lkp_value = nla_strdup(nest_info[1]);
+
+                               len = yaml_nested_header(data, size, &indent,
+                                                        first ? mapping : 0,
+                                                        &keys[i]);
                                if (len < 0)
                                        goto unwind;
                                data->buffer += len;
                                *size -= len;
                                len = 0;
-
+skip_nested_header:
                                data->indent += indent;
                                yaml_parse_value_list(data, size, nest_info,
                                                      &keys[i]);
                                data->indent -= indent;
+                       }
 
-                               if (keys[i].lkp_key_format & LNKF_FLOW) {
-                                       char *tmp = (char *)data->buffer - 2;
-                                       char *brace = " }\n";
+                       /* nested bookend header */
+                       if (keys[i].lkp_key_format & LNKF_FLOW) {
+                               char *tmp = (char *)data->buffer - 2;
+                               char *brace = " }\n";
 
-                                       if (keys[i].lkp_key_format &
-                                           LNKF_SEQUENCE)
-                                               brace = " ]\n";
+                               if (keys[i].lkp_key_format &
+                                   LNKF_SEQUENCE)
+                                       brace = " ]\n";
 
-                                       memcpy(tmp, brace, strlen(brace));
-                                       data->buffer++;
-                                       *size -= 1;
-                               }
+                               memcpy(tmp, brace, strlen(brace));
+                               data->buffer++;
+                               *size -= 1;
                        }
                        data->cur = old;
+
+                       /* This is for the special case of the first attr of
+                        * a nested list is another nested list. We had to
+                        * insert a '-' but that is only done once so clear
+                        * the mapping of LNKF_SEQUENCE.
+                        */
+                       if (first && attr) {
+                               if (mapping & LNKF_MAPPING)
+                                       mapping &= ~LNKF_SEQUENCE;
+                               first = false;
+                       }
                        break;
                }
 
+               /* Handle the key:\n YAML case or updating an individual key */
                case NLA_NUL_STRING:
                        if (i == 1) {
                                if (data->cur != data->root)
@@ -681,6 +889,9 @@ not_first:
                        }
                        break;
 
+               /* The below is used for a plain scalar or to complete the
+               *  key : value pair.
+               */
                case NLA_STRING:
                        len = snprintf(data->buffer, *size, "%s",
                                       nla_get_string(attr));
@@ -745,6 +956,29 @@ unwind:
        }
 }
 
+static bool cleanup_children(struct yaml_nl_node *parent)
+{
+       struct yaml_nl_node *child;
+
+       if (nl_list_empty(&parent->children)) {
+               struct ln_key_props *keys = parent->keys.lkl_list;
+               int i;
+
+               for (i = 1; i < parent->keys.lkl_maxattr; i++)
+                       if (keys[i].lkp_value)
+                               free(keys[i].lkp_value);
+               nl_list_del(&parent->list);
+               return true;
+       }
+
+       while ((child = get_next_child(parent, 0)) != NULL) {
+               if (cleanup_children(child))
+                       free(child);
+       }
+
+       return false;
+}
+
 /* This is the CB_VALID callback for the Netlink library that we
  * have hooked into. Any successful Netlink message is passed to
  * this function which handles both the incoming key tables and
@@ -761,10 +995,19 @@ static int yaml_netlink_msg_parse(struct nl_msg *msg, void *arg)
                struct genlmsghdr *ghdr = genlmsg_hdr(nlh);
                struct nlattr *attrs[LN_SCALAR_MAX + 1];
 
-               if (genlmsg_parse(nlh, 0, attrs, LN_SCALAR_MAX,
-                                 scalar_attr_policy))
+               if (lnet_genlmsg_parse(nlh, 0, attrs, LN_SCALAR_MAX,
+                                      scalar_attr_policy))
                        return NL_SKIP;
 
+               /* If root already exists this means we are updating the
+                * key table. Free old key table.
+                */
+               if (data->root && (nlh->nlmsg_flags & NLM_F_REPLACE)) {
+                       cleanup_children(data->root);
+                       free(data->root);
+                       data->root = NULL;
+               }
+
                if (attrs[LN_SCALAR_ATTR_LIST]) {
                        int rc = yaml_parse_key_list(data, NULL,
                                                     attrs[LN_SCALAR_ATTR_LIST]);
@@ -797,7 +1040,7 @@ static int yaml_netlink_msg_parse(struct nl_msg *msg, void *arg)
                for (i = 1; i < maxtype; i++)
                        policy[i].type = data->cur->keys.lkl_list[i].lkp_data_type;
 
-               if (genlmsg_parse(nlh, 0, attrs, maxtype, policy))
+               if (lnet_genlmsg_parse(nlh, 0, attrs, maxtype, policy))
                        return NL_SKIP;
 
                size = data->parser->raw_buffer.end -
@@ -850,29 +1093,6 @@ static int yaml_netlink_msg_error(struct sockaddr_nl *who,
        return NL_STOP;
 }
 
-static bool cleanup_children(struct yaml_nl_node *parent)
-{
-       struct yaml_nl_node *child;
-
-       if (nl_list_empty(&parent->children)) {
-               struct ln_key_props *keys = parent->keys.lkl_list;
-               int i;
-
-               for (i = 1; i < parent->keys.lkl_maxattr; i++)
-                       if (keys[i].lkp_value)
-                               free(keys[i].lkp_value);
-               nl_list_del(&parent->list);
-               return true;
-       }
-
-       while ((child = get_next_child(parent, 0)) != NULL) {
-               if (cleanup_children(child))
-                       free(child);
-       }
-
-       return false;
-}
-
 /* This is the libnl callback for when the last Netlink packet
  * is finished being parsed or its called right away in case
  * the Linux kernel reports back an error from the Netlink layer.
@@ -1082,7 +1302,7 @@ static unsigned int indent_level(const char *str)
        return tmp - str;
 }
 
-#define LNKF_END 8
+#define LNKF_BLOCK 8
 
 static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter,
                                                char *line,
@@ -1095,12 +1315,14 @@ static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter,
        new_indent = indent_level(line);
        if (new_indent < indent) {
                *offset = indent - emitter->best_indent;
-               return LNKF_END;
+               return LNKF_BLOCK;
        }
 
        if (strncmp(line + new_indent, "- ", 2) == 0) {
                memset(line + new_indent, ' ', 2);
-               new_indent += 2;
+               /* Eat white spaces physical YAML config files have */
+               new_indent += strspn(line + new_indent, " ");
+               fmt |= LNKF_SEQUENCE;
        }
 
        /* hdr: [ a : 1, b : 2, c : 3 ] */
@@ -1125,7 +1347,7 @@ static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter,
 
        if (indent != new_indent) {
                *offset = new_indent;
-               fmt |= LNKF_SEQUENCE;
+               fmt |= LNKF_BLOCK;
        }
 
        return fmt;
@@ -1146,7 +1368,7 @@ static int yaml_fill_scalar_data(struct nl_msg *msg,
                        sep = tmp;
        }
        if (sep)
-               *sep++ = '\0';
+               *sep = '\0';
 
        if (strspn(line, "-0123456789") == strlen(line)) {
                num = strtoll(line, NULL, 0);
@@ -1162,18 +1384,37 @@ static int yaml_fill_scalar_data(struct nl_msg *msg,
        }
 
        if (fmt & LNKF_MAPPING && sep) {
+               char *end = strchr(sep, '\n');
+               int len;
+
+               /* restore ':' */
+               *sep = ':';
+               sep++;
                while (isspace(*sep))
                        ++sep;
 
-               if (!strlen(sep))
+               len = end ? end - sep : strlen(sep);
+               if (len <= 0)
                        goto nla_put_failure;
-
-               if (strspn(sep, "-0123456789") == strlen(sep)) {
+               sep[len] = '\0';
+
+               if (strcasecmp(sep, "yes") == 0 ||
+                   strcasecmp(sep, "true") == 0 ||
+                   strcasecmp(sep, "on") == 0 ||
+                   strcasecmp(sep, "y") == 0) {
+                       NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, 1);
+               } else if (strcasecmp(sep, "no") == 0 ||
+                          strcasecmp(sep, "false") == 0 ||
+                          strcasecmp(sep, "off") == 0 ||
+                          strcasecmp(sep, "n") == 0) {
+                       NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, 0);
+               } else if (strspn(sep, "-0123456789") == strlen(sep)) {
                        num = strtoll(sep, NULL, 0);
                        NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, num);
                } else {
                        NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, sep);
                }
+               sep[len] = '\n';
        }
 nla_put_failure:
        return rc;
@@ -1184,15 +1425,14 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
                                   char **entry, unsigned int *indent,
                                   enum lnet_nl_key_format fmt)
 {
-       bool nested = fmt & LNKF_SEQUENCE;
-       struct nlattr *list = NULL;
-       char *line;
+       struct nlattr *mapping = NULL, *seq = NULL;
+       char *line, *tmp;
        int rc = 0;
 
        /* Not needed for FLOW only case */
-       if (nested) {
-               list = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
-               if (!list) {
+       if (fmt & LNKF_SEQUENCE) {
+               seq = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
+               if (!seq) {
                        yaml_emitter_set_writer_error(out->emitter,
                                                      "Emmitter netlink list creation failed");
                        rc = -EINVAL;
@@ -1200,16 +1440,18 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
                }
        }
 
-       if (fmt != LNKF_FLOW) {
-               rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent);
-               if (rc < 0)
-                       goto nla_put_failure;
-       }
-
        if (fmt & LNKF_FLOW) {
-               char *tmp = strchr(*hdr, '{'), *split = NULL;
+               struct nlattr *list = NULL;
                bool format = false;
+               char *split = NULL;
+
+               if (fmt != LNKF_FLOW) {
+                       rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent);
+                       if (rc < 0)
+                               goto nla_put_failure;
+               }
 
+               tmp = strchr(*hdr, '{');
                if (!tmp) {
                        tmp = strchr(*hdr, '[');
                        if (!tmp) {
@@ -1277,6 +1519,21 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out,
 
                nla_nest_end(msg, list);
        } else {
+next_mapping:
+               if (fmt & LNKF_BLOCK && strchr(*hdr, ':')) {
+                       mapping = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
+                       if (!mapping) {
+                               yaml_emitter_set_writer_error(out->emitter,
+                                                             "Emmitter netlink list creation failed");
+                               rc = -EINVAL;
+                               goto nla_put_failure;
+                       }
+               }
+
+               rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent);
+               if (rc < 0)
+                       goto nla_put_failure;
+
                do {
                        line = strsep(entry, "\n");
 have_next_line:
@@ -1284,41 +1541,88 @@ have_next_line:
                                break;
 
                        fmt = yaml_format_type(out->emitter, line, indent);
-                       if (fmt == LNKF_END)
+                       if (fmt == LNKF_BLOCK)
                                break;
 
-                       if (fmt & ~LNKF_MAPPING) { /* Filter out mappings */
+                       /* sequences of simple scalars, general mappings, and
+                        * plain scalars are not nested structures in a
+                        * netlink packet.
+                        */
+                       if (fmt == LNKF_SEQUENCE || fmt == LNKF_MAPPING || fmt == 0) {
+                               rc = yaml_fill_scalar_data(msg, fmt,
+                                                          line + *indent);
+                               if (rc < 0)
+                                       goto nla_put_failure;
+                       } else {
                                rc = yaml_create_nested_list(out, msg, &line,
                                                             entry, indent,
                                                             fmt);
                                if (rc < 0)
                                        goto nla_put_failure;
+
+                               /* if the original line that called
+                                * yaml_create_nested_list above was an
+                                * sequence and the next line is also
+                                * then break to treat it as a mapping / scalar
+                                * instead to avoid over nesting.
+                                */
+                               if (line && seq) {
+                                       fmt = yaml_format_type(out->emitter, line, indent);
+                                       if ((fmt & LNKF_SEQUENCE) || (fmt & LNKF_BLOCK))
+                                               break;
+                               }
+
                                if (line)
                                        goto have_next_line;
-                       } else {
-                               rc = yaml_fill_scalar_data(msg, fmt,
-                                                          line + *indent);
-                               if (rc < 0)
-                                       goto nla_put_failure;
                        }
                } while (strcmp(*entry, ""));
 
-               if (line && line[*indent] == '-') {
-                       line[*indent] = ' ';
-                       *indent += 2;
+               if (mapping) {
+                       nla_nest_end(msg, mapping);
+                       mapping = NULL;
+               }
+       }
+
+       /* test if next line is sequence at the same level. */
+       if (line && (line[0] != '\0') && (fmt & LNKF_BLOCK)) {
+               int old_indent = indent_level(*hdr);
+
+               fmt = yaml_format_type(out->emitter, line, indent);
+               if (fmt != LNKF_BLOCK && old_indent == *indent) {
+                       /* If we have a normal mapping set then treate
+                        * it as a collection of scalars i.e don't create
+                        * another nested level. For scalar:\n and plain
+                        * scalar case we send it to next_mapping to
+                        * create another nested level.
+                        */
+                       tmp = strchr(line, ':');
+                       if (tmp) {
+                               fmt = LNKF_BLOCK;
+                               if (strstr(line, ": "))
+                                       fmt |= LNKF_MAPPING;
+                               if (strstr(line, "- "))
+                                       fmt |= LNKF_SEQUENCE;
+                               *hdr = line;
+                               goto next_mapping;
+                       }
+
                        goto have_next_line;
                }
-               if (*entry && !strlen(*entry))
+       }
+
+       if (seq) {
+               if (*indent >= 2)
+                       *indent -= 2;
+               nla_nest_end(msg, seq);
+               seq = NULL;
+               if (*entry && !strlen(*entry) && fmt != LNKF_BLOCK)
                        line = NULL;
-               /* strsep in the above loop moves entry to a value pass the
-                * end of the nested list. So to avoid losing this value we
-                * replace hdr with line.
-                */
-               *hdr = line;
        }
 
-       if (nested)
-               nla_nest_end(msg, list);
+       /* strsep in the above loop moves entry to a value pass the end of the
+        * nested list. So to avoid losing this value we replace hdr with line.
+        */
+       *hdr = line;
 nla_put_failure:
        return rc;
 }
@@ -1431,7 +1735,7 @@ already_have_line:
                        }
 
                        fmt = yaml_format_type(out->emitter, line, &indent);
-                       if (fmt & ~LNKF_MAPPING) {
+                       if (fmt) {
                                rc = yaml_create_nested_list(out, msg, &line,
                                                             &entry, &indent,
                                                             fmt);