Commit 1092cb21 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[NETLINK]: attr: add nested compat attribute type

Add a nested compat attribute type that can be used to convert
attributes that contain a structure to nested attributes in a
backwards compatible way.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 334a8132
...@@ -118,6 +118,9 @@ ...@@ -118,6 +118,9 @@
* Nested Attributes Construction: * Nested Attributes Construction:
* nla_nest_start(skb, type) start a nested attribute * nla_nest_start(skb, type) start a nested attribute
* nla_nest_end(skb, nla) finalize a nested attribute * nla_nest_end(skb, nla) finalize a nested attribute
* nla_nest_compat_start(skb, type, start a nested compat attribute
* len, data)
* nla_nest_compat_end(skb, type) finalize a nested compat attribute
* nla_nest_cancel(skb, nla) cancel nested attribute construction * nla_nest_cancel(skb, nla) cancel nested attribute construction
* *
* Attribute Length Calculations: * Attribute Length Calculations:
...@@ -152,6 +155,7 @@ ...@@ -152,6 +155,7 @@
* nla_find_nested() find attribute in nested attributes * nla_find_nested() find attribute in nested attributes
* nla_parse() parse and validate stream of attrs * nla_parse() parse and validate stream of attrs
* nla_parse_nested() parse nested attribuets * nla_parse_nested() parse nested attribuets
* nla_parse_nested_compat() parse nested compat attributes
* nla_for_each_attr() loop over all attributes * nla_for_each_attr() loop over all attributes
* nla_for_each_nested() loop over the nested attributes * nla_for_each_nested() loop over the nested attributes
*========================================================================= *=========================================================================
...@@ -170,6 +174,7 @@ enum { ...@@ -170,6 +174,7 @@ enum {
NLA_FLAG, NLA_FLAG,
NLA_MSECS, NLA_MSECS,
NLA_NESTED, NLA_NESTED,
NLA_NESTED_COMPAT,
NLA_NUL_STRING, NLA_NUL_STRING,
NLA_BINARY, NLA_BINARY,
__NLA_TYPE_MAX, __NLA_TYPE_MAX,
...@@ -190,6 +195,7 @@ enum { ...@@ -190,6 +195,7 @@ enum {
* NLA_NUL_STRING Maximum length of string (excluding NUL) * NLA_NUL_STRING Maximum length of string (excluding NUL)
* NLA_FLAG Unused * NLA_FLAG Unused
* NLA_BINARY Maximum length of attribute payload * NLA_BINARY Maximum length of attribute payload
* NLA_NESTED_COMPAT Exact length of structure payload
* All other Exact length of attribute payload * All other Exact length of attribute payload
* *
* Example: * Example:
...@@ -733,6 +739,39 @@ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, ...@@ -733,6 +739,39 @@ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
{ {
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
} }
/**
* nla_parse_nested_compat - parse nested compat attributes
* @tb: destination array with maxtype+1 elements
* @maxtype: maximum attribute type to be expected
* @nla: attribute containing the nested attributes
* @data: pointer to point to contained structure
* @len: length of contained structure
* @policy: validation policy
*
* Parse a nested compat attribute. The compat attribute contains a structure
* and optionally a set of nested attributes. On success the data pointer
* points to the nested data and tb contains the parsed attributes
* (see nla_parse).
*/
static inline int __nla_parse_nested_compat(struct nlattr *tb[], int maxtype,
struct nlattr *nla,
const struct nla_policy *policy,
int len)
{
if (nla_len(nla) < len)
return -1;
if (nla_len(nla) >= NLA_ALIGN(len) + sizeof(struct nlattr))
return nla_parse_nested(tb, maxtype,
nla_data(nla) + NLA_ALIGN(len),
policy);
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
return 0;
}
#define nla_parse_nested_compat(tb, maxtype, nla, policy, data, len) \
({ data = nla_len(nla) >= len ? nla_data(nla) : NULL; \
__nla_parse_nested_compat(tb, maxtype, nla, policy, len); })
/** /**
* nla_put_u8 - Add a u16 netlink attribute to a socket buffer * nla_put_u8 - Add a u16 netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to * @skb: socket buffer to add attribute to
...@@ -964,6 +1003,51 @@ static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start) ...@@ -964,6 +1003,51 @@ static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start)
return skb->len; return skb->len;
} }
/**
* nla_nest_compat_start - Start a new level of nested compat attributes
* @skb: socket buffer to add attributes to
* @attrtype: attribute type of container
* @attrlen: length of structure
* @data: pointer to structure
*
* Start a nested compat attribute that contains both a structure and
* a set of nested attributes.
*
* Returns the container attribute
*/
static inline struct nlattr *nla_nest_compat_start(struct sk_buff *skb,
int attrtype, int attrlen,
const void *data)
{
struct nlattr *start = (struct nlattr *)skb_tail_pointer(skb);
if (nla_put(skb, attrtype, attrlen, data) < 0)
return NULL;
if (nla_nest_start(skb, attrtype) == NULL) {
nlmsg_trim(skb, start);
return NULL;
}
return start;
}
/**
* nla_nest_compat_end - Finalize nesting of compat attributes
* @skb: socket buffer the attribtues are stored in
* @start: container attribute
*
* Corrects the container attribute header to include the all
* appeneded attributes.
*
* Returns the total data length of the skb.
*/
static inline int nla_nest_compat_end(struct sk_buff *skb, struct nlattr *start)
{
struct nlattr *nest = (void *)start + NLMSG_ALIGN(start->nla_len);
start->nla_len = skb_tail_pointer(skb) - (unsigned char *)start;
return nla_nest_end(skb, nest);
}
/** /**
* nla_nest_cancel - Cancel nesting of attributes * nla_nest_cancel - Cancel nesting of attributes
* @skb: socket buffer the message is stored in * @skb: socket buffer the message is stored in
......
...@@ -72,6 +72,17 @@ static int validate_nla(struct nlattr *nla, int maxtype, ...@@ -72,6 +72,17 @@ static int validate_nla(struct nlattr *nla, int maxtype,
return -ERANGE; return -ERANGE;
break; break;
case NLA_NESTED_COMPAT:
if (attrlen < pt->len)
return -ERANGE;
if (attrlen < NLA_ALIGN(pt->len))
break;
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN)
return -ERANGE;
nla = nla_data(nla) + NLA_ALIGN(pt->len);
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
return -ERANGE;
break;
default: default:
if (pt->len) if (pt->len)
minlen = pt->len; minlen = pt->len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment