riscv: defconfig: enable NLS_CODEPAGE_437, NLS_ISO8859_1
[linux-2.6-microblaze.git] / net / netlink / policy.c
index 641ffbd..8d7c900 100644 (file)
@@ -14,7 +14,7 @@
 
 #define INITIAL_POLICIES_ALLOC 10
 
-struct nl_policy_dump {
+struct netlink_policy_dump_state {
        unsigned int policy_idx;
        unsigned int attr_idx;
        unsigned int n_alloc;
@@ -24,18 +24,19 @@ struct nl_policy_dump {
        } policies[];
 };
 
-static int add_policy(struct nl_policy_dump **statep,
+static int add_policy(struct netlink_policy_dump_state **statep,
                      const struct nla_policy *policy,
                      unsigned int maxtype)
 {
-       struct nl_policy_dump *state = *statep;
+       struct netlink_policy_dump_state *state = *statep;
        unsigned int n_alloc, i;
 
        if (!policy || !maxtype)
                return 0;
 
        for (i = 0; i < state->n_alloc; i++) {
-               if (state->policies[i].policy == policy)
+               if (state->policies[i].policy == policy &&
+                   state->policies[i].maxtype == maxtype)
                        return 0;
 
                if (!state->policies[i].policy) {
@@ -62,43 +63,85 @@ static int add_policy(struct nl_policy_dump **statep,
        return 0;
 }
 
-static unsigned int get_policy_idx(struct nl_policy_dump *state,
-                                  const struct nla_policy *policy)
+/**
+ * netlink_policy_dump_get_policy_idx - retrieve policy index
+ * @state: the policy dump state
+ * @policy: the policy to find
+ * @maxtype: the policy's maxattr
+ *
+ * Returns: the index of the given policy in the dump state
+ *
+ * Call this to find a policy index when you've added multiple and e.g.
+ * need to tell userspace which command has which policy (by index).
+ *
+ * Note: this will WARN and return 0 if the policy isn't found, which
+ *      means it wasn't added in the first place, which would be an
+ *      internal consistency bug.
+ */
+int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
+                                      const struct nla_policy *policy,
+                                      unsigned int maxtype)
 {
        unsigned int i;
 
+       if (WARN_ON(!policy || !maxtype))
+                return 0;
+
        for (i = 0; i < state->n_alloc; i++) {
-               if (state->policies[i].policy == policy)
+               if (state->policies[i].policy == policy &&
+                   state->policies[i].maxtype == maxtype)
                        return i;
        }
 
-       WARN_ON_ONCE(1);
-       return -1;
+       WARN_ON(1);
+       return 0;
+}
+
+static struct netlink_policy_dump_state *alloc_state(void)
+{
+       struct netlink_policy_dump_state *state;
+
+       state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
+                       GFP_KERNEL);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+       state->n_alloc = INITIAL_POLICIES_ALLOC;
+
+       return state;
 }
 
-int netlink_policy_dump_start(const struct nla_policy *policy,
-                             unsigned int maxtype,
-                              unsigned long *_state)
+/**
+ * netlink_policy_dump_add_policy - add a policy to the dump
+ * @pstate: state to add to, may be reallocated, must be %NULL the first time
+ * @policy: the new policy to add to the dump
+ * @maxtype: the new policy's max attr type
+ *
+ * Returns: 0 on success, a negative error code otherwise.
+ *
+ * Call this to allocate a policy dump state, and to add policies to it. This
+ * should be called from the dump start() callback.
+ *
+ * Note: on failures, any previously allocated state is freed.
+ */
+int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
+                                  const struct nla_policy *policy,
+                                  unsigned int maxtype)
 {
-       struct nl_policy_dump *state;
+       struct netlink_policy_dump_state *state = *pstate;
        unsigned int policy_idx;
        int err;
 
-       /* also returns 0 if "*_state" is our ERR_PTR() end marker */
-       if (*_state)
-               return 0;
+       if (!state) {
+               state = alloc_state();
+               if (IS_ERR(state))
+                       return PTR_ERR(state);
+       }
 
        /*
         * walk the policies and nested ones first, and build
         * a linear list of them.
         */
 
-       state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
-                       GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
-       state->n_alloc = INITIAL_POLICIES_ALLOC;
-
        err = add_policy(&state, policy, maxtype);
        if (err)
                return err;
@@ -129,72 +172,103 @@ int netlink_policy_dump_start(const struct nla_policy *policy,
                }
        }
 
-       *_state = (unsigned long)state;
-
+       *pstate = state;
        return 0;
 }
 
-static bool netlink_policy_dump_finished(struct nl_policy_dump *state)
+static bool
+netlink_policy_dump_finished(struct netlink_policy_dump_state *state)
 {
        return state->policy_idx >= state->n_alloc ||
               !state->policies[state->policy_idx].policy;
 }
 
-bool netlink_policy_dump_loop(unsigned long *_state)
+/**
+ * netlink_policy_dump_loop - dumping loop indicator
+ * @state: the policy dump state
+ *
+ * Returns: %true if the dump continues, %false otherwise
+ *
+ * Note: this frees the dump state when finishing
+ */
+bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
 {
-       struct nl_policy_dump *state = (void *)*_state;
+       return !netlink_policy_dump_finished(state);
+}
 
-       if (IS_ERR(state))
-               return false;
+int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
+{
+       /* nested + type */
+       int common = 2 * nla_attr_size(sizeof(u32));
 
-       if (netlink_policy_dump_finished(state)) {
-               kfree(state);
-               /* store end marker instead of freed state */
-               *_state = (unsigned long)ERR_PTR(-ENOENT);
-               return false;
+       switch (pt->type) {
+       case NLA_UNSPEC:
+       case NLA_REJECT:
+               /* these actually don't need any space */
+               return 0;
+       case NLA_NESTED:
+       case NLA_NESTED_ARRAY:
+               /* common, policy idx, policy maxattr */
+               return common + 2 * nla_attr_size(sizeof(u32));
+       case NLA_U8:
+       case NLA_U16:
+       case NLA_U32:
+       case NLA_U64:
+       case NLA_MSECS:
+       case NLA_S8:
+       case NLA_S16:
+       case NLA_S32:
+       case NLA_S64:
+               /* maximum is common, u64 min/max with padding */
+               return common +
+                      2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
+       case NLA_BITFIELD32:
+               return common + nla_attr_size(sizeof(u32));
+       case NLA_STRING:
+       case NLA_NUL_STRING:
+       case NLA_BINARY:
+               /* maximum is common, u32 min-length/max-length */
+               return common + 2 * nla_attr_size(sizeof(u32));
+       case NLA_FLAG:
+               return common;
        }
 
-       return true;
+       /* this should then cause a warning later */
+       return 0;
 }
 
-int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state)
+static int
+__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
+                                struct sk_buff *skb,
+                                const struct nla_policy *pt,
+                                int nestattr)
 {
-       struct nl_policy_dump *state = (void *)_state;
-       const struct nla_policy *pt;
-       struct nlattr *policy, *attr;
+       int estimate = netlink_policy_dump_attr_size_estimate(pt);
        enum netlink_attribute_type type;
-       bool again;
-
-send_attribute:
-       again = false;
+       struct nlattr *attr;
 
-       pt = &state->policies[state->policy_idx].policy[state->attr_idx];
-
-       policy = nla_nest_start(skb, state->policy_idx);
-       if (!policy)
-               return -ENOBUFS;
-
-       attr = nla_nest_start(skb, state->attr_idx);
+       attr = nla_nest_start(skb, nestattr);
        if (!attr)
-               goto nla_put_failure;
+               return -ENOBUFS;
 
        switch (pt->type) {
        default:
        case NLA_UNSPEC:
        case NLA_REJECT:
                /* skip - use NLA_MIN_LEN to advertise such */
-               nla_nest_cancel(skb, policy);
-               again = true;
-               goto next;
+               nla_nest_cancel(skb, attr);
+               return -ENODATA;
        case NLA_NESTED:
                type = NL_ATTR_TYPE_NESTED;
                fallthrough;
        case NLA_NESTED_ARRAY:
                if (pt->type == NLA_NESTED_ARRAY)
                        type = NL_ATTR_TYPE_NESTED_ARRAY;
-               if (pt->nested_policy && pt->len &&
+               if (state && pt->nested_policy && pt->len &&
                    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
-                                get_policy_idx(state, pt->nested_policy)) ||
+                                netlink_policy_dump_get_policy_idx(state,
+                                                                   pt->nested_policy,
+                                                                   pt->len)) ||
                     nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
                                 pt->len)))
                        goto nla_put_failure;
@@ -215,6 +289,14 @@ send_attribute:
                else
                        type = NL_ATTR_TYPE_U64;
 
+               if (pt->validation_type == NLA_VALIDATE_MASK) {
+                       if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
+                                             pt->mask,
+                                             NL_POLICY_TYPE_ATTR_PAD))
+                               goto nla_put_failure;
+                       break;
+               }
+
                nla_get_range_unsigned(pt, &range);
 
                if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
@@ -254,12 +336,6 @@ send_attribute:
                                pt->bitfield32_valid))
                        goto nla_put_failure;
                break;
-       case NLA_EXACT_LEN:
-               type = NL_ATTR_TYPE_BINARY;
-               if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, pt->len) ||
-                   nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, pt->len))
-                       goto nla_put_failure;
-               break;
        case NLA_STRING:
        case NLA_NUL_STRING:
        case NLA_BINARY:
@@ -269,14 +345,27 @@ send_attribute:
                        type = NL_ATTR_TYPE_NUL_STRING;
                else
                        type = NL_ATTR_TYPE_BINARY;
-               if (pt->len && nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
-                                          pt->len))
-                       goto nla_put_failure;
-               break;
-       case NLA_MIN_LEN:
-               type = NL_ATTR_TYPE_BINARY;
-               if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, pt->len))
+
+               if (pt->validation_type == NLA_VALIDATE_RANGE ||
+                   pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) {
+                       struct netlink_range_validation range;
+
+                       nla_get_range_unsigned(pt, &range);
+
+                       if (range.min &&
+                           nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+                                       range.min))
+                               goto nla_put_failure;
+
+                       if (range.max < U16_MAX &&
+                           nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+                                       range.max))
+                               goto nla_put_failure;
+               } else if (pt->len &&
+                          nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+                                      pt->len)) {
                        goto nla_put_failure;
+               }
                break;
        case NLA_FLAG:
                type = NL_ATTR_TYPE_FLAG;
@@ -286,8 +375,66 @@ send_attribute:
        if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type))
                goto nla_put_failure;
 
-       /* finish and move state to next attribute */
        nla_nest_end(skb, attr);
+       WARN_ON(attr->nla_len > estimate);
+
+       return 0;
+nla_put_failure:
+       nla_nest_cancel(skb, attr);
+       return -ENOBUFS;
+}
+
+/**
+ * netlink_policy_dump_write_attr - write a given attribute policy
+ * @skb: the message skb to write to
+ * @pt: the attribute's policy
+ * @nestattr: the nested attribute ID to use
+ *
+ * Returns: 0 on success, an error code otherwise; -%ENODATA is
+ *         special, indicating that there's no policy data and
+ *         the attribute is generally rejected.
+ */
+int netlink_policy_dump_write_attr(struct sk_buff *skb,
+                                  const struct nla_policy *pt,
+                                  int nestattr)
+{
+       return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
+}
+
+/**
+ * netlink_policy_dump_write - write current policy dump attributes
+ * @skb: the message skb to write to
+ * @state: the policy dump state
+ *
+ * Returns: 0 on success, an error code otherwise
+ */
+int netlink_policy_dump_write(struct sk_buff *skb,
+                             struct netlink_policy_dump_state *state)
+{
+       const struct nla_policy *pt;
+       struct nlattr *policy;
+       bool again;
+       int err;
+
+send_attribute:
+       again = false;
+
+       pt = &state->policies[state->policy_idx].policy[state->attr_idx];
+
+       policy = nla_nest_start(skb, state->policy_idx);
+       if (!policy)
+               return -ENOBUFS;
+
+       err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx);
+       if (err == -ENODATA) {
+               nla_nest_cancel(skb, policy);
+               again = true;
+               goto next;
+       } else if (err) {
+               goto nla_put_failure;
+       }
+
+       /* finish and move state to next attribute */
        nla_nest_end(skb, policy);
 
 next:
@@ -309,3 +456,14 @@ nla_put_failure:
        nla_nest_cancel(skb, policy);
        return -ENOBUFS;
 }
+
+/**
+ * netlink_policy_dump_free - free policy dump state
+ * @state: the policy dump state to free
+ *
+ * Call this from the done() method to ensure dump state is freed.
+ */
+void netlink_policy_dump_free(struct netlink_policy_dump_state *state)
+{
+       kfree(state);
+}